Commit Graph

12 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
230b0ec683 feat(03-05): TechnicianProfile/JobTypeRate schema, migration, tenant scoping
- Added CompensationModel enum (PER_JOB, SALARY, HYBRID)
- Added TechnicianProfile model with userId FK, skills[], zoneId?, compensationModel, monthlySalary?, isActive, @@unique([tenantId,userId])
- Added JobTypeRate model with jobType, rate, isActive, @@unique([tenantId,jobType])
- Added reverse relations: User.technicianProfiles[], Zone.technicianProfiles[]
- Applied migration: 20260305000851_add_technician_profiles
- Added technicianProfile and jobTypeRate to TENANT_SCOPED_MODELS with full 12-operation extension blocks
2026-03-05 08:10:46 +08:00
kevin-asprec
d59f1d55ea feat(03-04): JobOrder schema, migration, and tenant scoping
- Added JobOrderStatus enum (PENDING, IN_PROGRESS, COMPLETED, CANCELLED)
- Added JobOrder model with ticket FK, assignedTo/createdBy user FKs
- Added orderNumber (JO-NNNN), scheduledDate, timestamps, outcomeNotes
- Added jobOrders reverse relation on Ticket model
- Added assignedJobOrders/createdJobOrders reverse relations on User model
- Applied migration: 20260304235859_add_job_orders
- Added jobOrder to TENANT_SCOPED_MODELS with full 12-operation extension block
2026-03-05 07:59:56 +08:00
kevin-asprec
0967fc23fd feat(03-02): Collection/Remittance schema, 1030 COA account, migration, tenant scoping
- Added 1030 Cash in Transit to ISP_CHART_OF_ACCOUNTS (between 1020 and 1100)
- Added CollectionStatus (COMPLETED/VOIDED) and RemittanceStatus (PENDING/VERIFIED) enums
- Added Collection model with FIFO allocations, zone-scoped collector, JE link, void fields
- Added CollectionAllocation model linking collections to invoices
- Added Remittance model with two-party verification, variance field, JE link
- Added reverse relations on User (collections, remittances) and Subscriber (collections)
- Applied migration: add-collections-remittances
- Extended TENANT_SCOPED_MODELS with collection, collectionAllocation, remittance
- Added full 12-operation extension blocks for all three new models
2026-03-05 07:46:21 +08:00
kevin-asprec
b0562a0a12 feat(03-03): Ticket schema, categories, migration, and tenant scoping
- Added TicketStatus, TicketPriority, TicketSource enums to schema
- Added TicketCategory model (tenant-scoped, unique per tenant by name)
- Added Ticket model with lifecycle fields (status, priority, source, resolvedAt, closedAt)
- Added reverse relations: Subscriber.tickets, User.createdTickets, TicketCategory.tickets
- Applied migration 20260304233528_add_tickets
- Added ticket and ticketCategory to TENANT_SCOPED_MODELS with full extension blocks
- Seeded 6 default ISP ticket categories in createTenant transaction
2026-03-05 07:36:45 +08:00
kevin-asprec
56f5d071c4 feat(03-01): Zone schema, migration, and tenant scoping
- Add Zone model (id, tenantId, name, description, isActive)
- Add ZoneAssignment model (collector-to-zone join table)
- Replace Subscriber.zone String? with Subscriber.zoneId FK to Zone
- Add Zone + ZoneAssignment to TENANT_SCOPED_MODELS with full operation blocks
- Add "Zone" to AppSubjects in types.ts
- Grant OFFICE_STAFF manage Zone, COLLECTOR read Zone in permissions.ts
- Migration 20260305000000_add_zones applied to DB
2026-03-05 07:24:58 +08:00
kevin-asprec
6b91e67bdc feat(02-05): Payment model with FIFO allocation and void
- Add PaymentMethod (CASH, BANK_TRANSFER) and PaymentStatus (COMPLETED, VOIDED) enums
- Add Payment model with idempotency key, journal entry link, void fields
- Add PaymentAllocation model for FIFO invoice allocation tracking
- Add Payment/PaymentAllocation relations to Subscriber, Invoice, User
- Update TENANT_SCOPED_MODELS with "payment" and "paymentAllocation"
- Add payment/paymentAllocation query extensions in withTenantContext()
- Implement recordPayment() with FIFO allocation, overpayment credit balance
- Implement voidPayment() with reversing journal entries
- Implement getSubscriberPaymentHistory() with pagination
- Run migration: 20260304154606_add_payment_model
2026-03-04 23:47:58 +08:00
kevin-asprec
7cb7a9099c feat(02-04): Invoice model, BillingService, InvoiceService, CreditService
- Add InvoiceStatus enum + Invoice + InvoiceLine models to schema
- Add Invoice relation to Subscriber model
- Add invoice/invoiceLine to TENANT_SCOPED_MODELS in prisma-tenant.ts
- Add invoice/invoiceLine tenant-scoped query extensions to withTenantContext()
- Run migration: 20260304152900_add_invoice_model
- Create invoice-service.ts: generateInvoiceNumber, getInvoice, listInvoices,
  updateInvoiceStatus, markOverdueInvoices, voidInvoice (with JE reversal)
