Commit Graph

13 Commits

Author SHA1 Message Date
kevin-asprec
fba2ba4bf9 feat(03-05): technician service, compensation service, APIs, and 27 passing tests
- technician-service.ts: createTechnicianProfile, updateTechnicianProfile, getTechnicianProfile, getTechnicianProfileByUserId, listTechnicians
- compensation-service.ts: getCompensationSummary (all 3 models), getTechnicianCompensationDetail (job-by-job)
- 6 API routes: GET/POST /api/technicians, GET/PUT /api/technicians/[id], GET /api/technicians/[id]/compensation, GET/POST /api/job-type-rates, PUT /api/job-type-rates/[id], GET /api/reports/compensation
- Added TechnicianProfile and JobTypeRate to CASL AppSubjects; OFFICE_STAFF read access
- 27 integration tests: all 3 models, missing rate defaults to 0, only COMPLETED count, date range filter, summary+detail reports, cross-tenant isolation — all green
2026-03-05 08:17:03 +08:00
kevin-asprec
86284f1f5b feat(03-04): job order service, API routes, and 17 passing integration tests
- Added checkTicketAutoResolve/checkTicketRevertToOpen to ticket-service.ts
- Created job-order-service.ts: createJobOrder, updateJobOrderStatus, updateJobOrder,
  getJobOrder, listJobOrders, getMyJobOrders with VALID_JO_TRANSITIONS guard map
- PENDING->IN_PROGRESS->COMPLETED(outcomeNotes required)/CANCELLED lifecycle enforced
- OPEN ticket auto-transitions to ASSIGNED on first job order creation
- Auto-resolves ticket when all non-cancelled jobs COMPLETED
- Reverts ticket to OPEN when all jobs CANCELLED
- Created POST /api/tickets/[id]/job-orders, GET/PUT /api/job-orders/[id],
  POST /api/job-orders/[id]/status, GET /api/job-orders (with TECHNICIAN filter)
- 17 integration tests: lifecycle, auto-resolve, revert-to-open, partial completion,
  technician self-service, cross-tenant isolation — all green
2026-03-05 08:04:21 +08:00
kevin-asprec
a72aaa987d feat(03-02): Collector service, remittance service, report service, APIs, and tests
- collector-service.ts: recordCollection (FIFO, zone enforcement, DR 1030/CR 1100 JE),
  voidCollection (reversing JE), getCollectionHistory
- remittance-service.ts: createRemittance, verifyRemittance (DR 1010/CR 1030, variance
  non-blocking), listRemittances
- collection-report-service.ts: getDailyCollectionSummary, getCollectorCollectionDetail
- 6 API routes: POST/GET /collections, GET /collections/[id],
  POST /collections/[id]/void, POST/GET /remittances,
  POST /remittances/[id]/verify, GET /reports/collections
- 26 integration tests: 13 collector (FIFO, zone enforcement, JE verification, void,
  cross-tenant) + 13 remittance (variance, JE accounts, double-verify rejection)
- All 26 tests pass
2026-03-05 07:54:39 +08:00
kevin-asprec
74d26d92f0 feat(03-03): Ticket service, category service, API routes, and 28 integration tests
- ticket-category-service.ts: createCategory, updateCategory, listCategories
- ticket-service.ts: createTicket (TKT-NNNN numbering), updateTicket, getTicket, listTickets, transitionTicketStatus (guard map), resolveTicket (idempotent)
- 5 ticket API routes: GET/POST /api/tickets, GET/PUT /api/tickets/[id], POST /api/tickets/[id]/status
- 2 category API routes: GET/POST /api/ticket-categories, PUT /api/ticket-categories/[id]
- 28 integration tests: lifecycle, transitions, deactivated category rejection, idempotent resolve, cross-tenant isolation
2026-03-05 07:40:10 +08:00
kevin-asprec
214df6cd92 feat(03-01): Zone service, API routes, and 25 integration tests
- zone-service.ts: createZone, updateZone, listZones, getZone,
  assignSubscriberToZone, removeSubscriberFromZone, assignCollectorToZone,
  removeCollectorFromZone, getCollectorZones, getCollectorSubscribers
- API routes: GET/POST /api/zones, GET/PUT /api/zones/[id],
  POST/DELETE /api/zones/[id]/subscribers, GET /api/collectors/[id]/subscribers
- 25 integration tests: zone CRUD, subscriber assignment, collector scoping,
  no-zone-assignment security boundary, cross-tenant isolation
- Fix subscriber-service.ts: zone String? -> zoneId FK (deviation Rule 1)
- Fix subscriber API routes: zone -> zoneId (deviation Rule 1)
2026-03-05 07:30:34 +08:00
kevin-asprec
bbfc9d6fb4 feat(02-05): Payment APIs, outstanding report, and 29 passing tests
- Implement OutstandingReportService.getOutstandingReport() with filtering/pagination
- Add POST/GET /api/payments (record and list payments)
- Add GET /api/payments/[id] (payment detail)
- Add POST /api/payments/[id]/void (void payment)
- Add GET /api/subscribers/[id]/payments (payment history)
- Add GET /api/subscribers/[id]/balance (outstanding balance)
- Add GET /api/reports/outstanding (outstanding report)
- Write 29 comprehensive tests covering full/partial/overpayment,
  FIFO allocation, JE balance, bank vs cash, idempotency, void,
  outstanding report filters, reconciliation, history, tenant isolation
