Phases & timeline
7 phases, ~7-8 months for 2-3 engineers. Each phase ends with a working, demoable slice. No dead ends, no half-built features.
| Phase | Scope | Effort | Exit criteria |
|---|---|---|---|
| 0 Foundation | Monorepo, API, web, DB, Redis, CI, hosting | shipped | API /v2/health green, Web renders, repo on GitHub |
| 1 Identity + Property | Auth, User/Profile/PropertyOwner, Property/Floor/Room, RLS | 6 wks | Owner logs in, manages own properties; admin sees all |
| 2 Contracts | Contract, ContractItem, ContractRoom, payment schedule, lifecycle | 6 wks | Owner creates/extends/terminates contracts; expiry SMS fires |
| 3 Billing | Unified Invoice, Money type, bill generation worker, basic invoice PDF | 8 wks | Monthly bill run creates invoices + sends them; PDF downloads |
| 4 Payments + recon + eBarimt | QPay, SocialPay, Khan/Golomt/TDB statement import, eBarimt outbox | 6 wks | Tenant pays via QPay; bank statement auto-reconciles; eBarimt receipt issued |
| 5 Chatbot + reports + files + cuts | Gemini, remaining reports, file uploads, community cut | 4 wks | Owner asks chatbot questions; statement PDF matches v1; tenant uploads ID |
| 6 Decommission v1 | Mobile API parity, switch all users, archive SQL Server | 4 wks | XAF host shut down; SQL Server snapshot archived; v2 is the only system |
What ships at the end of each phase (demo-able)
Phase 0 — Foundation (DONE)
- Turborepo monorepo with apps/api, apps/web, packages/{db,shared,tsconfig,eslint-config}
- Postgres 16 + Redis in
docker-compose.yml - Drizzle schema + migration + RLS scaffolding (User seed table)
- Hono API with
@hono/zod-openapi+ Scalar UI at/docs - Next.js 15 web fetches API health on root page
- Pushed to spacehub-mn/spacehub26
Phase 1 — Identity & Property (6 wks)
- better-auth setup — Drizzle adapter, email+password, JWT plugin, bearer plugin, organization plugin (org = PropertyOwner), phoneNumber plugin (131344 SMS OTP for tenant signup)
- RLS middleware — open transaction per request,
SET LOCALGUCs, policies declared on every domain table - Schema: User, Profile, PropertyOwner, Property, Floor, Room
- API:
/v2/auth/*,/v2/me,/v2/property-owners,/v2/properties,/v2/properties/{id}/floors,/v2/properties/{id}/rooms - Web: login, signup, profile, property list/detail/edit (admin)
- Tests: RLS smoke (owner A cannot see owner B's properties)
Exit: deploy preview lets a real PropertyOwner manage their building inventory.
Phase 2 — Contracts (6 wks)
- Schema: Contract, ContractItem, ContractRoom, ContractUtility — single status enum, payment schedule as
jsonbcolumn for fixed terms - Transition functions —
activate(),extend(),terminate()with allowed-transition table - API: CRUD + actions
POST /v2/contracts/{id}/extend,POST /v2/contracts/{id}/terminate - Web: contract list, detail, create wizard, extend modal, terminate confirm
- Worker:
contract-expiry-notificationBullMQ repeatable job (daily 10:00 Asia/Ulaanbaatar) → SMS via 131344 - Backfill from v1 — one-shot script reading SQL Server, writing Postgres
Phase 3 — Billing (8 wks) — biggest phase
- Unified Invoice model — collapses Bill / InvoiceBill / Sale / Discount.
kindenum,amountBigInt,paid_amountBigInt - BigInt Money helpers already in
@spacehub/shared - Bill generation worker — BullMQ repeatable (monthly on day-of-month per property), idempotent by
(period, contract_id) - InvoicePackage — batch print + email/SMS dispatch
- React-PDF templates for the 3-5 most-used reports (statement, invoice, contract)
- API: invoices CRUD + actions, package endpoints
- Web: invoice list, detail, period selector, batch generate UI
Phase 4 — Payments + bank recon + eBarimt (6 wks)
- QPay client (REST) + webhook handler with signature verification
- SocialPay client
- Khan Bank statement importer — port Excel parser, validate signature
- Golomt OpenBanking client
- TDB importer
- Statement reconciliation service — fuzzy match (date window + amount tolerance + memo fuzzy), index weak-refs in memory to kill O(N·M) leak
- eBarimt 3.0 outbox — Sale insert → outbox row in same tx → worker drains → POST to eBarimt → record receipt id
- Web: payment status on invoice, recon queue UI
Phase 5 — Chatbot + reports + files + cuts (4 wks)
- Gemini chatbot — Vercel AI SDK +
@ai-sdk/google, streaming SSE, persist conversation in Postgres, port 10 function-calling tools - Move chatbot PO whitelist to config flag
- Remaining React-PDF templates
- File storage — Cloudflare R2 or MinIO, presigned upload pattern,
sharpthumbnails for property photos - Cut decision — confirm with stakeholders: drop Post/community features or port minimal version
Phase 6 — Decommission (4 wks)
- Mobile API parity check — every endpoint
SpacehubApiV2serves has a/v2/*equivalent - Flutter app cuts over — release new mobile version pointed at v2 API
- Switch web traffic — reverse proxy routes everything to Next.js
- Stop CDC (if used)
- Snapshot + archive SQL Server — kept for 12 months as audit fallback
- Shut down XAF Blazor + Automation hosts
Risk buffers
- Report fidelity — accountants will compare PDFs pixel-perfect. Allocate 1 wk slack in Phase 3.
- Bank integration drift — Khan/Golomt/TDB APIs change quietly. Allocate 1 wk slack in Phase 4.
- eBarimt 3.0 compliance — quirks in receipt format. Stage against eBarimt sandbox before flipping.
- Mobile release coordination — Phase 6 depends on Flutter app store approval (~1 week lead).