Phase 2 — Plan
What we build after the Phase 1 MVP reaches UAT. Phase 2 widens scale to household-business revenue 1–3B VND (Personal Income Tax on % of revenue, accounting book S2a) and adds multi-channel F&B, general retail, and grocery. Target window: Jun → Sep 2026.
Read this first — the board is behind the code
A 2026-05-31 code audit found the dev team has already built much of what the PO feature-sheet marks "Phase 2 / not confirmed" (KDS, BOM/recipe, lot+expiry, barcode infra, COGS posting, X/Z report schema, single-user shift, split/merge). So most of Phase 2 is finish + wire-up + activate, not build from scratch. This plan tags every item:
- Done — already in code & working (verify, don't rebuild)
- Finish — schema/service exists, needs state-machine / UI / wiring
- Build-new — genuinely absent, must be built
Before sprint planning, reconcile the PO sheet against code so we don't pay twice for built features.
The three reasons Phase 2 exists
Everything else is finishing work. These are the genuinely-new capabilities that define Phase 2 — none can be cut:
- E1 — Tax for the 1–3B band (S2a book + form 01/CNKD + T-VAN submit). The S2a fetcher today is a stub returning fake data; this is the headline net-new work.
- E2 — Business Type: retail + grocery. Code defaults to and hardcodes F&B in ~40–60% of commerce; the new industries need real branches.
- E4 — business customers with a tax code. Customers can't store a tax code (MST) today, so we can't issue VAT invoices to companies — a hard requirement that gates B2B.
Epic dependency map
Decisions locked (2026-05-31)
- Shifts must be multi-employee → Phase 2 lands the
feat/shift-variant-2refactor (multi-employeeshift/shift-assignment/shift-device/shift-report), not the current single-userPosSession. - QR self-order · Offline mode · Loyalty/voucher · Webshop → Phase 3. They are genuinely absent (build-from-scratch) and not required for the 1–3B + retail/grocery goal.
Priority tiers
| Tier | Meaning | Epics |
|---|---|---|
| P2-core | The reason Phase 2 exists; cannot be cut | E1 Tax · E2 Business Type · E4 CRM |
| P2-finish | High value, mostly wiring already-built code | E3 POS · E5 Reports · E6 Cash |
| P2-important | Real value, small once E4 lands | E7 Pricing |
E1 · Tax for the 1–3B band Build-new · P2-core
Goal: Serve household businesses taxed on % of revenue — book S2a + tax declaration form 01/CNKD, submittable to the tax authority. This is the single biggest net-new chunk of Phase 2.
| Item | Tag | Evidence / what to do |
|---|---|---|
| S2a PIT calculator | Build-new | s2x-hkd.data-fetcher.service.ts is a stub with fake data — replace with real fetcher: revenue × industry tax-rate |
| Tax-rate % by industry | Build-new | Generic pricing/tax entity exists, but no merchant-industry → rate mapping; add schema + seed VN rates |
| Form 01/CNKD | Build-new | Absent — design form schema + XML builder (TaxDeclarationLevel TIRE_0–3 exists as a hook) |
| T-VAN submission | Finish | t-van today is query-only (queryInvoiceMessages, queryTaxInformation); add submit* POST + audit |
| Reuse: book engine | Done | LedgerDataFetcherService is book-type parameterized; S1a works, S2a template (s2a-hkd.typ) already exists |
| Reuse: e-invoice POS | Done | POS_VAT/POS_SALE + REAL_TIME mode + BullMQ issuance production-ready |
Depends on: E4 (business-customer tax code needed for company VAT invoices). Feeds: E5 (tax/compliance reports).
E2 · Business Type — retail + grocery Build-new · P2-core
Goal: Make the platform genuinely multi-industry. The enum exists but the behaviour is F&B-shaped.
| Item | Tag | Evidence / what to do |
|---|---|---|
| Industry enum | Done | MerchantIndustry = RETAIL/FNB/TICKET/OTHER already defined |
| De-hardcode F&B | Build-new | merchant.industry defaults to FNB; invoice templates + ~40–60% of commerce assume F&B — add retail/grocery branches |
| Retail catalog/inventory | Finish | Variants exist; add retail framing (size/color, "catalog" vs "menu"), wire barcode flow (infra InventoryIdentifier done) |
| Grocery: lot/expiry surfacing | Done | lotNumber/expiryDate + Typesense isExpired/isExpiringSoon already there — surface in grocery UI |
| Industry-driven invoice/tax | Finish | MerchantInvoiceProfile.businessType (HOUSEHOLD/BUSINESS) drives tax method; extend mapping for retail/grocery |
Depends on: E1 (tax method per industry). Feeds: E5 (industry-specific KPIs).
E3 · POS — multi-employee shift + KDS + X/Z Finish · P2-finish
Goal: Complete the F&B/retail floor. Mostly wiring already-built code — except the multi-employee shift refactor.
| Item | Tag | Evidence / what to do |
|---|---|---|
| Multi-employee shift | Finish | DECISION: land feat/shift-variant-2 (shift/shift-assignment/shift-device/shift-report, commit cc9baf892, currently need-review). Stabilize + merge, replacing single-user PosSession |
| KDS / send-to-kitchen | Done | kitchen-ticket schema + KitchenTicketService.sendToKitchen + Kitchen.screen.tsx + WS + Kafka→inventory all built |
| KDS ticket printing | Finish | Wire sendToKitchen → PrinterService (tauri-tcp-printer.helper.ts); define ticket format (2-ply) |
| X/Z report | Finish | PosSessionReport (X/Z, full fields) exists; build print template + auto-generate Z on shift close |
| Split / move table | Done | sale-check split + order-split/order-merge; add floor-plan context-menu buttons |
| Merge table | Finish | order-merge works on DRAFT only; confirm policy for PROCESSING orders |
| Manual order status | Finish | Add PATCH /sale-orders/:id/status if mid-order updates needed (today: checkout/cancel + webhook only) |
Depends on: shift refactor stabilizing. Feeds: E5 (X/Z), E6 (shift cash reconciliation).
E4 · CRM — business customers + tax code + groups + payables Build-new · P2-core
Goal: Turn the thin Phase-1 contact record into a real CRM, and enable B2B invoicing. Largest absent cluster.
| Item | Tag | Evidence / what to do |
|---|---|---|
| Business customer (with tax code) | Build-new | user-profile has no tax_code/company → can't issue company VAT invoices; add fields + tax-code validation + invoice link |
| Customer groups | Build-new | Absent — add CustomerGroup + member bridge; foundation for E7 segment pricing |
| Customer payables | Build-new | Absent — add customer ledger/balance (FinanceVoucher has partyType=CUSTOMER to build on) |
| Supplier payables | Finish | Vendor master done; FinanceVoucher PAYMENT already auto-posts on PO-received — add the payables ledger/aggregation + UI |
| Individual customer | Done | name+phone+email works (customer.service.ts); add address |
Depends on: —. Feeds: E1 (company VAT invoices), E7 (segment pricing).
E5 · Reports — P&L + inventory Finish · P2-finish
Goal: Give owners the numbers the 1–3B tier needs.
| Item | Tag | Evidence / what to do |
|---|---|---|
| Revenue reports | Done | SalesReportService (daily/product/category) + UI built |
| All-system revenue dashboard | Finish | In-progress; finish the total dashboard |
| P&L (lãi/lỗ) | Build-new | COGS posts to finance (handleInventoryIssuedForSale) but no P&L service — build Revenue − COGS − OpEx |
| Inventory & best-seller | Finish | UI is ComingSoon; rank from existing getProductSales; add stock/turnover report |
Depends on: E2 (industry KPIs), E3 (shift data), E6 (expense data), E1 (tax). Schedule late.
E6 · Cash & expense depth Finish · P2-finish
Goal: Complete the cash side — manual payment vouchers + shift reconciliation.
| Item | Tag | Evidence / what to do |
|---|---|---|
| Receipt voucher | Done | FinanceVoucherTypes.RECEIPT auto-created on payment success |
| Payment voucher | Finish | PAYMENT type + auto-post on PO-received exist; build manual create/issue/void form (non-PO expenses) |
| Shift cash reconciliation | Finish | cashDiscrepancy computed in closeSession; auto-trigger ADJUSTMENT voucher (reason CASH_COUNT) + dashboard |
Depends on: E3 (multi-shift). Feeds: E5 (P&L OpEx).
E7 · Pricing — campaign + segment Finish · P2-important
Goal: Move from flat pricing to campaign and customer-segment pricing. The rule engine is already powerful; it just lacks context fields.
| Item | Tag | Evidence / what to do |
|---|---|---|
| Rule engine | Done | Operators EQ/NE/GT/IN/CONTAINS… + promotion types STANDARD/BUY_GET + effectiveFrom/To |
| Time / day pricing | Done | dayOfWeek, calculatingAt already in PricingContext |
| Campaign pricing | Finish | Add campaignId to promotion + context (time fields already usable) |
| Customer-segment (VIP/member) | Finish | Add customerGroupId/customerType to PricingContext + sample rules |
Depends on: E4 (customer groups). Feeds: —.
Deferred to Phase 3 (decided 2026-05-31)
Genuinely absent (build-from-scratch) and not required for the 1–3B + retail/grocery goal:
| Capability | Why Phase 3 | Code state |
|---|---|---|
| QR self-order | New guest-facing surface + table-QR mapping | Absent |
| Offline mode | Needs local store + sync queue + conflict resolution in Tauri | UI banner only |
| Loyalty / voucher / points | Points have EARN only (no REDEEM); no voucher model | Partial/absent |
| Webshop | Separate app, large scope | Absent (planned) |
| Device hub | Central device control beyond connection info | Absent |
Proposed sequence (Jun → Sep 2026)
June is mostly Phase 1, not Phase 2
T6 = Phase 1 UAT + carry-over cleanup (the MVP gate). Real Phase 2 build capacity is ~Jul–Sep (≈10–11 weeks). Don't double-count Phase 1 debt as Phase 2.
| Block | Focus | Work |
|---|---|---|
| T6 (parallel) | Spec + harvest | Write E1 tax + E2 Business-Type specs · "harvest sprint": activate already-built low-risk items (KDS print, X/Z template, InventoryTicket state machine) while UAT runs |
| T7 — foundations | The 3 core reasons | E1 S2a calculator + industry tax-rate · E2 retail/grocery branches · E4 business-customer tax code |
| T8 — depth | Build on foundations | E1 form 01/CNKD + T-VAN submit · E3 land multi-shift refactor · E4 payables · E6 payment voucher + reconciliation · E7 pricing context |
| T9 — insight + harden | Downstream + stabilize | E5 P&L + inventory reports · integration + UAT for 1–3B & retail/grocery |
Sizing guardrail
The WK22 review showed completion collapses when a cycle commits ~100 items vs the sustainable ~32 (16 members × 2). Size each Phase 2 sprint to that ceiling and keep Phase 1 carry-over out of the Phase 2 count.
Related Pages
- Roadmap · Phase 1 — What Shipped · Sprint Reviews
- Traceability Matrix — where each epic's PRD/ADR will be linked
- Target Customers · Tax Policy