/** * Postgres migration set (D19). Numbered, applied in order by db-pg.ts's runner, * each inside its own transaction, recorded in schema_migrations. Inline strings * (not .sql files) so esbuild bundles them into dist/server.cjs with no file I/O. * * Translation choices vs the SQLite SCHEMA (deliberate, recorded in the D19 spec): * - every *_paise / money column is BIGINT (int4 overflows at ₹2.15 crore in paise); * - 0/1 flags stay INTEGER — repos say `active=1` and that SQL must be identical on * both engines (boolean would break it); * - JSON payloads stay TEXT — node-pg auto-parses json/jsonb columns into objects, * which would break the repos' JSON.parse at the edge. A jsonb ALTER is a later, * pg-only optimisation if we ever index into payloads; * - dates stay TEXT ISO (house convention, index-friendly, no timezone surprises); * - the reminder.rule_kind CHECK is born in its final widened form (SQLite reaches * it via the guarded table rebuild; Postgres starts correct). */ export interface PgMigration { id: string; sql: string } export const PG_MIGRATIONS: PgMigration[] = [ { id: '001-init', sql: ` CREATE TABLE IF NOT EXISTS staff_user ( id text PRIMARY KEY, email text NOT NULL UNIQUE, display_name text NOT NULL, role text NOT NULL CHECK (role IN ('owner','manager','staff')), phone text, title text, pw_salt text NOT NULL, pw_hash text NOT NULL, active integer NOT NULL DEFAULT 1 ); CREATE TABLE IF NOT EXISTS session ( token text PRIMARY KEY, staff_id text NOT NULL, expires_at text NOT NULL ); CREATE TABLE IF NOT EXISTS client ( id text PRIMARY KEY, code text NOT NULL UNIQUE, name text NOT NULL, gstin text, state_code text NOT NULL DEFAULT '32', address text NOT NULL DEFAULT '', contacts text NOT NULL DEFAULT '[]', status text NOT NULL DEFAULT 'active' CHECK (status IN ('lead','active','dormant','lost')), owner_id text, anydesk text, os text, district text, sector text, db_password_enc text, notes text NOT NULL DEFAULT '', source text NOT NULL DEFAULT 'hq', created_at text NOT NULL ); CREATE TABLE IF NOT EXISTS module ( id text PRIMARY KEY, code text NOT NULL UNIQUE, name text NOT NULL, sac text NOT NULL DEFAULT '998313', allowed_kinds text NOT NULL DEFAULT '["one_time","monthly","yearly","usage"]', multi_subscription integer NOT NULL DEFAULT 0, active integer NOT NULL DEFAULT 1, quote_content text NOT NULL DEFAULT '[]' ); CREATE TABLE IF NOT EXISTS module_price_book ( id text PRIMARY KEY, module_id text NOT NULL, edition text NOT NULL DEFAULT 'standard', kind text NOT NULL, price_paise bigint NOT NULL, effective_from text NOT NULL ); CREATE TABLE IF NOT EXISTS client_module ( id text PRIMARY KEY, client_id text NOT NULL, module_id text NOT NULL, status text NOT NULL DEFAULT 'quoted' CHECK (status IN ('quoted','ordered','installing','installed','trained','live','expired','cancelled')), kind text NOT NULL, edition text NOT NULL DEFAULT 'standard', installed_on text, completed_on text, trained_on text, next_renewal text, active integer NOT NULL DEFAULT 1 ); CREATE TABLE IF NOT EXISTS tax_class ( class_code text NOT NULL, rate_pct_bp integer NOT NULL, cess_pct_bp integer NOT NULL DEFAULT 0, effective_from text NOT NULL, effective_to text ); CREATE TABLE IF NOT EXISTS doc_series ( doc_type text NOT NULL, fy text NOT NULL, prefix text NOT NULL, next_seq integer NOT NULL, PRIMARY KEY (doc_type, fy) ); CREATE TABLE IF NOT EXISTS document ( id text PRIMARY KEY, doc_type text NOT NULL CHECK (doc_type IN ('QUOTATION','PROFORMA','INVOICE','RECEIPT','CREDIT_NOTE')), doc_no text, fy text NOT NULL, client_id text NOT NULL, doc_date text NOT NULL, due_date text, status text NOT NULL DEFAULT 'draft' CHECK (status IN ('draft','sent','accepted','invoiced','part_paid','paid','lost','cancelled')), ref_doc_id text, taxable_paise bigint NOT NULL, cgst_paise bigint NOT NULL, sgst_paise bigint NOT NULL, igst_paise bigint NOT NULL, round_off_paise bigint NOT NULL, payable_paise bigint NOT NULL, payload text NOT NULL, source text NOT NULL DEFAULT 'hq', created_by text NOT NULL, created_at text NOT NULL, UNIQUE (doc_no) ); CREATE TABLE IF NOT EXISTS document_event ( id text PRIMARY KEY, document_id text NOT NULL, at_wall text NOT NULL, kind text NOT NULL, meta text NOT NULL DEFAULT '{}' ); CREATE TABLE IF NOT EXISTS document_share ( id text PRIMARY KEY, document_id text NOT NULL, token text NOT NULL UNIQUE, created_by text NOT NULL, created_at text NOT NULL, expires_at text, revoked integer NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS payment ( id text PRIMARY KEY, client_id text NOT NULL, received_on text NOT NULL, mode text NOT NULL CHECK (mode IN ('bank','upi','cheque','cash','other')), reference text NOT NULL DEFAULT '', amount_paise bigint NOT NULL, tds_paise bigint NOT NULL DEFAULT 0, created_by text NOT NULL, created_at text NOT NULL ); CREATE TABLE IF NOT EXISTS payment_allocation ( id text PRIMARY KEY, payment_id text NOT NULL, document_id text NOT NULL, amount_paise bigint NOT NULL ); CREATE TABLE IF NOT EXISTS email_account ( id text PRIMARY KEY, address text NOT NULL, refresh_token_enc text NOT NULL, status text NOT NULL DEFAULT 'active' CHECK (status IN ('active','dead')), updated_at text NOT NULL ); CREATE TABLE IF NOT EXISTS email_log ( id text PRIMARY KEY, document_id text, to_addr text NOT NULL, subject text NOT NULL, status text NOT NULL CHECK (status IN ('sent','failed')), gmail_message_id text, error text, at_wall text NOT NULL, bounced integer NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS setting ( key text PRIMARY KEY, value text NOT NULL ); CREATE TABLE IF NOT EXISTS audit_log ( id text PRIMARY KEY, at_wall text NOT NULL, user_id text NOT NULL, action text NOT NULL, entity text NOT NULL, entity_id text NOT NULL, before_json text, after_json text ); CREATE TABLE IF NOT EXISTS stg_client ( row_no integer PRIMARY KEY, code text, name text, gstin text, state_code text, address text, phone text, email text, status text, anydesk text, os text, district text, sector text, problems text NOT NULL DEFAULT '[]' ); CREATE TABLE IF NOT EXISTS stg_invoice ( row_no integer PRIMARY KEY, client_code text, doc_no text, doc_date text, taxable_paise bigint, tax_paise bigint, total_paise bigint, paid integer, problems text NOT NULL DEFAULT '[]' ); CREATE TABLE IF NOT EXISTS recurring_plan ( id text PRIMARY KEY, client_id text NOT NULL, client_module_id text, cadence text NOT NULL CHECK (cadence IN ('monthly','yearly')), amount_paise bigint, next_run text NOT NULL, policy text NOT NULL DEFAULT 'manual' CHECK (policy IN ('auto','manual')), active integer NOT NULL DEFAULT 1 ); CREATE TABLE IF NOT EXISTS amc_contract ( id text PRIMARY KEY, client_id text NOT NULL, coverage text NOT NULL DEFAULT '', period_from text NOT NULL, period_to text NOT NULL, amount_paise bigint NOT NULL, renewal_reminder_days integer NOT NULL DEFAULT 30, legacy_paid integer, invoice_doc_id text, active integer NOT NULL DEFAULT 1 ); CREATE TABLE IF NOT EXISTS interaction_type ( code text PRIMARY KEY, label text NOT NULL ); CREATE TABLE IF NOT EXISTS interaction ( id text PRIMARY KEY, client_id text NOT NULL, type_code text NOT NULL, on_date text NOT NULL, staff_id text NOT NULL, notes text NOT NULL DEFAULT '', outcome text CHECK (outcome IN ('positive','neutral','negative')), follow_up_on text, created_at text NOT NULL ); CREATE TABLE IF NOT EXISTS reminder ( id text PRIMARY KEY, rule_kind text NOT NULL CHECK (rule_kind IN ('invoice_overdue','renewal_due','amc_expiring','follow_up','recurring_generated','email_bounced','quote_followup')), subject_id text NOT NULL, due_period text NOT NULL, client_id text NOT NULL, doc_id text, status text NOT NULL DEFAULT 'queued' CHECK (status IN ('queued','sent','failed','dismissed')), policy_applied text NOT NULL DEFAULT 'manual' CHECK (policy_applied IN ('auto','manual')), error text, created_at text NOT NULL, sent_at text, UNIQUE (rule_kind, subject_id, due_period) ); CREATE TABLE IF NOT EXISTS reminder_schedule ( id text PRIMARY KEY, rule_kind text NOT NULL, effective_from text NOT NULL, effective_to text, day_offsets text NOT NULL, subject text, body text ); CREATE TABLE IF NOT EXISTS aws_usage ( id text PRIMARY KEY, client_id text NOT NULL, month text NOT NULL, storage_gb double precision NOT NULL DEFAULT 0, transfer_gb double precision NOT NULL DEFAULT 0, cost_paise bigint NOT NULL DEFAULT 0, source text NOT NULL DEFAULT 'auto' CHECK (source IN ('auto','manual')), updated_at text NOT NULL, UNIQUE (client_id, month) ); `, }, ]