docs(04-03): complete expense tracking plan
Tasks completed: 2/2 - Task 1: Schema + COA additions + expense category seeding - Task 2: VendorService, ExpenseService, API routes, migration, 15 tests SUMMARY: .planning/phases/04-inventory-expenses-and-financial-reports/04-03-SUMMARY.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,18 +10,18 @@ See: .planning/PROJECT.md (updated 2026-03-04)
|
|||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Phase: 4 of 5 (Inventory, Expenses, and Financial Reports)
|
Phase: 4 of 5 (Inventory, Expenses, and Financial Reports)
|
||||||
Plan: 1 of 3 in phase 4 (18/20 total complete)
|
Plan: 3 of 5 in phase 4 (20/22 total complete)
|
||||||
Status: In progress. 04-01 (Inventory Event-Ledger) complete.
|
Status: In progress. 04-01 and 04-03 complete.
|
||||||
Last activity: 2026-03-05 — Completed 04-01-PLAN.md (Inventory Event-Ledger — 13 tests, dual tracking, immutable movements, JE posting)
|
Last activity: 2026-03-05 — Completed 04-03-PLAN.md (Expense Tracking — 15 tests, vendor management, approval workflow, JE posting)
|
||||||
|
|
||||||
Progress: [██████████████████░░] 90% (18/20 plans across all phases)
|
Progress: [████████████████████] 91% (20/22 plans across all phases)
|
||||||
|
|
||||||
## Performance Metrics
|
## Performance Metrics
|
||||||
|
|
||||||
**Velocity:**
|
**Velocity:**
|
||||||
- Total plans completed: 12
|
- Total plans completed: 13
|
||||||
- Average duration: 10 min
|
- Average duration: 11 min
|
||||||
- Total execution time: 120 min
|
- Total execution time: 137 min
|
||||||
|
|
||||||
**By Phase:**
|
**By Phase:**
|
||||||
|
|
||||||
@@ -30,11 +30,11 @@ Progress: [██████████████████░░] 90% (18
|
|||||||
| 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 | 5/5 complete | 57 min | 11.4 min |
|
| 02-subscriber-and-billing-core | 5/5 complete | 57 min | 11.4 min |
|
||||||
| 03-operational-modules | 5/5 complete | ~65 min | ~13 min |
|
| 03-operational-modules | 5/5 complete | ~65 min | ~13 min |
|
||||||
| 04-inventory-expenses-reports | 1/3 complete | 16 min | 16 min |
|
| 04-inventory-expenses-reports | 2/5 complete | 33 min | 16.5 min |
|
||||||
|
|
||||||
**Recent Trend:**
|
**Recent Trend:**
|
||||||
- Last 10 plans: 02-01 (7 min), 02-03 (7 min), 02-02 (16 min), 02-04 (12 min), 02-05 (8 min), 03-01 (15 min), 03-02 (11 min), 03-04 (6 min), 03-05 (9 min), 04-01 (16 min)
|
- Last 10 plans: 02-02 (16 min), 02-04 (12 min), 02-05 (8 min), 03-01 (15 min), 03-02 (11 min), 03-04 (6 min), 03-05 (9 min), 04-01 (16 min), 04-03 (17 min)
|
||||||
- Trend: stable — 04-01 migration applied cleanly, all 13 tests passed on first run
|
- Trend: stable — 04-03 all 15 tests passed, migration via db push + resolve
|
||||||
|
|
||||||
*Updated after each plan completion*
|
*Updated after each plan completion*
|
||||||
|
|
||||||
@@ -124,6 +124,12 @@ Recent decisions affecting current work:
|
|||||||
- [04-01]: CASL subject is "Inventory" (existing type in types.ts) — OFFICE_STAFF gets can("manage", "Inventory")
|
- [04-01]: CASL subject is "Inventory" (existing type in types.ts) — OFFICE_STAFF gets can("manage", "Inventory")
|
||||||
- [04-01]: Stock levels derived in JS from movement aggregation — acceptable for ISP scale (same pattern as collector balances, outstanding reports)
|
- [04-01]: Stock levels derived in JS from movement aggregation — acceptable for ISP scale (same pattern as collector balances, outstanding reports)
|
||||||
- [04-01]: Inventory cleanup order in tests: stockMovements -> inventoryItems -> journalEntryLines -> null reversesEntryId -> journalEntries -> accountingPeriods -> accounts -> users -> tenant
|
- [04-01]: Inventory cleanup order in tests: stockMovements -> inventoryItems -> journalEntryLines -> null reversesEntryId -> journalEntries -> accountingPeriods -> accounts -> users -> tenant
|
||||||
|
- [04-03]: ExpensePaymentMethod determines CR account: CASH->1010, BANK_TRANSFER/CHECK->1020
|
||||||
|
- [04-03]: Default behavior is immediate post (no approval required); requireApproval flag enables DRAFT-only creation
|
||||||
|
- [04-03]: System expense categories (isSystemCategory=true) cannot be deleted; custom categories deletable if no expenses reference them
|
||||||
|
- [04-03]: Vendor added as CASL subject; OFFICE_STAFF gets manage Expense and manage Vendor
|
||||||
|
- [04-03]: ISP COA now has 31 accounts — added 5080 Fuel/Transportation, 5085 Rent Expense
|
||||||
|
- [04-03]: Expense cleanup order: expenses -> vendors -> expenseCategories (non-system) -> journalEntryLines -> null reversesEntryId -> journalEntries -> accountingPeriods -> accounts -> ticketCategories -> expenseCategories (system) -> users -> tenant
|
||||||
|
|
||||||
### Pending Todos
|
### Pending Todos
|
||||||
|
|
||||||
@@ -141,6 +147,6 @@ None.
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-03-05T02:32:00Z
|
Last session: 2026-03-05T02:52:00Z
|
||||||
Stopped at: Completed 04-01-PLAN.md (Inventory Event-Ledger — 13 tests, dual tracking, immutable movements)
|
Stopped at: Completed 04-03-PLAN.md (Expense Tracking — 15 tests, vendor management, approval workflow, JE posting)
|
||||||
Resume file: None
|
Resume file: None
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
---
|
||||||
|
phase: 04-inventory-expenses-and-financial-reports
|
||||||
|
plan: 03
|
||||||
|
subsystem: expenses, accounting
|
||||||
|
tags: [expense-tracking, vendor-management, journal-entries, double-entry, approval-workflow, prisma]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires:
|
||||||
|
- phase: 02-subscriber-and-billing-core
|
||||||
|
provides: "JournalEntryService, Chart of Accounts, accounting period management"
|
||||||
|
- phase: 01-foundation
|
||||||
|
provides: "Tenant provisioning, CASL permissions, withTenantContext"
|
||||||
|
provides:
|
||||||
|
- "Vendor model and VendorService (CRUD)"
|
||||||
|
- "ExpenseCategory model with 9 pre-seeded ISP categories"
|
||||||
|
- "Expense model with ExpenseService (create, approve, post, void)"
|
||||||
|
- "Automatic JE posting on expense post (DR expense account, CR cash/bank)"
|
||||||
|
- "Optional approval workflow (DRAFT -> APPROVED -> POSTED)"
|
||||||
|
- "API routes for expenses, categories, and vendors"
|
||||||
|
- "COA accounts 5080 Fuel/Transportation, 5085 Rent Expense (31 total)"
|
||||||
|
affects: [04-05-financial-reports, 05-dashboard]
|
||||||
|
|
||||||
|
# Tech tracking
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "Expense JE pattern: DR category.accountCode, CR 1010/1020 based on paymentMethod"
|
||||||
|
- "Optional approval workflow via requireApproval flag"
|
||||||
|
- "ExpensePaymentMethod enum determines CR account (CASH->1010, BANK_TRANSFER/CHECK->1020)"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- "src/lib/services/expense-service.ts"
|
||||||
|
- "src/lib/services/vendor-service.ts"
|
||||||
|
- "src/app/api/expenses/route.ts"
|
||||||
|
- "src/app/api/expenses/[id]/route.ts"
|
||||||
|
- "src/app/api/expenses/[id]/approve/route.ts"
|
||||||
|
- "src/app/api/expenses/categories/route.ts"
|
||||||
|
- "src/app/api/expenses/categories/[id]/route.ts"
|
||||||
|
- "src/app/api/vendors/route.ts"
|
||||||
|
- "src/app/api/vendors/[id]/route.ts"
|
||||||
|
- "src/lib/__tests__/expense-service.test.ts"
|
||||||
|
- "prisma/migrations/20260305_add_expense_vendor_models/migration.sql"
|
||||||
|
modified:
|
||||||
|
- "prisma/schema.prisma"
|
||||||
|
- "src/lib/tenant.ts"
|
||||||
|
- "src/lib/accounting/chart-of-accounts.ts"
|
||||||
|
- "src/lib/casl/types.ts"
|
||||||
|
- "src/lib/casl/permissions.ts"
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "ExpensePaymentMethod determines CR account: CASH->1010, BANK_TRANSFER/CHECK->1020"
|
||||||
|
- "Default behavior is immediate post (no approval required); requireApproval flag enables DRAFT-only creation"
|
||||||
|
- "System expense categories (isSystemCategory=true) cannot be deleted; custom categories can be deleted if no expenses reference them"
|
||||||
|
- "Vendor subject added to CASL types; OFFICE_STAFF gets manage permissions for Expense and Vendor"
|
||||||
|
- "COA now has 31 accounts (added 5080 Fuel/Transportation, 5085 Rent Expense)"
|
||||||
|
- "Expense cleanup order: expenses -> vendors -> expenseCategories (non-system) -> journalEntryLines -> null reversesEntryId -> journalEntries -> accountingPeriods -> accounts -> ticketCategories -> expenseCategories (system) -> users -> tenant"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "Expense JE: DR category expense account (from accountCode), CR cash/bank account"
|
||||||
|
- "Optional approval workflow pattern: requireApproval flag on create, approveExpense triggers post"
|
||||||
|
- "Category-to-COA linking via accountCode field on ExpenseCategory"
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
duration: 17min
|
||||||
|
completed: 2026-03-05
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 4 Plan 3: Expense Tracking Summary
|
||||||
|
|
||||||
|
**Expense tracking with vendor management, 9 pre-seeded categories, optional approval workflow, and automatic double-entry JE posting on every expense**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **Duration:** 17 min
|
||||||
|
- **Started:** 2026-03-05T02:35:00Z
|
||||||
|
- **Completed:** 2026-03-05T02:52:00Z
|
||||||
|
- **Tasks:** 2
|
||||||
|
- **Files modified:** 16
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
- Full expense lifecycle: create, approve, post, void with JE integration
|
||||||
|
- 9 default ISP expense categories seeded at tenant creation (bandwidth, equipment, salary, etc.)
|
||||||
|
- Vendor CRUD with unique name per tenant
|
||||||
|
- Every posted expense creates balanced JE (DR expense account from category, CR cash/bank)
|
||||||
|
- Optional approval workflow (DRAFT -> APPROVED -> POSTED when enabled)
|
||||||
|
- 15 integration tests covering all expense scenarios
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
Each task was committed atomically:
|
||||||
|
|
||||||
|
1. **Task 1: Schema + COA + category seeding** - `fe4d22f` (feat)
|
||||||
|
2. **Task 2: Services, API routes, migration, tests** - `b7bbc50` (feat)
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
- `prisma/schema.prisma` - Added Vendor, ExpenseCategory, Expense models + enums
|
||||||
|
- `src/lib/tenant.ts` - Added expense category seeding in createTenant transaction
|
||||||
|
- `src/lib/accounting/chart-of-accounts.ts` - Added 5080, 5085 expense accounts (31 total)
|
||||||
|
- `src/lib/casl/types.ts` - Added Vendor subject
|
||||||
|
- `src/lib/casl/permissions.ts` - Added Expense/Vendor permissions for OFFICE_STAFF
|
||||||
|
- `src/lib/services/vendor-service.ts` - VendorService: create, update, list, get
|
||||||
|
- `src/lib/services/expense-service.ts` - ExpenseService: create, approve, post, void, list, categories CRUD
|
||||||
|
- `src/app/api/expenses/route.ts` - POST/GET expense endpoints
|
||||||
|
- `src/app/api/expenses/[id]/route.ts` - GET expense detail
|
||||||
|
- `src/app/api/expenses/[id]/approve/route.ts` - POST approve expense
|
||||||
|
- `src/app/api/expenses/categories/route.ts` - GET/POST expense categories
|
||||||
|
- `src/app/api/expenses/categories/[id]/route.ts` - PUT expense category
|
||||||
|
- `src/app/api/vendors/route.ts` - GET/POST vendor endpoints
|
||||||
|
- `src/app/api/vendors/[id]/route.ts` - GET/PUT vendor detail
|
||||||
|
- `src/lib/__tests__/expense-service.test.ts` - 15 integration tests
|
||||||
|
- `prisma/migrations/20260305_add_expense_vendor_models/migration.sql` - DB migration
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
- ExpensePaymentMethod determines CR account: CASH -> 1010 Cash on Hand, BANK_TRANSFER/CHECK -> 1020 Cash in Bank
|
||||||
|
- Default behavior is immediate post (requireApproval=false); when enabled, expense stays DRAFT until approved
|
||||||
|
- System expense categories protected from deletion; custom categories deletable only if no expenses reference them
|
||||||
|
- Added Vendor as a new CASL subject (separate from Expense) for granular permissions
|
||||||
|
- COA expanded to 31 accounts with Fuel/Transportation (5080) and Rent Expense (5085)
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Auto-fixed Issues
|
||||||
|
|
||||||
|
**1. [Rule 2 - Missing Critical] Added Vendor to CASL types and OFFICE_STAFF permissions**
|
||||||
|
- **Found during:** Task 1 (Schema + permissions)
|
||||||
|
- **Issue:** Plan specified API routes for vendors with ADMIN/OFFICE_STAFF access, but Vendor was not in AppSubjects and OFFICE_STAFF had no Expense/Vendor permissions
|
||||||
|
- **Fix:** Added "Vendor" to AppSubjects type, added can("manage", "Expense") and can("manage", "Vendor") for OFFICE_STAFF role
|
||||||
|
- **Files modified:** src/lib/casl/types.ts, src/lib/casl/permissions.ts
|
||||||
|
- **Verification:** API routes compile with correct permission checks
|
||||||
|
- **Committed in:** fe4d22f (Task 1 commit)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Total deviations:** 1 auto-fixed (1 missing critical)
|
||||||
|
**Impact on plan:** Essential for API route authorization. No scope creep.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
- Docker exec commands were unresponsive; switched to `DATABASE_URL_LOCAL` with `prisma db push` for migration (same result, different approach)
|
||||||
|
|
||||||
|
## User Setup Required
|
||||||
|
None - no external service configuration required.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
- Expense tracking complete, all costs hit the double-entry ledger automatically
|
||||||
|
- Financial reports in 04-05 can now include expense data alongside revenue
|
||||||
|
- Ready for 04-04 (if exists) or 04-05 Financial Reports
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 04-inventory-expenses-and-financial-reports*
|
||||||
|
*Completed: 2026-03-05*
|
||||||
Reference in New Issue
Block a user