184 lines
9.4 KiB
SQL
184 lines
9.4 KiB
SQL
-- Sync schema drift: soft-delete columns, mustChangePassword, RBAC tables,
|
|
-- recurring expenses, and payroll tables.
|
|
-- Uses IF NOT EXISTS / IF NOT EXISTS for idempotency (safe on fresh and db-pushed databases).
|
|
|
|
-- ── Soft-delete columns ──
|
|
|
|
ALTER TABLE "tenants" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "mustChangePassword" BOOLEAN NOT NULL DEFAULT false;
|
|
ALTER TABLE "subscriptions" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "invoices" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "payments" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "remittances" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "tickets" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "areas" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "plans" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "clients" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "employees" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "expenses" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "assets" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "company_accounts" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
ALTER TABLE "chart_of_accounts" ADD COLUMN IF NOT EXISTS "deletedAt" TIMESTAMP(3);
|
|
|
|
-- Indexes for deletedAt
|
|
CREATE INDEX IF NOT EXISTS "tenants_deletedAt_idx" ON "tenants"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "users_deletedAt_idx" ON "users"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "subscriptions_deletedAt_idx" ON "subscriptions"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "invoices_deletedAt_idx" ON "invoices"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "payments_deletedAt_idx" ON "payments"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "remittances_deletedAt_idx" ON "remittances"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "tickets_deletedAt_idx" ON "tickets"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "areas_deletedAt_idx" ON "areas"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "plans_deletedAt_idx" ON "plans"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "clients_deletedAt_idx" ON "clients"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "employees_deletedAt_idx" ON "employees"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "expenses_deletedAt_idx" ON "expenses"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "assets_deletedAt_idx" ON "assets"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "company_accounts_deletedAt_idx" ON "company_accounts"("deletedAt");
|
|
CREATE INDEX IF NOT EXISTS "chart_of_accounts_deletedAt_idx" ON "chart_of_accounts"("deletedAt");
|
|
|
|
-- ── RBAC: tenant_roles ──
|
|
|
|
CREATE TABLE IF NOT EXISTS "tenant_roles" (
|
|
"id" TEXT NOT NULL,
|
|
"tenantId" TEXT NOT NULL,
|
|
"name" TEXT NOT NULL,
|
|
"slug" TEXT NOT NULL,
|
|
"description" TEXT,
|
|
"isSystem" BOOLEAN NOT NULL DEFAULT false,
|
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
"deletedAt" TIMESTAMP(3),
|
|
|
|
CONSTRAINT "tenant_roles_pkey" PRIMARY KEY ("id")
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "tenant_roles_tenantId_slug_key" ON "tenant_roles"("tenantId", "slug");
|
|
CREATE INDEX IF NOT EXISTS "tenant_roles_tenantId_idx" ON "tenant_roles"("tenantId");
|
|
CREATE INDEX IF NOT EXISTS "tenant_roles_deletedAt_idx" ON "tenant_roles"("deletedAt");
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'tenant_roles_tenantId_fkey') THEN
|
|
ALTER TABLE "tenant_roles" ADD CONSTRAINT "tenant_roles_tenantId_fkey"
|
|
FOREIGN KEY ("tenantId") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ── RBAC: role_permissions ──
|
|
|
|
CREATE TABLE IF NOT EXISTS "role_permissions" (
|
|
"id" TEXT NOT NULL,
|
|
"tenantRoleId" TEXT NOT NULL,
|
|
"module" TEXT NOT NULL,
|
|
"canView" BOOLEAN NOT NULL DEFAULT false,
|
|
"canCreate" BOOLEAN NOT NULL DEFAULT false,
|
|
"canUpdate" BOOLEAN NOT NULL DEFAULT false,
|
|
"canArchive" BOOLEAN NOT NULL DEFAULT false,
|
|
"canApprove" BOOLEAN NOT NULL DEFAULT false,
|
|
"canExport" BOOLEAN NOT NULL DEFAULT false,
|
|
|
|
CONSTRAINT "role_permissions_pkey" PRIMARY KEY ("id")
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "role_permissions_tenantRoleId_module_key" ON "role_permissions"("tenantRoleId", "module");
|
|
CREATE INDEX IF NOT EXISTS "role_permissions_tenantRoleId_idx" ON "role_permissions"("tenantRoleId");
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'role_permissions_tenantRoleId_fkey') THEN
|
|
ALTER TABLE "role_permissions" ADD CONSTRAINT "role_permissions_tenantRoleId_fkey"
|
|
FOREIGN KEY ("tenantRoleId") REFERENCES "tenant_roles"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ── RBAC: user_tenant_roles ──
|
|
|
|
CREATE TABLE IF NOT EXISTS "user_tenant_roles" (
|
|
"id" TEXT NOT NULL,
|
|
"userId" TEXT NOT NULL,
|
|
"tenantRoleId" TEXT NOT NULL,
|
|
|
|
CONSTRAINT "user_tenant_roles_pkey" PRIMARY KEY ("id")
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "user_tenant_roles_userId_tenantRoleId_key" ON "user_tenant_roles"("userId", "tenantRoleId");
|
|
CREATE INDEX IF NOT EXISTS "user_tenant_roles_userId_idx" ON "user_tenant_roles"("userId");
|
|
CREATE INDEX IF NOT EXISTS "user_tenant_roles_tenantRoleId_idx" ON "user_tenant_roles"("tenantRoleId");
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'user_tenant_roles_userId_fkey') THEN
|
|
ALTER TABLE "user_tenant_roles" ADD CONSTRAINT "user_tenant_roles_userId_fkey"
|
|
FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'user_tenant_roles_tenantRoleId_fkey') THEN
|
|
ALTER TABLE "user_tenant_roles" ADD CONSTRAINT "user_tenant_roles_tenantRoleId_fkey"
|
|
FOREIGN KEY ("tenantRoleId") REFERENCES "tenant_roles"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ── Recurring Expenses ──
|
|
|
|
CREATE TABLE IF NOT EXISTS "recurring_expenses" (
|
|
"id" TEXT NOT NULL,
|
|
"tenantId" TEXT NOT NULL,
|
|
"category" TEXT NOT NULL,
|
|
"description" TEXT NOT NULL,
|
|
"amount" DECIMAL(10,2) NOT NULL,
|
|
"frequency" TEXT NOT NULL DEFAULT 'monthly',
|
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
|
"nextRunDate" TIMESTAMP(3) NOT NULL,
|
|
"lastRunDate" TIMESTAMP(3),
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
"deletedAt" TIMESTAMP(3),
|
|
|
|
CONSTRAINT "recurring_expenses_pkey" PRIMARY KEY ("id")
|
|
);
|
|
CREATE INDEX IF NOT EXISTS "recurring_expenses_tenantId_idx" ON "recurring_expenses"("tenantId");
|
|
CREATE INDEX IF NOT EXISTS "recurring_expenses_deletedAt_idx" ON "recurring_expenses"("deletedAt");
|
|
|
|
-- ── Payroll Runs ──
|
|
|
|
CREATE TABLE IF NOT EXISTS "payroll_runs" (
|
|
"id" TEXT NOT NULL,
|
|
"tenantId" TEXT NOT NULL,
|
|
"period" TEXT NOT NULL,
|
|
"status" TEXT NOT NULL DEFAULT 'draft',
|
|
"totalAmount" DECIMAL(12,2) NOT NULL DEFAULT 0,
|
|
"processedBy" TEXT,
|
|
"processedAt" TIMESTAMP(3),
|
|
"notes" TEXT,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
"deletedAt" TIMESTAMP(3),
|
|
|
|
CONSTRAINT "payroll_runs_pkey" PRIMARY KEY ("id")
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "payroll_runs_tenantId_period_key" ON "payroll_runs"("tenantId", "period");
|
|
CREATE INDEX IF NOT EXISTS "payroll_runs_tenantId_idx" ON "payroll_runs"("tenantId");
|
|
CREATE INDEX IF NOT EXISTS "payroll_runs_deletedAt_idx" ON "payroll_runs"("deletedAt");
|
|
|
|
-- ── Payslips ──
|
|
|
|
CREATE TABLE IF NOT EXISTS "payslips" (
|
|
"id" TEXT NOT NULL,
|
|
"payrollRunId" TEXT NOT NULL,
|
|
"employeeId" TEXT NOT NULL,
|
|
"baseSalary" DECIMAL(10,2) NOT NULL,
|
|
"deductions" DECIMAL(10,2) NOT NULL DEFAULT 0,
|
|
"bonuses" DECIMAL(10,2) NOT NULL DEFAULT 0,
|
|
"netPay" DECIMAL(10,2) NOT NULL,
|
|
"status" TEXT NOT NULL DEFAULT 'pending',
|
|
"notes" TEXT,
|
|
|
|
CONSTRAINT "payslips_pkey" PRIMARY KEY ("id")
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "payslips_payrollRunId_employeeId_key" ON "payslips"("payrollRunId", "employeeId");
|
|
CREATE INDEX IF NOT EXISTS "payslips_payrollRunId_idx" ON "payslips"("payrollRunId");
|
|
CREATE INDEX IF NOT EXISTS "payslips_employeeId_idx" ON "payslips"("employeeId");
|
|
DO $$ BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'payslips_payrollRunId_fkey') THEN
|
|
ALTER TABLE "payslips" ADD CONSTRAINT "payslips_payrollRunId_fkey"
|
|
FOREIGN KEY ("payrollRunId") REFERENCES "payroll_runs"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'payslips_employeeId_fkey') THEN
|
|
ALTER TABLE "payslips" ADD CONSTRAINT "payslips_employeeId_fkey"
|
|
FOREIGN KEY ("employeeId") REFERENCES "employees"("id") ON UPDATE CASCADE;
|
|
END IF;
|
|
END $$;
|