Commit Graph

17 Commits

Author SHA1 Message Date
kevin-asprec
a742f701e7 feat(04-01): InventoryService, API routes, migration, and 13 passing tests
- InventoryService: registerItem, recordMovement, getStockLevels, getItemMovements, listItems
- RECEIVED movements auto-post JE (DR 1200 Equipment Inventory, CR 2010 AP)
- Stock levels derived from movement aggregation (no mutable quantity column)
- All 5 movement types validated with type-specific rules
- API routes: POST/GET items, GET item detail, POST/GET movements, GET stock-levels
- CASL: OFFICE_STAFF gets manage Inventory permission
- Migration applied: add_inventory_models (4 enums, 2 tables)
- 13 tests: registration, all movement types, stock derivation, JE posting, history

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 10:31:44 +08:00
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
25a12effb0 feat(01-05): super-admin UI panel and comprehensive test harness
- (super-admin)/layout.tsx: server guard (isSuperAdmin check), sidebar nav
- (super-admin)/admin/page.tsx: dashboard with tenant stats (total/active/suspended)
- (super-admin)/admin/tenants/page.tsx: tenant table with status badges, suspend/activate
- src/middleware.ts: /admin/* routes require isSuperAdmin in JWT token
- src/lib/__tests__/super-admin.test.ts: 11 tests covering middleware guard + suspension logic
- All 93 tests pass (auth 8, RBAC 66, isolation 6, super-admin 11, setup 2)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:06:53 +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
3c37cb1866 feat(01-02): login page UI, logout flow, seed script, and auth unit tests
- src/app/(auth)/layout.tsx: centered auth layout for login page
- src/app/(auth)/login/page.tsx: login form with error/loading states, sign up link
- src/components/providers.tsx: SessionProvider wrapper for client-side session
- src/components/layout/header.tsx: authenticated header with Sign out button
- src/app/(dashboard)/layout.tsx: dashboard layout wrapping Header component
- src/app/(dashboard)/dashboard/page.tsx: basic dashboard page post-login
- src/app/layout.tsx: wrap root with SessionProvider via Providers component
- prisma/seed.ts: idempotent seed for Demo ISP tenant + admin + super-admin users
- package.json: add db:seed script and prisma.seed config, add tsx devDep
- src/lib/__tests__/auth.test.ts: 8 unit tests for authOptions callbacks
2026-03-04 18:42:53 +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
kevin-asprec
90bc5836fd feat(01-01): Next.js 15 project with Docker Compose dev environment
- Next.js 15 with App Router, TypeScript, Tailwind CSS (v4), ESLint
- docker-compose.yml with PostgreSQL 16, Redis 7, and app service
- Dockerfile (Node 20 alpine) for containerized development
- .env and .env.example with DATABASE_URL, DATABASE_URL_LOCAL, REDIS_URL
- src/app/page.tsx updated to render NetForge heading
- All three services start healthy with docker compose up -d
2026-03-04 18:24:59 +08:00