mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 19:58:11 +00:00
186 lines
7.2 KiB
TypeScript
186 lines
7.2 KiB
TypeScript
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
|
|
/**
|
|
* Freight billing — `invoices` + `invoice_lines` tables.
|
|
*
|
|
* Matches:
|
|
* - billing/entities/invoice.entity.ts
|
|
* - billing/entities/invoice-line.entity.ts
|
|
*
|
|
* The status enum mirrors `Freight.InvoiceStatus` and uses TypeORM's default
|
|
* enum-type name (`<table>_<column>_enum`) so the entity's `type: "enum"`
|
|
* column resolves to it without an explicit `enumName`.
|
|
*/
|
|
export class CreateInvoices1821000000002 implements MigrationInterface {
|
|
name = "CreateInvoices1821000000002";
|
|
|
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
const typeExists = await queryRunner.query(
|
|
`SELECT 1 FROM pg_type WHERE typname = 'invoices_status_enum' AND typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'freight');`,
|
|
);
|
|
|
|
if (!typeExists.length) {
|
|
await queryRunner.query(`
|
|
CREATE TYPE freight.invoices_status_enum AS ENUM (
|
|
'DRAFT',
|
|
'PENDING',
|
|
'PAID',
|
|
'OVERDUE',
|
|
'CANCELLED',
|
|
'REFUNDED'
|
|
);
|
|
`);
|
|
}
|
|
|
|
await queryRunner.query(`
|
|
CREATE TABLE IF NOT EXISTS freight.invoices (
|
|
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
|
invoice_number varchar(64) NOT NULL,
|
|
company_id uuid NOT NULL,
|
|
company_profile_id uuid NOT NULL,
|
|
total_amount numeric(14, 2) NOT NULL,
|
|
currency varchar(8) NOT NULL DEFAULT 'ETB',
|
|
status freight.invoices_status_enum NOT NULL DEFAULT 'DRAFT',
|
|
source varchar(255) NOT NULL,
|
|
source_id varchar(255) NOT NULL,
|
|
type varchar(255) NOT NULL,
|
|
issued_at timestamptz,
|
|
payment_id uuid,
|
|
due_at timestamptz NOT NULL,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
deleted_at timestamptz,
|
|
CONSTRAINT pk_invoices PRIMARY KEY (id),
|
|
CONSTRAINT uq_invoices_invoice_number UNIQUE (invoice_number),
|
|
CONSTRAINT fk_invoices_company FOREIGN KEY (company_id)
|
|
REFERENCES freight.companies (id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_invoices_company_profile FOREIGN KEY (company_profile_id)
|
|
REFERENCES freight.company_profiles (id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_invoices_payment FOREIGN KEY (payment_id)
|
|
REFERENCES freight.payments (id) ON DELETE SET NULL
|
|
);
|
|
`);
|
|
|
|
await queryRunner.query(
|
|
`
|
|
ALTER TABLE freight.invoices
|
|
ADD COLUMN IF NOT EXISTS id uuid DEFAULT uuid_generate_v4(),
|
|
ADD COLUMN IF NOT EXISTS invoice_number varchar(64),
|
|
ADD COLUMN IF NOT EXISTS company_id uuid,
|
|
ADD COLUMN IF NOT EXISTS company_profile_id uuid,
|
|
ADD COLUMN IF NOT EXISTS total_amount numeric(14, 2) NOT NULL DEFAULT 0,
|
|
ADD COLUMN IF NOT EXISTS currency varchar(8) NOT NULL DEFAULT 'ETB',
|
|
ADD COLUMN IF NOT EXISTS status freight.invoices_status_enum NOT NULL DEFAULT 'DRAFT',
|
|
ADD COLUMN IF NOT EXISTS source varchar(255),
|
|
ADD COLUMN IF NOT EXISTS source_id varchar(255),
|
|
ADD COLUMN IF NOT EXISTS type varchar(255),
|
|
ADD COLUMN IF NOT EXISTS issued_at timestamptz,
|
|
ADD COLUMN IF NOT EXISTS payment_id uuid,
|
|
ADD COLUMN IF NOT EXISTS due_at timestamptz,
|
|
ADD COLUMN IF NOT EXISTS created_at timestamptz NOT NULL DEFAULT now(),
|
|
ADD COLUMN IF NOT EXISTS updated_at timestamptz NOT NULL DEFAULT now(),
|
|
ADD COLUMN IF NOT EXISTS deleted_at timestamptz;
|
|
`,
|
|
);
|
|
await queryRunner.query(`
|
|
UPDATE freight.invoices
|
|
SET due_at = COALESCE(due_at, issued_at, created_at, now())
|
|
WHERE due_at IS NULL;
|
|
`);
|
|
await queryRunner.query(`ALTER TABLE freight.invoices ALTER COLUMN due_at SET NOT NULL;`);
|
|
|
|
await queryRunner.query(`
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM pg_constraint
|
|
WHERE contype = 'p'
|
|
AND conrelid = 'freight.invoices'::regclass
|
|
) THEN
|
|
ALTER TABLE freight.invoices ADD CONSTRAINT pk_invoices PRIMARY KEY (id);
|
|
END IF;
|
|
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM pg_constraint
|
|
WHERE conname = 'uq_invoices_invoice_number'
|
|
AND conrelid = 'freight.invoices'::regclass
|
|
) THEN
|
|
ALTER TABLE freight.invoices ADD CONSTRAINT uq_invoices_invoice_number UNIQUE (invoice_number);
|
|
END IF;
|
|
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM pg_constraint
|
|
WHERE conname = 'fk_invoices_company'
|
|
AND conrelid = 'freight.invoices'::regclass
|
|
) THEN
|
|
ALTER TABLE freight.invoices ADD CONSTRAINT fk_invoices_company
|
|
FOREIGN KEY (company_id) REFERENCES freight.companies (id) ON DELETE RESTRICT;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM pg_constraint
|
|
WHERE conname = 'fk_invoices_company_profile'
|
|
AND conrelid = 'freight.invoices'::regclass
|
|
) THEN
|
|
ALTER TABLE freight.invoices ADD CONSTRAINT fk_invoices_company_profile
|
|
FOREIGN KEY (company_profile_id) REFERENCES freight.company_profiles (id) ON DELETE RESTRICT;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM pg_constraint
|
|
WHERE conname = 'fk_invoices_payment'
|
|
AND conrelid = 'freight.invoices'::regclass
|
|
) THEN
|
|
ALTER TABLE freight.invoices ADD CONSTRAINT fk_invoices_payment
|
|
FOREIGN KEY (payment_id) REFERENCES freight.payments (id) ON DELETE SET NULL;
|
|
END IF;
|
|
END $$;
|
|
`);
|
|
|
|
await queryRunner.query(
|
|
`CREATE INDEX IF NOT EXISTS idx_invoices_company ON freight.invoices (company_id);`,
|
|
);
|
|
await queryRunner.query(
|
|
`CREATE INDEX IF NOT EXISTS idx_invoices_company_profile ON freight.invoices (company_profile_id);`,
|
|
);
|
|
await queryRunner.query(
|
|
`CREATE INDEX IF NOT EXISTS idx_invoices_source ON freight.invoices (source, source_id);`,
|
|
);
|
|
await queryRunner.query(
|
|
`CREATE INDEX IF NOT EXISTS idx_invoices_status ON freight.invoices (status);`,
|
|
);
|
|
|
|
await queryRunner.query(`
|
|
CREATE TABLE IF NOT EXISTS freight.invoice_lines (
|
|
id uuid NOT NULL DEFAULT uuid_generate_v4(),
|
|
invoice_id uuid NOT NULL,
|
|
charge_type varchar NOT NULL,
|
|
description varchar(255),
|
|
quantity numeric(12, 2) NOT NULL DEFAULT 1,
|
|
unit_rate numeric(14, 2) NOT NULL DEFAULT 0,
|
|
amount numeric(14, 2) NOT NULL,
|
|
currency varchar(8) NOT NULL DEFAULT 'ETB',
|
|
metadata jsonb,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
deleted_at timestamptz,
|
|
CONSTRAINT pk_invoice_lines PRIMARY KEY (id),
|
|
CONSTRAINT fk_invoice_lines_invoice FOREIGN KEY (invoice_id)
|
|
REFERENCES freight.invoices (id) ON DELETE CASCADE
|
|
);
|
|
`);
|
|
|
|
await queryRunner.query(
|
|
`CREATE INDEX IF NOT EXISTS idx_invoice_lines_invoice ON freight.invoice_lines (invoice_id);`,
|
|
);
|
|
}
|
|
|
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
await queryRunner.query(`DROP TABLE IF EXISTS freight.invoice_lines;`);
|
|
await queryRunner.query(`DROP TABLE IF EXISTS freight.invoices;`);
|
|
await queryRunner.query(
|
|
`DROP TYPE IF EXISTS freight.invoices_status_enum;`,
|
|
);
|
|
}
|
|
}
|