You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
8.5 KiB
TypeScript
187 lines
8.5 KiB
TypeScript
/**
|
|
* 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)
|
|
);
|
|
`,
|
|
},
|
|
]
|