- All 265 tests pass (full regression clean)
2026-03-04 23:52:57 +08:00
kevin-asprec
902587683f feat(02-04): billing API routes and comprehensive tests (38 passing)
- Fix shouldBillToday() month-wrapping logic for PREPAID lead days
- POST /api/billing/generate — triggers monthly invoice generation cycle
- GET /api/invoices — paginated list with status/subscriber/date filters
- GET /api/invoices/[id] — invoice detail with lines and subscriber
- POST /api/invoices/[id]/void — void with JE reversal

Test coverage (38 tests):
- computeBillingPeriod pure function
- shouldBillToday: postpaid, prepaid, and month-wrapping edge case
- Invoice number sequencing per tenant/year
- Invoice generation: amounts, InvoiceLine, period dates
- Journal entries: DR AR (1100), CR Revenue (4010), balanced
- Idempotency: duplicate prevention via unique(subscriberId, periodStart)
- Credit auto-application: full, partial, zero credit, JE (DR 1150, CR 1100)
- Billing cycle: active-only, suspended/cancelled excluded, prepaid lead days
- Overdue detection: bulk update of DRAFT/SENT/PARTIAL past due date
- Void: JE reversal, already-voided guard, PAID guard
- getInvoice, listInvoices, status filtering
- Tenant isolation: Tenant B cannot see Tenant A invoices
2026-03-04 23:38:43 +08:00
kevin-asprec
30ec936b68 feat(02-02): journal entry API routes + comprehensive test suite
- GET/POST /api/accounting/journal-entries (list + create manual entry)
- GET /api/accounting/journal-entries/[id] (single entry with lines)
- POST /api/accounting/journal-entries/[id]/approve (maker-checker approval)
- POST /api/accounting/journal-entries/[id]/reverse (create reversing entry)
- GET /api/accounting/accounts/[id]/balance (derived balance, never stored)
- All routes use closure pattern over withPermission HOF (same as periods/close)
- Add startDate parameter to getAccountBalance for date range queries
- 36 integration tests: balance enforcement, closed period, immutability,
  reversals, maker-checker, entry numbering, account balance derivation,
  trial balance self-verification
- Total test suite: 198 tests all passing
2026-03-04 23:23:11 +08:00
kevin-asprec
03a4a29150 feat(02-03): Subscriber and ServicePlan API routes + tests
- GET/POST /api/service-plans — list with activeOnly filter, create with validation
- PUT /api/service-plans/[id] — partial update via closure pattern
- GET/POST /api/subscribers — list/search with status/plan/name filters, paginated; create returns 201
- GET/PUT /api/subscribers/[id] — get with servicePlan relation, profile update
- PATCH /api/subscribers/[id]/status — full status lifecycle transitions
- All dynamic routes use closure pattern (withPermission HOF + params closure)
- 41 tests covering ServicePlan CRUD, Subscriber CRUD, search/filter, status lifecycle, tenant isolation
- 162 total tests pass (41 new + 121 existing)
2026-03-04 23:02:42 +08:00
kevin-asprec
a53ee9cd1c feat(02-01): COA auto-provisioning on tenant signup + API routes + tests
- Create seed-coa.ts: seedChartOfAccounts(tx, tenantId) seeds 28 accounts in transaction
- Update tenant.ts: createTenant() calls seedChartOfAccounts inside $transaction block
- Add GET /api/accounting/accounts — list COA for tenant (requires read:Account)
- Add GET /api/accounting/periods — list accounting periods (requires read:Account)
- Add POST /api/accounting/periods/[id]/close — close period (requires manage:Account)
- Add 28 integration tests: COA definition, seeding, period management, createTenant integration
- All 121 tests pass (93 existing + 28 new)
2026-03-04 22:51:52 +08:00
kevin-asprec
df40eae328 feat(01-05): super-admin API routes and middleware guard
- withSuperAdmin() HOF: checks isSuperAdmin from session, returns 401/403
- GET /api/admin/tenants: lists all tenants with userCount, subscriberCount
- GET /api/admin/tenants/[id]: single tenant detail with users list
- POST /api/admin/tenants/[id]/suspend: suspend/activate with 7-day grace
- prisma/seed.ts: add Test ISP 2 tenant and admin2@demo.com for isolation tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:04:10 +08:00
kevin-asprec
43761d94ee feat(01-03): tenant signup API, service, and UI
- Add businessAddress and contactPhone fields to Tenant schema
- Create src/lib/tenant.ts with createTenant() function:
  - Validates input, slugifies business name, hashes password (bcrypt 12)
  - Prisma transaction creates Tenant + admin User atomically
  - Custom EmailAlreadyExistsError for 409 Conflict responses
- Create POST /api/tenants/signup route returning 201/400/409/500
- Create /signup page with full form (business name, owner info, password, optional fields)
  - Client-side validation: required fields, email format, password match
  - Redirects to /login?registered=true on success
- Update /login page to show success banner when ?registered=true
2026-03-04 18:40:40 +08:00
kevin-asprec
71a9277913 feat(01-02): configure NextAuth.js v4 with credentials provider and JWT
- Install next-auth@4, bcryptjs, @types/bcryptjs, @types/jest
- src/types/next-auth.d.ts: extend Session/JWT with tenantId, roles, isSuperAdmin
- src/lib/auth-options.ts: CredentialsProvider + JWT/session callbacks, 24h maxAge
- src/lib/auth.ts: getServerSession() and getCurrentUser() server helpers
- src/app/api/auth/[...nextauth]/route.ts: NextAuth GET/POST handler
- src/middleware.ts: withAuth middleware protecting all routes except /login /signup /api/auth/*
2026-03-04 18:37:29 +08:00