feat(02-01): Account and AccountingPeriod Prisma models + COA definition
- Add AccountType, NormalBalance, PeriodStatus enums to schema - Add Account model with tenant scoping, code/name/type/normalBalance/parentId - Add AccountingPeriod model with year/month/status/closedAt/closedById - Create ISP_CHART_OF_ACCOUNTS with 28 accounts across all 5 types (1000-5000 ranges) - Create accounting-period.ts with getOpenPeriod, closePeriod, isDateInClosedPeriod - Extend TENANT_SCOPED_MODELS with account and accountingPeriod - Add full query extension blocks for account and accountingPeriod in withTenantContext - Run migration: 20260304144656_add_accounting_models
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "AccountType" AS ENUM ('ASSET', 'LIABILITY', 'EQUITY', 'REVENUE', 'EXPENSE');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "NormalBalance" AS ENUM ('DEBIT', 'CREDIT');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "PeriodStatus" AS ENUM ('OPEN', 'CLOSED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Account" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"accountType" "AccountType" NOT NULL,
|
||||
"normalBalance" "NormalBalance" NOT NULL,
|
||||
"parentId" TEXT,
|
||||
"isSystemAccount" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Account_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "AccountingPeriod" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"year" INTEGER NOT NULL,
|
||||
"month" INTEGER NOT NULL,
|
||||
"status" "PeriodStatus" NOT NULL DEFAULT 'OPEN',
|
||||
"closedAt" TIMESTAMP(3),
|
||||
"closedById" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "AccountingPeriod_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Account_tenantId_idx" ON "Account"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Account_tenantId_code_key" ON "Account"("tenantId", "code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "AccountingPeriod_tenantId_idx" ON "AccountingPeriod"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "AccountingPeriod_tenantId_year_month_key" ON "AccountingPeriod"("tenantId", "year", "month");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Account" ADD CONSTRAINT "Account_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "Account"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "AccountingPeriod" ADD CONSTRAINT "AccountingPeriod_closedById_fkey" FOREIGN KEY ("closedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -39,6 +39,24 @@ enum Role {
|
||||
CLIENT
|
||||
}
|
||||
|
||||
enum AccountType {
|
||||
ASSET
|
||||
LIABILITY
|
||||
EQUITY
|
||||
REVENUE
|
||||
EXPENSE
|
||||
}
|
||||
|
||||
enum NormalBalance {
|
||||
DEBIT
|
||||
CREDIT
|
||||
}
|
||||
|
||||
enum PeriodStatus {
|
||||
OPEN
|
||||
CLOSED
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// MODELS
|
||||
// =============================================================================
|
||||
@@ -69,6 +87,56 @@ model Tenant {
|
||||
users User[]
|
||||
}
|
||||
|
||||
/// An Account represents a node in the Chart of Accounts tree.
|
||||
/// Account balances are NEVER stored — they are always derived from journal entry sums.
|
||||
/// System accounts (isSystemAccount=true) are auto-created from ISP_CHART_OF_ACCOUNTS
|
||||
/// and cannot be deleted by tenant admins.
|
||||
model Account {
|
||||
id String @id @default(uuid())
|
||||
tenantId String
|
||||
/// Account code (e.g., "1010" for Cash on Hand). Unique per tenant.
|
||||
code String
|
||||
name String
|
||||
accountType AccountType
|
||||
normalBalance NormalBalance
|
||||
/// Optional parent account id for hierarchical COA (e.g., 1010 parent = 1000)
|
||||
parentId String?
|
||||
parent Account? @relation("AccountSubAccounts", fields: [parentId], references: [id])
|
||||
children Account[] @relation("AccountSubAccounts")
|
||||
/// COA accounts seeded at tenant creation cannot be deleted by admins
|
||||
isSystemAccount Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
/// Account codes must be unique within a tenant
|
||||
@@unique([tenantId, code])
|
||||
/// RLS-ready index — always present on tenant-scoped models
|
||||
@@index([tenantId])
|
||||
}
|
||||
|
||||
/// An AccountingPeriod represents one calendar month for a tenant.
|
||||
/// Once CLOSED, no journal entries may be posted to that period.
|
||||
/// Periods are created on-demand (first time an entry needs one).
|
||||
model AccountingPeriod {
|
||||
id String @id @default(uuid())
|
||||
tenantId String
|
||||
year Int
|
||||
month Int
|
||||
status PeriodStatus @default(OPEN)
|
||||
/// Timestamp when the period was closed (null if OPEN)
|
||||
closedAt DateTime?
|
||||
/// The admin user who closed this period
|
||||
closedById String?
|
||||
closedBy User? @relation(fields: [closedById], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
/// One period per month per tenant
|
||||
@@unique([tenantId, year, month])
|
||||
/// RLS-ready index — always present on tenant-scoped models
|
||||
@@index([tenantId])
|
||||
}
|
||||
|
||||
/// A User belongs to a Tenant (or is a super-admin with no tenant).
|
||||
/// Email uniqueness is enforced per-tenant, not globally.
|
||||
/// Super-admins have isSuperAdmin=true and tenantId=null.
|
||||
@@ -89,6 +157,9 @@ model User {
|
||||
isActive Boolean @default(true)
|
||||
isSuperAdmin Boolean @default(false)
|
||||
|
||||
/// Accounting periods this user has closed (as administrator)
|
||||
closedPeriods AccountingPeriod[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
|
||||
Reference in New Issue
Block a user