Phase 04: 5 plans in 2 waves - Wave 1: 04-01 (inventory event-ledger), 04-03 (expense tracking), 04-05 (financial reports) — parallel - Wave 2: 04-02 (asset management), 04-04 (expense reports + audit trail) — sequential - Ready for execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
163 lines
8.0 KiB
Markdown
163 lines
8.0 KiB
Markdown
---
|
|
phase: 04-inventory-expenses-and-financial-reports
|
|
plan: 04
|
|
type: execute
|
|
wave: 2
|
|
depends_on: ["04-03"]
|
|
files_modified:
|
|
- src/lib/services/expense-report-service.ts
|
|
- src/lib/services/audit-trail-service.ts
|
|
- src/app/api/reports/expenses/route.ts
|
|
- src/app/api/reports/expenses/by-vendor/route.ts
|
|
- src/app/api/accounting/journal-entries/[id]/audit/route.ts
|
|
- src/lib/__tests__/expense-report-service.test.ts
|
|
autonomous: true
|
|
|
|
must_haves:
|
|
truths:
|
|
- "Admin can generate expense reports filtered by category, vendor, and date range"
|
|
- "Expense report shows totals per category and per vendor for the period"
|
|
- "Every journal entry has a full audit trail: who created it, when, what source transaction"
|
|
- "Audit trail shows referenceType and referenceId linking JE back to source (Invoice, Payment, Expense, StockMovement, Collection, Remittance)"
|
|
artifacts:
|
|
- path: "src/lib/services/expense-report-service.ts"
|
|
provides: "getExpensesByCategory, getExpensesByVendor, getExpenseSummary"
|
|
exports: ["ExpenseReportService"]
|
|
- path: "src/lib/services/audit-trail-service.ts"
|
|
provides: "getJournalEntryAudit, getAuditTrailForEntity"
|
|
exports: ["AuditTrailService"]
|
|
- path: "src/lib/__tests__/expense-report-service.test.ts"
|
|
provides: "Tests for expense reports and audit trail"
|
|
min_lines: 60
|
|
key_links:
|
|
- from: "src/lib/services/expense-report-service.ts"
|
|
to: "prisma.expense"
|
|
via: "Aggregation queries on expense records"
|
|
pattern: "expense\\.(groupBy|findMany|aggregate)"
|
|
- from: "src/lib/services/audit-trail-service.ts"
|
|
to: "prisma.journalEntry"
|
|
via: "Query JE with createdBy, approvedBy, referenceType"
|
|
pattern: "journalEntry\\.find"
|
|
---
|
|
|
|
<objective>
|
|
Build expense reporting (by category, vendor, period) and the journal entry audit trail that links every accounting entry back to its source transaction.
|
|
|
|
Purpose: Gives ISP owners spending visibility (where money goes) and auditors the ability to trace any ledger entry to its origin. ACCT-08 (audit trail) applies to ALL journal entries, not just expense ones.
|
|
Output: ExpenseReportService, AuditTrailService, report API routes, tests.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@C:\Users\KevinAsprec\.claude/get-shit-done/workflows/execute-plan.md
|
|
@C:\Users\KevinAsprec\.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/04-inventory-expenses-and-financial-reports/04-CONTEXT.md
|
|
@.planning/phases/04-inventory-expenses-and-financial-reports/04-03-SUMMARY.md
|
|
@prisma/schema.prisma
|
|
@src/lib/accounting/journal-entry-service.ts
|
|
@src/lib/services/expense-service.ts
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: ExpenseReportService and AuditTrailService</name>
|
|
<files>
|
|
src/lib/services/expense-report-service.ts
|
|
src/lib/services/audit-trail-service.ts
|
|
src/app/api/reports/expenses/route.ts
|
|
src/app/api/reports/expenses/by-vendor/route.ts
|
|
src/app/api/accounting/journal-entries/[id]/audit/route.ts
|
|
</files>
|
|
<action>
|
|
**ExpenseReportService** (`src/lib/services/expense-report-service.ts`):
|
|
Static class:
|
|
|
|
- `getExpensesByCategory(tenantPrisma, { startDate, endDate })`:
|
|
- Query POSTED expenses grouped by categoryId within date range
|
|
- Return: array of { categoryId, categoryName, totalAmount (Decimal), expenseCount (number) }
|
|
- Order by totalAmount DESC
|
|
|
|
- `getExpensesByVendor(tenantPrisma, { startDate, endDate, vendorId? })`:
|
|
- Query POSTED expenses grouped by vendorId within date range
|
|
- Optional vendorId filter for single-vendor detail
|
|
- Return: array of { vendorId, vendorName, totalAmount, expenseCount }
|
|
- Include a "No Vendor" bucket for expenses without vendorId
|
|
- Order by totalAmount DESC
|
|
|
|
- `getExpenseSummary(tenantPrisma, { startDate, endDate })`:
|
|
- Return combined report: { totalExpenses (Decimal), byCategory: [...], byVendor: [...], expenseCount }
|
|
- Calls the two methods above and computes grand total
|
|
|
|
**AuditTrailService** (`src/lib/services/audit-trail-service.ts`):
|
|
Static class:
|
|
|
|
- `getJournalEntryAudit(tenantPrisma, entryId)`:
|
|
- Fetch JE with include: createdBy (select id, email, firstName, lastName), approvedBy, lines with account details
|
|
- Return: { entryNumber, date, description, source, status, referenceType, referenceId, createdBy: {name, email}, createdAt, approvedBy?: {name, email}, approvedAt?, lines: [{account code+name, debit, credit}] }
|
|
- This provides ACCT-08: who created it, when, what source transaction
|
|
|
|
- `getAuditTrailForEntity(tenantPrisma, { referenceType, referenceId })`:
|
|
- Fetch all JEs where referenceType and referenceId match
|
|
- Returns array of JE audit records — enables "show all journal entries for this invoice" or "for this payment"
|
|
- Includes reversing entries (where reversesEntryId links to matching JEs)
|
|
|
|
**API Routes:**
|
|
- `GET /api/reports/expenses` — expense summary by category. Query: startDate, endDate. ADMIN only.
|
|
- `GET /api/reports/expenses/by-vendor` — expense summary by vendor. Query: startDate, endDate, vendorId?. ADMIN only.
|
|
- `GET /api/accounting/journal-entries/[id]/audit` — full audit trail for a JE. ADMIN, OFFICE_STAFF.
|
|
|
|
All routes use withPermission() HOF.
|
|
</action>
|
|
<verify>API route files exist and export correct HTTP methods</verify>
|
|
<done>ExpenseReportService provides category and vendor expense reports; AuditTrailService links every JE to its creator and source transaction</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Tests for expense reports and audit trail</name>
|
|
<files>src/lib/__tests__/expense-report-service.test.ts</files>
|
|
<action>
|
|
**Setup:** createTenant, create admin user, create 2 vendors, create expenses across 3 categories (use 2 different expense categories from the 9 seeded defaults + 1 custom category). Create expenses linked to different vendors. Ensure expenses are POSTED (so JEs exist).
|
|
|
|
**Test cases:**
|
|
1. Expense report by category — returns correct totals per category for date range
|
|
2. Expense report by category excludes DRAFT/VOIDED expenses (only POSTED counted)
|
|
3. Expense report by vendor — returns correct totals per vendor
|
|
4. Expense report by vendor includes "No Vendor" bucket for unlinked expenses
|
|
5. Expense summary — combines category and vendor views with grand total
|
|
6. Date range filtering works (expenses outside range excluded)
|
|
7. JE audit trail — fetch audit for a posted expense's JE: shows createdBy user, source=SYSTEM, referenceType="Expense", referenceId=expense.id
|
|
8. Audit trail for entity — fetch all JEs for an expense (original + void reversal if voided)
|
|
9. JE audit trail on a payment JE (from Phase 2) — verify referenceType="Payment" is correctly returned (proves ACCT-08 works across all JE sources, not just expenses)
|
|
|
|
Cleanup order: expenses -> vendors -> expenseCategories (custom only) -> journalEntryLines -> null reversesEntryId -> journalEntries -> invoiceLines -> invoices -> subscribers -> servicePlans -> accountingPeriods -> accounts -> ticketCategories -> expenseCategories (system) -> users -> tenant
|
|
</action>
|
|
<verify>npx jest expense-report-service --verbose passes all tests</verify>
|
|
<done>Expense reports correctly aggregate by category/vendor with date filtering. Audit trail shows creator, timestamp, and source reference for any JE. All tests pass.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
- `npx jest expense-report-service --verbose` — all tests pass
|
|
- Expense report by category shows correct totals
|
|
- Expense report by vendor shows correct totals
|
|
- JE audit trail includes who/when/source for all JE types (not just expenses)
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- Admin can generate expense reports by category and vendor for any date range
|
|
- Reports show totals and counts, excluding non-POSTED expenses
|
|
- Every journal entry (Invoice, Payment, Expense, Collection, Remittance, StockMovement) has traceable audit trail
|
|
- All tests pass
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/04-inventory-expenses-and-financial-reports/04-04-SUMMARY.md`
|
|
</output>
|