feat(F-145): completed feature
This commit is contained in:
110
project/migrations/049_reporting_payment_lines.js
Normal file
110
project/migrations/049_reporting_payment_lines.js
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* F-145 — Reporting: payment lines and POS cash-safe capture.
|
||||
*
|
||||
* Creates `reporting_payment_lines`, an immutable table of payment events per order
|
||||
* enriched with store/terminal/session/method — the reporting layer's view of payments.
|
||||
*
|
||||
* Immutability: no UPDATE path. A refund is a new row with status='refund'.
|
||||
*
|
||||
* Idempotency: `pgm.createTable` is already IF NOT EXISTS; the DO $$ block guards
|
||||
* against duplicate FK constraint errors on re-run (PG16 does NOT support
|
||||
* `ADD CONSTRAINT IF NOT EXISTS`).
|
||||
*
|
||||
* @param {import('node-pg-migrate').MigrationBuilder} pgm
|
||||
*/
|
||||
|
||||
/** Table name */
|
||||
const TABLE = 'reporting_payment_lines';
|
||||
|
||||
/** FK name for order_id */
|
||||
const FK_ORDER = 'reporting_payment_lines_order_id_fkey';
|
||||
/** FK name for store_id */
|
||||
const FK_STORE = 'reporting_payment_lines_store_id_fkey';
|
||||
|
||||
export const up = (pgm) => {
|
||||
// ── Table ────────────────────────────────────────────────────────────────
|
||||
// `createTable` generates CREATE TABLE IF NOT EXISTS (PG>=9.6).
|
||||
pgm.createTable(
|
||||
TABLE,
|
||||
{
|
||||
id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') },
|
||||
order_id: { type: 'uuid', notNull: true },
|
||||
store_id: { type: 'uuid', notNull: true },
|
||||
terminal_id: { type: 'uuid' },
|
||||
cash_session_id: { type: 'uuid' },
|
||||
payment_method_id: { type: 'uuid' },
|
||||
provider: { type: 'text', notNull: true },
|
||||
amount_cents: { type: 'integer', notNull: true },
|
||||
currency: { type: 'text', notNull: true, default: "'EUR'" },
|
||||
status: { type: 'text', notNull: true },
|
||||
provider_ref: { type: 'text' },
|
||||
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
updated_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
|
||||
},
|
||||
{
|
||||
// Inline CHECK constraints.
|
||||
constraints: {
|
||||
check: {
|
||||
nonzero_amount: 'amount_cents != 0',
|
||||
eur_only: "currency = 'EUR'",
|
||||
valid_status: "status IN ('payment', 'refund', 'partial_refund')",
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// ── Foreign Keys (guarded — PG16 does NOT support ADD CONSTRAINT IF NOT EXISTS) ─
|
||||
pgm.sql(`
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = '${FK_ORDER}'
|
||||
) THEN
|
||||
ALTER TABLE ${TABLE}
|
||||
ADD CONSTRAINT ${FK_ORDER}
|
||||
FOREIGN KEY (order_id) REFERENCES orders_orders(id)
|
||||
ON DELETE RESTRICT;
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = '${FK_STORE}'
|
||||
) THEN
|
||||
ALTER TABLE ${TABLE}
|
||||
ADD CONSTRAINT ${FK_STORE}
|
||||
FOREIGN KEY (store_id) REFERENCES pos_stores(id)
|
||||
ON DELETE RESTRICT;
|
||||
END IF;
|
||||
END
|
||||
$$ LANGUAGE plpgsql;
|
||||
`);
|
||||
|
||||
// ── Indexes ─────────────────────────────────────────────────────────────
|
||||
pgm.createIndex(TABLE, ['store_id', 'created_at'], {
|
||||
name: 'reporting_payment_lines_store_created_idx',
|
||||
});
|
||||
pgm.createIndex(TABLE, 'order_id', { name: 'reporting_payment_lines_order_idx' });
|
||||
pgm.createIndex(
|
||||
TABLE,
|
||||
'cash_session_id',
|
||||
{
|
||||
name: 'reporting_payment_lines_session_idx',
|
||||
where: 'cash_session_id IS NOT NULL',
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export const down = (pgm) => {
|
||||
pgm.dropIndex(TABLE, 'cash_session_id', {
|
||||
name: 'reporting_payment_lines_session_idx',
|
||||
ifExists: true,
|
||||
});
|
||||
pgm.dropIndex(TABLE, 'order_id', {
|
||||
name: 'reporting_payment_lines_order_idx',
|
||||
ifExists: true,
|
||||
});
|
||||
pgm.dropIndex(TABLE, ['store_id', 'created_at'], {
|
||||
name: 'reporting_payment_lines_store_created_idx',
|
||||
ifExists: true,
|
||||
});
|
||||
pgm.dropTable(TABLE, { ifExists: true });
|
||||
};
|
||||
Reference in New Issue
Block a user