Commit Graph

41 Commits

Author SHA1 Message Date
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
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
df4a467a38 docs(02-04): complete billing engine plan
Tasks completed: 2/2
- Task 1: Invoice model + BillingService + InvoiceService + CreditService
- Task 2: Billing API routes + comprehensive tests (38 passing)

SUMMARY: .planning/phases/02-subscriber-and-billing-core/02-04-SUMMARY.md
2026-03-04 23:41:50 +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
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
c60c22b080 docs(02-02): complete JournalEntryService plan
Tasks completed: 2/2
- Task 1: JournalEntry models + JournalEntryService
- Task 2: Journal entry API routes + comprehensive tests

SUMMARY: .planning/phases/02-subscriber-and-billing-core/02-02-SUMMARY.md
2026-03-04 23:25:42 +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
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
5c6969343b docs(02-03): complete Subscriber and ServicePlan management plan
Tasks completed: 2/2
- Task 1: Subscriber and ServicePlan Prisma models + service layer
- Task 2: Subscriber and ServicePlan API routes + tests

SUMMARY: .planning/phases/02-subscriber-and-billing-core/02-03-SUMMARY.md
2026-03-04 23:04:15 +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
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
e49db94f38 docs(02-01): complete Chart of Accounts plan
Tasks completed: 2/2
- Task 1: Account and AccountingPeriod Prisma models + COA definition
- Task 2: COA auto-provisioning on tenant signup + API routes + tests

SUMMARY: .planning/phases/02-subscriber-and-billing-core/02-01-SUMMARY.md
2026-03-04 22:53:38 +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
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
bd8e83bf68 fix(02): revise plans based on checker feedback
- 02-05: Update outstanding report key_link and action to document
  Invoice.amountPaid as transactional convenience field (not standalone
  stored balance), consistent with creditBalance pattern in 02-03
- 02-04: Add applyCredit wiring to generateInvoiceForSubscriber, add
  credit-service.ts extraction to avoid circular imports, add credit
  auto-application tests
- 02-01/02-03: Add parallel Prisma migration serialization notes for
  Wave 1 concurrent execution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 22:35:59 +08:00
kevin-asprec
9489b5c7bc docs(02): create phase plan
Phase 02: Subscriber and Billing Core
- 5 plans in 4 waves
- Wave 1: 02-01 (COA), 02-03 (Subscribers) parallel
- Wave 2: 02-02 (Journal Entry Service)
- Wave 3: 02-04 (Billing Engine)
- Wave 4: 02-05 (Payment Tracker)
- Ready for execution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 22:27:35 +08:00
kevin-asprec
e14b926da1 docs(02): capture phase context
Phase 02: Subscriber and Billing Core
- Implementation decisions documented
- Phase boundary established

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 22:06:07 +08:00
kevin-asprec
0cb9a483bf docs(01): complete Foundation phase
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:22:00 +08:00
kevin-asprec
9d8c45ce10 docs(01-05): complete super-admin panel plan
Tasks completed: 2/2
- Task 1: Super-admin API routes and middleware guard
- Task 2: Super-admin UI panel and comprehensive test harness

Phase 1 Foundation: COMPLETE (5/5 plans)
93 tests passing across auth, RBAC, tenant isolation, super-admin

SUMMARY: .planning/phases/01-foundation/01-05-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:08:38 +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
d25885aaeb docs(01-04): complete CASL RBAC plan
Tasks completed: 2/2
- Task 1: CASL permission definitions and ability factory
- Task 2: API authorization middleware and RBAC tests

SUMMARY: .planning/phases/01-foundation/01-04-SUMMARY.md
2026-03-04 18:59:14 +08:00
kevin-asprec
1df2b2d87d feat(01-04): API authorization middleware and RBAC tests (66 passing)
- Create src/lib/middleware/authorize.ts with withPermission() HOF
- Returns 401 for unauthenticated, 403 for unauthorized access
- Passes ability + user to authorized handlers for fine-grained checks
- Add authorize() convenience alias for handler-first usage pattern
- Create src/lib/__tests__/rbac.test.ts with 66 unit tests covering:
  - Admin full access to all subjects
  - Office Staff: can manage billing, blocked from Chart of Accounts
  - Collector: can record payments, blocked from invoice management
  - Technician: blocked from billing/payments (critical security boundary)
  - Client: scoped to own data only
  - Multi-role additive union (TECHNICIAN+COLLECTOR gets both sets)
  - Super-admin bypasses all permission checks
