docs(02-01): complete Chart of Accounts plan
Tasks completed: 2/2 - Task 1: Account and AccountingPeriod Prisma models + COA definition - Task 2: COA auto-provisioning on tenant signup + API routes + tests SUMMARY: .planning/phases/02-subscriber-and-billing-core/02-01-SUMMARY.md
This commit is contained in:
@@ -9,29 +9,30 @@ See: .planning/PROJECT.md (updated 2026-03-04)
|
|||||||
|
|
||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Phase: 1 of 5 (Foundation) — COMPLETE
|
Phase: 2 of 5 (Subscriber and Billing Core) — In progress
|
||||||
Plan: 5 of 5 in phase 1 complete
|
Plan: 1 of 5 in phase 2 complete (6/20 total)
|
||||||
Status: Phase 1 complete. Ready for Phase 2.
|
Status: In progress. 02-01 (COA + Accounting Periods) complete.
|
||||||
Last activity: 2026-03-04 — Completed 01-05-PLAN.md (super-admin panel, tenant management, 93 tests)
|
Last activity: 2026-03-04 — Completed 02-01-PLAN.md (ISP Chart of Accounts, accounting period management, 121 tests)
|
||||||
|
|
||||||
Progress: [█████░░░░░] 25% (5/20 plans across all phases)
|
Progress: [██████░░░░] 30% (6/20 plans across all phases)
|
||||||
|
|
||||||
## Performance Metrics
|
## Performance Metrics
|
||||||
|
|
||||||
**Velocity:**
|
**Velocity:**
|
||||||
- Total plans completed: 5
|
- Total plans completed: 6
|
||||||
- Average duration: 8.2 min
|
- Average duration: 8.0 min
|
||||||
- Total execution time: 41 min
|
- Total execution time: 48 min
|
||||||
|
|
||||||
**By Phase:**
|
**By Phase:**
|
||||||
|
|
||||||
| Phase | Plans | Total | Avg/Plan |
|
| Phase | Plans | Total | Avg/Plan |
|
||||||
|-------|-------|-------|----------|
|
|-------|-------|-------|----------|
|
||||||
| 01-foundation | 5/5 complete | 41 min | 8.2 min |
|
| 01-foundation | 5/5 complete | 41 min | 8.2 min |
|
||||||
|
| 02-subscriber-and-billing-core | 1/5 complete | 7 min | 7 min |
|
||||||
|
|
||||||
**Recent Trend:**
|
**Recent Trend:**
|
||||||
- Last 5 plans: 01-01 (11 min), 01-02 (8 min), 01-03 (9 min), 01-04 (7 min), 01-05 (6 min)
|
- Last 6 plans: 01-01 (11 min), 01-02 (8 min), 01-03 (9 min), 01-04 (7 min), 01-05 (6 min), 02-01 (7 min)
|
||||||
- Trend: gradually accelerating (11 → 6 min)
|
- Trend: stable around 7-8 min
|
||||||
|
|
||||||
*Updated after each plan completion*
|
*Updated after each plan completion*
|
||||||
|
|
||||||
@@ -68,6 +69,11 @@ Recent decisions affecting current work:
|
|||||||
- [01-05]: Next.js 15 route params wrapped in Promise<P> — HOF awaits params before passing to handler
|
- [01-05]: Next.js 15 route params wrapped in Promise<P> — HOF awaits params before passing to handler
|
||||||
- [01-05]: subscriberCount hardcoded to 0 in admin API — Subscriber model added in Phase 2; API shape is forward-compatible
|
- [01-05]: subscriberCount hardcoded to 0 in admin API — Subscriber model added in Phase 2; API shape is forward-compatible
|
||||||
- [01-05]: Dual guard strategy for /admin: middleware.ts (JWT edge), layout.tsx (server), API handlers (endpoint) — three defense-in-depth layers
|
- [01-05]: Dual guard strategy for /admin: middleware.ts (JWT edge), layout.tsx (server), API handlers (endpoint) — three defense-in-depth layers
|
||||||
|
- [02-01]: ISP COA has 28 accounts (5 category headers 1000/2000/3000/4000/5000 + 23 leaf accounts) — hierarchical for reporting
|
||||||
|
- [02-01]: Subscriber Credits (1150) is contra-asset with CREDIT normal balance — correctly reduces AR for overpayments
|
||||||
|
- [02-01]: seedChartOfAccounts receives Prisma tx client — works inside createTenant $transaction for atomic provisioning
|
||||||
|
- [02-01]: Accounting periods created on-demand via getOpenPeriod() — not pre-seeded on signup (no wasted periods for unused months)
|
||||||
|
- [02-01]: close route uses closure pattern over withPermission HOF — withPermission doesn't support dynamic params directly; POST fn closes over Next.js params
|
||||||
|
|
||||||
### Pending Todos
|
### Pending Todos
|
||||||
|
|
||||||
@@ -82,6 +88,6 @@ None.
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-03-04T11:07:04Z
|
Last session: 2026-03-04T14:51:56Z
|
||||||
Stopped at: Completed 01-05-PLAN.md (super-admin panel + tenant management + 93 total tests)
|
Stopped at: Completed 02-01-PLAN.md (COA + accounting period management + 28 new tests, 121 total)
|
||||||
Resume file: None
|
Resume file: None
|
||||||
|
|||||||
142
.planning/phases/02-subscriber-and-billing-core/02-01-SUMMARY.md
Normal file
142
.planning/phases/02-subscriber-and-billing-core/02-01-SUMMARY.md
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
---
|
||||||
|
phase: 02-subscriber-and-billing-core
|
||||||
|
plan: "01"
|
||||||
|
subsystem: database
|
||||||
|
tags: [prisma, postgresql, accounting, double-entry, chart-of-accounts, multi-tenancy]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires:
|
||||||
|
- phase: 01-foundation
|
||||||
|
provides: Tenant model, User model, createTenant(), withTenantContext(), withPermission() HOF
|
||||||
|
|
||||||
|
provides:
|
||||||
|
- Account Prisma model with tenant scoping, code/name/accountType/normalBalance/parentId
|
||||||
|
- AccountingPeriod Prisma model with year/month/status/closedAt/closedById
|
||||||
|
- ISP_CHART_OF_ACCOUNTS: 28-account definition covering all 5 accounting types
|
||||||
|
- seedChartOfAccounts(tx, tenantId): auto-provisions COA inside Prisma transaction
|
||||||
|
- createTenant() now provisions full COA atomically with tenant+user creation
|
||||||
|
- closePeriod, getOpenPeriod, isDateInClosedPeriod functions
|
||||||
|
- GET /api/accounting/accounts — list COA for authenticated tenant
|
||||||
|
- GET /api/accounting/periods — list accounting periods
|
||||||
|
- POST /api/accounting/periods/[id]/close — close a period
|
||||||
|
|
||||||
|
affects:
|
||||||
|
- 02-02-journal-entry-service (posts to Account records created here; enforces closed periods)
|
||||||
|
- 02-03-invoice-billing (uses Subscription Revenue 4010, Accounts Receivable 1100)
|
||||||
|
- 02-04-payment-collection (uses Cash accounts 1010/1020, credits AR)
|
||||||
|
- All future phases that touch financial data
|
||||||
|
|
||||||
|
# Tech tracking
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "COA auto-provisioning in Prisma $transaction — atomic tenant+user+COA creation"
|
||||||
|
- "Zero mutable balance fields — all balances derived from journal entry sums"
|
||||||
|
- "On-demand period creation via getOpenPeriod() — periods created at first use"
|
||||||
|
- "TxClient pattern — seed functions accept transaction client, not full PrismaClient"
|
||||||
|
- "withTenantContext() extended with account and accountingPeriod query blocks"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- prisma/migrations/20260304144656_add_accounting_models/migration.sql
|
||||||
|
- src/lib/accounting/chart-of-accounts.ts
|
||||||
|
- src/lib/accounting/accounting-period.ts
|
||||||
|
- src/lib/accounting/seed-coa.ts
|
||||||
|
- src/app/api/accounting/accounts/route.ts
|
||||||
|
- src/app/api/accounting/periods/route.ts
|
||||||
|
- src/app/api/accounting/periods/[id]/close/route.ts
|
||||||
|
- src/lib/__tests__/accounting-coa.test.ts
|
||||||
|
modified:
|
||||||
|
- prisma/schema.prisma
|
||||||
|
- src/lib/prisma-tenant.ts
|
||||||
|
- src/lib/tenant.ts
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "28 accounts in ISP COA — 5 parent headers (1000/2000/3000/4000/5000) + 23 leaf accounts"
|
||||||
|
- "Subscriber Credits (1150) is contra-asset with CREDIT normal balance — reduces AR"
|
||||||
|
- "seedChartOfAccounts receives Prisma tx client (not full PrismaClient) — works inside $transaction"
|
||||||
|
- "Periods created on-demand via getOpenPeriod() — not pre-seeded at tenant creation"
|
||||||
|
- "closedPeriods back-reference added on User model for Prisma relation integrity"
|
||||||
|
- "close route uses closure pattern (withPermission wrapping inside POST fn) for param access"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "Accounting isolation: zero mutable balance fields — enforced at model level (no balanceColumn exists)"
|
||||||
|
- "COA seeding: ISP_CHART_OF_ACCOUNTS array ordered parents-before-children for correct parentId resolution"
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
duration: 7min
|
||||||
|
completed: 2026-03-04
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 2 Plan 01: Chart of Accounts and Accounting Period Management Summary
|
||||||
|
|
||||||
|
**ISP double-entry COA (28 accounts, all 5 types) auto-provisioned atomically on tenant signup, with period open/close management — zero mutable balance fields anywhere**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **Duration:** 7 min
|
||||||
|
- **Started:** 2026-03-04T14:44:47Z
|
||||||
|
- **Completed:** 2026-03-04T14:51:56Z
|
||||||
|
- **Tasks:** 2
|
||||||
|
- **Files modified:** 11
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- Account and AccountingPeriod Prisma models added with full tenant scoping (RLS-ready indexes, @@unique per tenant)
|
||||||
|
- 28-account ISP Chart of Accounts (1000-5000 ranges) with correct normal balances across all 5 types
|
||||||
|
- Every new tenant signup atomically provisions a complete COA inside the existing $transaction
|
||||||
|
- Accounting period close logic (closePeriod, getOpenPeriod, isDateInClosedPeriod) ready for journal entry enforcement in 02-02
|
||||||
|
- 28 new tests (121 total) covering COA definition purity, seeding correctness, period management, and createTenant integration
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
Each task was committed atomically:
|
||||||
|
|
||||||
|
1. **Task 1: Account and AccountingPeriod Prisma models + COA definition** - `7c0caf5` (feat)
|
||||||
|
2. **Task 2: COA auto-provisioning on tenant signup + API routes + tests** - `a53ee9c` (feat)
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
- `prisma/schema.prisma` - Added AccountType/NormalBalance/PeriodStatus enums, Account model, AccountingPeriod model with User.closedPeriods back-reference
|
||||||
|
- `prisma/migrations/20260304144656_add_accounting_models/migration.sql` - Migration creating account and accounting_period tables
|
||||||
|
- `src/lib/accounting/chart-of-accounts.ts` - ISP_CHART_OF_ACCOUNTS (28 accounts), AccountType and NormalBalance TypeScript types
|
||||||
|
- `src/lib/accounting/accounting-period.ts` - getOpenPeriod, closePeriod, isDateInClosedPeriod functions
|
||||||
|
- `src/lib/accounting/seed-coa.ts` - seedChartOfAccounts(tx, tenantId) — resolves parentCode to parentId during seeding
|
||||||
|
- `src/lib/prisma-tenant.ts` - Added "account" and "accountingPeriod" to TENANT_SCOPED_MODELS; full query extension blocks for both models
|
||||||
|
- `src/lib/tenant.ts` - createTenant() now calls seedChartOfAccounts(tx, tenant.id) inside the existing $transaction
|
||||||
|
- `src/app/api/accounting/accounts/route.ts` - GET /api/accounting/accounts (read:Account permission)
|
||||||
|
- `src/app/api/accounting/periods/route.ts` - GET /api/accounting/periods (read:Account permission)
|
||||||
|
- `src/app/api/accounting/periods/[id]/close/route.ts` - POST /api/accounting/periods/[id]/close (manage:Account permission)
|
||||||
|
- `src/lib/__tests__/accounting-coa.test.ts` - 28 integration and unit tests
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
- **28 accounts with parent headers:** 5 category headers (1000, 2000, 3000, 4000, 5000) plus 23 leaf accounts — provides hierarchical COA structure for reporting
|
||||||
|
- **Contra-asset 1150:** Subscriber Credits has CREDIT normal balance despite being ASSET type — correctly models overpayments that reduce the AR balance
|
||||||
|
- **TxClient via `any` cast in tests:** Prisma transaction client types don't align perfectly with interface extraction; using `any` in test is safe (the actual seedChartOfAccounts function is typed correctly via PrismaClient.$transaction parameter inference)
|
||||||
|
- **Close route uses closure pattern:** `withPermission` HOF doesn't support route params directly — POST function closes over `params` from Next.js route context before invoking the HOF
|
||||||
|
- **On-demand period creation:** Periods are created on first use (getOpenPeriod), not pre-seeded — avoids creating 12 periods per tenant on signup for months that may never have entries
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
None — plan executed exactly as written.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## User Setup Required
|
||||||
|
|
||||||
|
None — no external service configuration required.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
- Account and AccountingPeriod models in database — ready for journal entry posting (02-02)
|
||||||
|
- All 28 standard ISP accounts provisioned for every new tenant — journal entries can reference accounts by code
|
||||||
|
- closedPeriods enforcement hook ready — 02-02 JournalEntryService calls isDateInClosedPeriod before posting
|
||||||
|
- CASL "Account" subject already defined in casl/types.ts — permissions are operational
|
||||||
|
- withPermission("manage", "Account") guards period close endpoint — aligns with ADMIN-only restriction
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 02-subscriber-and-billing-core*
|
||||||
|
*Completed: 2026-03-04*
|
||||||
Reference in New Issue
Block a user