Phase 03: Operational Modules - 5 plans in 3 waves - Wave 1: 03-01 (zones), 03-03 (tickets) — parallel - Wave 2: 03-02 (collector collections), 03-04 (job orders) — parallel - Wave 3: 03-05 (technician compensation) — sequential - Ready for execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
14 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 03-operational-modules | 02 | execute | 2 |
|
|
true |
|
Purpose: This is the core collector workflow — collectors log cash received in the field, the system tracks what they've collected, office staff verify remittances with independent counts, and the accounting ledger records verified cash movements. The daily summary report gives management visibility into collection operations.
Output: Collection model (lump-sum field payment with FIFO allocation), Remittance model (two-party verification), journal entry on verified remittance, daily collection summary report, integration tests.
<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>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/03-operational-modules/03-CONTEXT.md @.planning/phases/03-operational-modules/03-01-SUMMARY.md @prisma/schema.prisma @src/lib/services/payment-service.ts @src/lib/accounting/journal-entry-service.ts @src/lib/accounting/chart-of-accounts.ts Task 1: Collection and Remittance Prisma models + new COA account prisma/schema.prisma, src/lib/prisma-tenant.ts, src/lib/accounting/chart-of-accounts.ts, src/lib/accounting/seed-coa.ts **New enum:** - `RemittanceStatus { PENDING, VERIFIED }` - `CollectionStatus { COMPLETED, VOIDED }` (mirroring PaymentStatus pattern)Collection model — a field cash collection logged by a collector:
- id (uuid PK), tenantId
- collectorId (FK to User — the collector who collected)
- subscriberId (FK to Subscriber)
- amount (Decimal 10,2) — lump sum received from subscriber
- collectionDate (DateTime) — when cash was received in the field
- notes (String?)
- status (CollectionStatus default COMPLETED)
- remittanceId (String? FK to Remittance — linked when included in a remittance batch)
- paymentId (String? FK to Payment — the underlying Payment record created via PaymentService)
- createdAt, updatedAt
- @@index([tenantId]), @@index([tenantId, collectorId]), @@index([tenantId, collectionDate])
Remittance model — a batch handoff of collected cash from collector to office:
- id (uuid PK), tenantId
- collectorId (FK to User)
- remittanceDate (DateTime) — when the collector handed over cash
- collectedTotal (Decimal 10,2) — sum of Collection amounts in this batch (system-calculated)
- verifiedTotal (Decimal 10,2?) — amount counted by office staff (null until verified)
- variance (Decimal 10,2?) — collectedTotal - verifiedTotal (system-calculated on verification)
- status (RemittanceStatus default PENDING)
- verifiedById (String? FK to User — office staff who verified)
- verifiedAt (DateTime?)
- journalEntryId (String?) — JE created on verification
- notes (String?)
- createdAt, updatedAt
- @@index([tenantId]), @@index([tenantId, collectorId]), @@index([tenantId, remittanceDate])
Update relations:
- User: add
collections Collection[],remittancesAsCollector Remittance[] @relation("RemittanceCollector"),remittancesVerified Remittance[] @relation("RemittanceVerifiedBy") - Subscriber: add
collections Collection[]
New COA account:
- Add account 1030 "Cash in Transit" (ASSET, DEBIT normal balance) to ISP_CHART_OF_ACCOUNTS in chart-of-accounts.ts
- This is a child of 1000 (Cash and Cash Equivalents)
- Update seed-coa.ts if needed to include it
- Purpose: When collector collects cash, it's in transit until verified remittance moves it to Cash on Hand (1010)
Add to TENANT_SCOPED_MODELS: "collection", "remittance"
Run npx prisma migrate dev --name add-collections-remittances
Journal Entry Pattern for Collection: When collector logs a collection, it creates a Payment via PaymentService (reusing FIFO allocation) AND creates a Collection record linking the collector. The Payment JE is: DR 1030 Cash in Transit, CR 1100 AR. Note: use 1030 (not 1010) because cash is with the collector, not yet in the office.
Journal Entry Pattern for Verified Remittance: DR 1010 Cash on Hand (verified amount) CR 1030 Cash in Transit (verified amount) This moves the cash from "in transit" to "on hand" upon office verification.
IMPORTANT: The collector collection payment must debit 1030 Cash in Transit (not 1010 Cash on Hand). This means CollectorService needs to create the Payment with a custom account override, or create its own JE pattern. The cleanest approach: CollectorService creates the Payment record directly (reusing the FIFO allocation logic from PaymentService but with 1030 as the debit account instead of 1010/1020). Extract the FIFO allocation logic into a shared helper if needed, or have CollectorService call PaymentService.recordPayment with a parameter indicating collector collection (which uses 1030 instead of 1010).
- npx prisma migrate dev completes without errors
- npx prisma generate succeeds
- Schema has Collection, Remittance models with correct relations
- chart-of-accounts.ts includes 1030 Cash in Transit
Collection and Remittance models exist, 1030 Cash in Transit added to COA, TENANT_SCOPED_MODELS updated, migration applied.
RemittanceService (src/lib/services/remittance-service.ts):
createRemittance(db, { collectorId, collectionIds, remittanceDate, notes }):- Validate all collectionIds belong to this collector and are unremitted
- Calculate collectedTotal as sum of collection amounts
- Create Remittance record with status PENDING
- Link collections to remittance (update collection.remittanceId)
- Return remittance
verifyRemittance(db, { remittanceId, verifiedById, verifiedTotal, notes }):- Load remittance, verify status is PENDING
- Calculate variance = collectedTotal - verifiedTotal
- Create journal entry via JournalEntryService: DR 1010 Cash on Hand (verifiedTotal), CR 1030 Cash in Transit (verifiedTotal). Reference type "Remittance".
- Update remittance: verifiedTotal, variance, verifiedById, verifiedAt, journalEntryId, status = VERIFIED
- Variance is recorded but does NOT block — remittance completes regardless
- Return remittance with variance info
getRemittances(db, { collectorId?, dateFrom?, dateTo?, status? })— list remittances with filters
CollectionReportService (src/lib/services/collection-report-service.ts):
getDailyCollectionSummary(db, { date, collectorId? }):- Query collections for the date (or all collectors if no collectorId)
- Query remittances for the date
- Return per-collector summary: { collectorId, collectorName, totalCollected, totalRemitted, variance, collectionCount }
- Include drill-down data: per-subscriber detail (subscriberName, amount, collectionDate)
API Routes:
POST /api/collections— collector logs a collection. COLLECTOR role. Body: { subscriberId, amount, collectionDate, notes? }. Extracts collectorId from session.GET /api/collections— list collections. COLLECTOR sees own; ADMIN/OFFICE_STAFF see all or filter by collectorId query param.GET /api/collections/[id]— get collection detail with payment allocation infoPOST /api/collections/[id]/void— void a collection. ADMIN, OFFICE_STAFF.POST /api/remittances— create remittance batch. COLLECTOR role. Body: { collectionIds, remittanceDate, notes? }GET /api/remittances— list remittances with filters. ADMIN, OFFICE_STAFF, COLLECTOR (own only).POST /api/remittances/[id]/verify— verify remittance. ADMIN, OFFICE_STAFF only. Body: { verifiedTotal, notes? }GET /api/reports/collections— daily collection summary. ADMIN, OFFICE_STAFF. Query params: date, collectorId?
Integration Tests:
collector-service.test.ts:
- Collector can log collection against subscriber in their zone
- Collector cannot collect from subscriber outside their zone
- Collection creates Payment with FIFO allocation (reuses PaymentService pattern)
- Collection uses 1030 Cash in Transit (not 1010)
- Void collection voids underlying payment
- getUnremittedCollections returns only collections not linked to remittance
- Collector balances derived from transactions (no stored balance field) — query collections sum vs remittances sum
remittance-service.test.ts:
- Create remittance batch from unremitted collections
- Cannot include already-remitted collections
- Verify remittance with matching total (variance = 0)
- Verify remittance with different total (variance recorded, not blocking)
- Verification creates JE: DR 1010, CR 1030
- Cannot verify already-verified remittance
- Daily collection summary returns correct totals per collector
npx vitest run src/lib/__tests__/collector-service.test.ts— all tests passnpx vitest run src/lib/__tests__/remittance-service.test.ts— all tests passnpx vitest run— full suite passes (no regressions) Collectors can log field collections with FIFO allocation via 1030 Cash in Transit, create remittance batches, office staff verify with independent count, variance tracked, JE posted on verification, daily collection summary report working. All derived from transaction log — no stored balance fields.
<success_criteria>
- Collection and Remittance models with proper relations and migration
- 1030 Cash in Transit account added to COA
- CollectorService handles field collection with FIFO and zone scoping
- RemittanceService handles batch creation and two-party verification with JE
- CollectionReportService produces daily summary per collector
- Integration tests prove the full collection-to-remittance-to-JE flow
- No stored balance fields — all collector totals derived from transaction log </success_criteria>