- Fix CASL MongoAbility type: use createMongoAbility throughout
- Fix condition casting for string-based subjects (no Prisma models yet)
- Fix ability merging: cannot() rules excluded for multi-role union
2026-03-04 18:57:31 +08:00
kevin-asprec
67bb6cc95c feat(01-04): CASL permission definitions and ability factory
- Install @casl/ability for role-based access control
- Create src/lib/casl/types.ts with AppAbility, AppSubjects, AppActions types
- Create src/lib/casl/permissions.ts with permission matrix for all 5 roles
- Create src/lib/casl/ability.ts with defineAbilityFor() factory function
- Support multi-role users via additive union of permissions
- Super-admin bypasses all permission checks via can("manage", "all")
2026-03-04 18:52:39 +08:00
kevin-asprec
36729343cc fix(01): use localhost DATABASE_URL for vitest host-side test execution
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 18:48:43 +08:00
kevin-asprec
dc85d348ef docs(01-03): complete tenant provisioning and isolation plan
Tasks completed: 2/2
- Task 1: Tenant signup API, tenant service, and signup UI
- Task 2: Prisma tenant middleware, PostgreSQL RLS, and isolation tests

SUMMARY: .planning/phases/01-foundation/01-03-SUMMARY.md
2026-03-04 18:47:01 +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
kevin-asprec
cf790c3257 docs(01-02): complete authentication plan
Tasks completed: 2/2
- Task 1: NextAuth.js configuration with credentials provider and JWT
- Task 2: Login page UI, logout flow, seed script, and auth unit tests

SUMMARY: .planning/phases/01-foundation/01-02-SUMMARY.md
2026-03-04 18:44:29 +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
19a5520dd5 docs(01-01): complete project scaffold and dev environment plan
Tasks completed: 2/2
- Task 1: Next.js 15 project with Docker Compose dev environment
- Task 2: Prisma schema with Tenant/User models and Vitest setup

SUMMARY: .planning/phases/01-foundation/01-01-SUMMARY.md
2026-03-04 18:32:40 +08:00
kevin-asprec
1adeab2fbc feat(01-01): Prisma schema with Tenant/User models and Vitest test setup
- prisma/schema.prisma with Tenant, User, Role, TenantStatus models
  - tenantId on all tenant-scoped models (RLS-ready convention)
  - @@unique([email, tenantId]) and @@index([tenantId]) on User
  - Grace period fields on Tenant (suspendedAt, gracePeriodEndsAt)
  - RLS comment block documenting tenantId convention for future models
- src/lib/prisma.ts singleton PrismaClient pattern (hot-reload safe)
- vitest.config.ts with node environment and @/* path alias
- src/lib/__tests__/setup.test.ts smoke test (2 tests passing)
- package.json scripts: test, test:watch, db:push, db:generate, db:studio
- Schema synced to PostgreSQL 16 via prisma db push
2026-03-04 18:31:04 +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
kevin-asprec
7e6d286fca docs(01): create phase plan
Phase 01: Foundation
- 5 plan(s) in 4 wave(s)
- 2 parallel (wave 2: auth + tenant provisioning), 3 sequential
- Ready for execution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 18:13:27 +08:00
kevin-asprec
2506a6745c docs(01): capture phase context
Phase 01: Foundation
- Implementation decisions documented
- Phase boundary established

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 18:05:03 +08:00
kevin-asprec
1bdb4eafae docs: create roadmap (5 phases) and research
Phases:
1. Foundation: tenant isolation, auth, Docker, RBAC
2. Subscriber & Billing Core: subscribers, invoicing, payments, accounting ledger
3. Operational Modules: collectors, ticketing, job orders, technicians
4. Inventory, Expenses & Reports: event-ledger inventory, expenses, financial statements
5. Visibility & Client Portal: dashboard, client self-service, integration & e2e tests

All 66 v1 requirements mapped to phases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:45:09 +08:00
kevin-asprec
21541fd8d5 docs: define v1 requirements
53 requirements across 12 categories
11 requirements deferred to v2
Full double-entry accounting with audit trail and derived balances

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:40:14 +08:00
kevin-asprec
c407e182f8 docs: complete project research for NetForge ISP Management SaaS
Files:
- STACK.md
- FEATURES.md
- ARCHITECTURE.md
- PITFALLS.md
- SUMMARY.md

Key findings:
- Stack: Next.js 15 + TypeScript + PostgreSQL + Prisma monolith; BullMQ for billing cron jobs; MikroTik client needs library verification (LOW confidence)
- Architecture: Modular monolith with shared-DB multi-tenancy; JournalEntryService as sole ledger gateway; derived financial state (no balance fields)
- Critical pitfall: Cross-tenant data leakage + fake accounting + billing state machine must all be solved in Phase 1 before any feature work

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:21:44 +08:00
kevin-asprec
ec2e583787 chore: add project config
Mode: yolo
Depth: standard
Parallelization: disabled
Workflow agents: research=on, plan_check=on, verifier=on

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:14:25 +08:00
kevin-asprec
54fb4915f0 docs: initialize project
Multi-tenant SaaS platform for ISP operations management — billing, payments, inventory, technician job orders, and full accounting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:11:11 +08:00