--- 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*