- Create credit-service.ts: applyCredit() — DR Subscriber Credits (1150), CR AR (1100)
- Create billing-service.ts: generateInvoiceForSubscriber (idempotent, JE + auto-credit),
  generateMonthlyInvoices (postpaid + prepaid timing with lead days)
2026-03-04 23:32:14 +08:00
kevin-asprec
837b7f1979 feat(02-02): JournalEntry models + JournalEntryService
- Add JournalEntryStatus and JournalEntrySource enums to Prisma schema
- Add JournalEntry model with maker-checker fields, self-referential reversal relation, and audit timestamps
- Add JournalEntryLine model with debit/credit Decimal(15,2) fields
- Update Account model with journalEntryLines back-relation
- Update User model with createdJournalEntries and approvedJournalEntries back-relations
- Run migration: 20260304150817_add_journal_entry_models
- Add journalEntry and journalEntryLine to TENANT_SCOPED_MODELS in prisma-tenant.ts
- Create JournalEntryService with createEntry, approveEntry, reverseEntry, getAccountBalance, getTrialBalance
- Enforce debit=credit balance using integer cents comparison (avoids float issues)
- SYSTEM source entries auto-posted; MANUAL entries start as DRAFT for maker-checker
2026-03-04 23:12:48 +08:00
kevin-asprec
9cc6af14e9 feat(02-03): Subscriber and ServicePlan Prisma models + service layer
- Add SubscriberStatus (ACTIVE/SUSPENDED/CANCELLED) and BillingType (PREPAID/POSTPAID) enums
- Add ServicePlan model with name, speed, monthlyPrice, billingType, soft-delete
- Add Subscriber model with accountNumber, billingDay, status lifecycle, creditBalance
- Add TenantSettings model with autoSuspendDays and prepaidLeadDays
- Migrate: 20260304145633_add_subscriber_models
- Extend prisma-tenant.ts with subscriber, servicePlan, tenantSettings query scoping
- Create service-plan-service.ts: createServicePlan, updateServicePlan, listServicePlans, deactivateServicePlan
- Create subscriber-service.ts: createSubscriber, updateSubscriber, changeSubscriberStatus, searchSubscribers, getSubscriber, generateAccountNumber
2026-03-04 22:58:53 +08:00
kevin-asprec
7c0caf5244 feat(02-01): Account and AccountingPeriod Prisma models + COA definition
- Add AccountType, NormalBalance, PeriodStatus enums to schema
- Add Account model with tenant scoping, code/name/type/normalBalance/parentId
- Add AccountingPeriod model with year/month/status/closedAt/closedById
- Create ISP_CHART_OF_ACCOUNTS with 28 accounts across all 5 types (1000-5000 ranges)
- Create accounting-period.ts with getOpenPeriod, closePeriod, isDateInClosedPeriod
- Extend TENANT_SCOPED_MODELS with account and accountingPeriod
- Add full query extension blocks for account and accountingPeriod in withTenantContext
- Run migration: 20260304144656_add_accounting_models
2026-03-04 22:47:45 +08:00
kevin-asprec
69eac9ffaa feat(01-03): Prisma tenant middleware, PostgreSQL RLS, and isolation tests
- Create src/lib/prisma-tenant.ts:
  - withTenantContext(tenantId) / createTenantPrisma — Prisma $extends client
  - Intercepts findMany, findFirst, findUnique, create, createMany, update,
    updateMany, delete, deleteMany, upsert, count, aggregate, groupBy on User
  - Auto-injects tenantId filter on all reads, writes, and deletes
  - setTenantRLS() helper for explicit RLS enforcement in transactions
  - TENANT_SCOPED_MODELS constant for future extensibility
- Create prisma/migrations/20260304104214_initial_schema — baseline migration
  capturing schema created by initial db push
- Create prisma/migrations/20260304104245_add_rls_policies:
  - ALTER TABLE User ENABLE ROW LEVEL SECURITY
  - CREATE POLICY tenant_isolation_user USING app.current_tenant_id session var
  - Defense-in-depth architecture comments explaining primary vs secondary enforcement
- Create src/lib/__tests__/tenant-isolation.test.ts (6 tests, all passing):
  - Test 1: Tenant A context returns only Tenant A's users (zero from B)
  - Test 2: Tenant B context returns only Tenant B's users (zero from A)
  - Test 3: create() auto-sets tenantId, invisible to other tenant
  - Test 4: findUnique by Tenant B's ID under Tenant A context returns null
  - Additional: findFirst cross-tenant blocked, count() is tenant-scoped
2026-03-04 18:45:13 +08:00