CA-Review Decisions Tracker — Property #1 Go-Live (EP-007 + Finance/Tax)
Purpose. This is the single source of truth for every decision that was previously gated on "CA (Chartered Accountant) review." Rather than block go-live waiting on a CA, each decision below was made up front, backed by hospitality-SME + Indian-tax research (GST rule / SAC / IT-Act section cited), and recorded here. If a CA later comments, we adjust the affected row — we do not re-litigate the whole set, and we are never blocked waiting.
- Decided: 2026-06-20 (operator + hospitality-sme research)
- Owner: Operator (finance sign-off) · Engineering (implementation)
- Revisit trigger: any CA/auditor comment, a second GSTIN onboarded, or a CBIC rate change.
- Each row: the choice made, the compliance anchor, current code status, and a CA-comment / revisit column to fill in if review feedback arrives.
Legend
- ✅ Shipped — already implemented in code; choice only needed recording.
- 🔨 Build — net-new work unblocked by this decision.
- 🟡 Hardening — shipped, plus a small additional step the operator chose.
Decisions
1 — Central-kitchen / commissary: GST treatment of inter-unit transfers
- Question: When the central kitchen moves prepared food to an F&B outlet, is it a taxable supply or a non-taxable internal transfer?
- CHOSEN: Internal stock-transfer (no GST) + GSTIN-pair auto-detect. Same-GSTIN/same-state move = NOT a supply → delivery challan (Rule 55 particulars), no GST, no ITC event. A GSTIN-pair check auto-flips the transfer to deemed supply if a second GSTIN/registration is ever onboarded.
- Compliance basis: CGST §7 read with Sch I para 2 (deems only distinct/related persons); §25(4) makes separate registrations distinct persons. Chain variant: Sch I deemed supply, value under Rule 28 (first proviso — invoice value = open market value when recipient takes full ITC), e-way bill per Decision 4.
- Status: 🔨 Build (commissary module not yet built — this locks its design). OPERATOR RATIFIED 2026-06-20 — confirmed the configurable per-property-pair GSTIN model (Decision 1), the 2-step indent→approve→issue workflow (Decision 3), and the e-Saral GSP for e-way bill (Decision 4). Commissary SCOPE = Production + distribution: the central kitchen PRODUCES semi-/finished goods (recipe explosion consuming central inventory → prepared items) AND distributes them to outlets — not stock-distribution-only. So the module needs: a CK production/recipe-explosion step (raw→prepared, WAC per Decision 2) + the indent→approve→issue transfer + the GSTIN-pair-driven challan/deemed-supply branch + the e-Saral e-way-bill rule engine. This is a multi-dispatch L build (pos-service + asset-service inventory + the e-Saral adapter); next step = a story-first design workflow.
- CA-comment / revisit: (open) — must wire the GSTIN-pair check before any second registration is onboarded; confirm delivery challan carries Rule 55 particulars even with no GST.
2 — Central-kitchen: costing / valuation for internal transfers
- Question: What unit cost does the indent/transfer note and outlet COGS use?
- CHOSEN: Weighted-Average Cost (WAC).
- Compliance basis: Ind AS 2 / AS 2 (Inventories) permit weighted-average (not last-purchase-price). Same WAC must flow to GL COGS — no standard-cost-on-transfer + WAC-in-GL split.
- Status: 🔨 Build (commissary module).
- CA-comment / revisit: (open) — if Decision 1 ever flips to deemed-supply (chain), the transfer value becomes a Rule 28/30 question (cost+10% ≠ WAC) — keep them as separate fields.
3 — Indenting / requisition workflow
- Question: Does an outlet→commissary indent need approval, and should par breaches auto-raise one?
- CHOSEN: Single-approval gate (value-threshold configurable) + par-level auto-DRAFT (human approves before issue).
- Compliance basis: Internal-control / segregation-of-duties (SOC-2 territory), not a GST/IT rule. Auto-issue without a human is the control gap.
- Status: 🔨 Build (commissary module). Par auto-draft = fast-follow.
- CA-comment / revisit: (open) — never auto-issue on the par path (maker-checker must stay); approval threshold configurable per property.
4 — E-way bill applicability (rule engine)
- Question: When is an e-way bill required for goods movement (commissary→outlet, procurement inbound)?
- CHOSEN: Per-state configurable threshold — inter-state always >₹50k; intra-state threshold configurable per state, dated from current notifications.
- Compliance basis: Rule 138 CGST — inter-state >₹50,000 always; intra-state thresholds set by each state's notification (a hardcoded ₹50k intra rule is wrong in many states).
- Status: 🔨 Build (rule engine). Live filing via the e-Saral GSP — OPERATOR DECISION (2026-06-20): the GSP is e-Saral (NOT Adaequare), with which the operator has already run an e-invoice POC that now needs productionizing; the same e-Saral integration carries both NIC IRP e-invoice + e-way-bill filing. Operator to provide e-Saral GSP credentials. This decision is the rule logic; the e-Saral adapter (auth + IRN + EWB endpoints) is the integration build.
- CA-comment / revisit: (open) — seed the per-state threshold table from current notifications with effective dates; a same-GSTIN sub-threshold move still needs a documented delivery challan. FOLLOW-UP THREAD: scope the e-Saral e-invoice productionization (replace/augment the current EINVOICE_MOCK / direct-NIC path with the e-Saral GSP) — related to but separable from the e-way-bill rule engine.
5 — SAC codes: PHONE + SPA
- Question: Are the flagged SACs correct, and at what GST rate?
- CHOSEN: Ratify both as shipped. PHONE = SAC 998414 @ 18%; SPA = SAC 999723 @ 18% (corrected from 999721 = hairdressing).
- Compliance basis: SAC 9984 telecom 18% (Notif 11/2017-CTR); CBIC 999723 = "physical well-being incl. spa/sauna/steam/massage." Spa rate is 18% regardless of room tariff (not accommodation — ₹7,500 band does not apply).
- Status: ✅ Shipped —
pbx-folio-charge.constants.ts(PHONE 998414@18%),TRANSACTION_CODE_TAX_MAP(SPA 999723, frontdesk folios CLAUDE.md gotcha 59). - Action owed: record finance sign-off + close the
FINANCE-RATIFYcode comment. - CA-comment / revisit: (open) — if the spa sells mixed services, a haircut-at-spa is 999721 vs massage 999723 (per-service SAC, not blanket).
6 — §269ST cash-ceiling posture (card 8758)
- Question: Keep a manager override on the ₹2,00,000/person/day cash limit, or hard block?
- CHOSEN: Hard block, no override — AND delete the deprecated accepted-but-ignored override DTO fields so no client can even appear to request an override.
- Compliance basis: §269ST is a flat prohibition on receiving ≥₹2,00,000 cash; §271DA penalty = 100% of the amount received, on the receiver (the hotel); relief only via Joint Commissioner (§273B) — no employee can authorise, and a logged override documents a knowing violation. Threshold is inclusive (exactly ₹2,00,000 is blocked). All three limbs (per-receipt, per-payer-day §8811, per-occasion §8810) live; refunds (cash OUT) correctly exempt.
- Status: ✅ Shipped hard block (
@hmp/compliancecash-ceiling.ts— override removed per Compliance ruling R3) → 🟡 Hardening to build: remove the@deprecatedoverride DTO fields entirely (cross-package; refresh consumer lockfile in the same commit as the dep bump). - CA-comment / revisit: (open) — CA agrees with hard block; the field removal is the strict-posture extra the operator chose.
7 — GST-2.0 per-property tax-config model
- Question: Keep the ₹7,500 band + rates as an effective-dated code constant + per-property split rows, or a fully editable per-property slab table?
- CHOSEN: Keep the effective-dated code band + per-property CGST/SGST/IGST + supplier-state rows — AND load the dated GST-2.0 seed + validate a per-property GSTIN + supplier-state config row exists before live IRN filing (don't rely on the byte-identical NO_CONFIG fallback for live invoicing).
- Compliance basis: The ₹7,500 boundary + 5%/18% rates are CBIC-set (GST 2.0, eff 22-Sep-2025) — NOT a per-hotel choice; making them editable invites mis-declaration. SAC 996311 for accommodation at BOTH bands (996312 is the camp/holiday-centre SAC, NOT a hotel high-tariff code); per-property config legitimately varies only supplier state (→ CGST/SGST vs IGST place-of-supply, IGST §12) and GSTIN. Band bands on the declared per-night tariff, not the discounted/charged amount.
- Status: ✅ Shipped model + 🟢 Hardening SHIPPED to test (AB#9043): dated GST-2.0 seed landed (hmp-infrastructure
e2487009— legacy ROOM rows closedeffectiveTo2025-09-21 + GST-2.0 5%/18% rowseffectiveFrom2025-09-22, SAC 996311 both bands) + readiness checkGET /folios/validate-tax-readiness+ pre-IRN WARN (frontdesk971f4be, property-service migration2c66e80→6547b3bdropping the legacy unique that blocked the dual-band rows). - CA-comment / revisit: As-built (2026-06-20) — the pre-IRN validation is a READINESS CHECK + WARN, NOT a hard block. A hard throw would break live invoicing for any unseeded property (every property resolves via the NO_CONFIG
@hmp/compliancefallback today). An unseeded property's IRN still generates;validate-tax-readinesssurfacesconfigSeededfor operator visibility. A true hard block waits until ALL live properties are seeded (future decision). Band stays effective-dated so historical invoices re-compute at their date's slab. (open follow-up: assertPropertyTenant on the readiness endpoint — minor same-tenant scope nit; resavenue sandbox still on pre-GST-2.0 "current" rows.)
8 — Tally day-book accounting export (EP-007 D-7 Phase-2)
- Question: How should the TallyPrime accounting export be delivered, what vouchers should it carry, how should ledger names map, and how should refunds / guest advances / journals post?
- CHOSEN (operator + hospitality-SME, 2026-06-20):
- Delivery — BOTH: Tally-importable XML download (S3 signed URL) AND a live HTTP push channel to the property's TallyPrime (port 9000), reusing the existing distribution-channel pattern. (Download is the safe default; push needs the property's Tally exposed.)
- Voucher scope — FULL DAY-BOOK: Sales + Receipt + Payment + Journal vouchers (not Sales-only).
- Ledger mapping — CONFIGURABLE + standard defaults: per-property
TenantConfig(TALLY_LEDGER_MAP)override merged over an SME default chart-of-accounts, so an unconfigured property is byte-identical. Prevents the #1 real-world Tally-import failure (ledger names not matching the target company's chart of accounts). - Refunds — SEPARATE PAYMENT VOUCHER: a refund (money OUT) emits a Tally Payment voucher (Dr Sundry Debtors, Cr Bank/Cash), keeping the Receipt register strictly money-IN.
- Advances — 'ADVANCE FROM GUESTS' LIABILITY LEDGER: a guest advance/deposit posts its counter-leg to a dedicated unearned-liability ledger, NOT Sundry Debtors (it converts to revenue at checkout).
- Journals — BUILD A SOURCE NOW: verify-first found no analytics MV records TDS / round-off / advance-conversion; operator chose to build the journal source (new MV + consumer) rather than ship a no-op stub, so the day-book carries journals at launch.
- Compliance basis: Tally GST Sales vouchers carry the RECORDED CGST/SGST/IGST split (read, never recomputed) with a per-voucher Σ=0 balance assert + fail-closed ledger map (never a blank/orphan ledger → GST can't mis-post). A guest advance is UNEARNED — a liability, not a trade receivable (Ind AS 115 / standard hotel accounting); posting it to Sundry Debtors would misstate the balance sheet. Single-property/single-GSTIN per file (one Tally company per GSTIN).
- Status: ✅ Foundation shipped (Sales-voucher download) → 🔨 Build in gated dispatches — D1 configurable ledger map + per-property company name, D2 Receipt vouchers, D3 Payment vouchers (+ refund-as-payment), D4a Round-off journal, D4b TDS journal (Dr "TDS Receivable" / Cr "Sundry Debtors"), D4c Advance-conversion journal (Dr "Advance from Guests" / Cr "Sundry Debtors", Σ=0, no GST) — ALL ✅ SHIPPED to test, AB#5213 (each gated code-review + compliance clean). D4 journal scope = FULL SET (Wave-3 operator decision): TDS + round-off + advance-conversion — all three sources built (new analytics MV + consumer + day-book journal branch each), idempotent + tenant-scoped, with a both-sides producer contract test for the two cross-service sources (D4b TDS, D4c advance). D5 HTTP-push channel (a
TallyPushChannelDeliveryChannel POSTing the rendered XML to a per-property TallyPrime gateway, disabled-by-default, SSRF-bounded, gated code-review + security clean) and D6 dashboard UI — D6a export button, D6b auth-service TenantConfig CRUD API (/tenants/:t/config/:cat/:key;categoryenum→String; it activates D1/D5's per-property reads which previously 404'd to defaults), D6c ledger-map + push-config editor forms — ALL ✅ SHIPPED to test, AB#5213. The entire D-7 build (D1–D6c) is now CODE-COMPLETE and on test. REMAINING = OPERATOR-OWNED ONLY: the CA golden-sample TallyPrime round-trip ship-gate (below) + step-6 Closed-Verified + staging/prod manual promotion. - CA-comment / revisit: (open) — operator ship-gate: a CA golden-sample TallyPrime round-trip must validate a generated file imports + posts balanced before any real-tenant rollout (that file becomes the regression fixture; applies to the default map — a per-property CUSTOM map should be CA round-tripped before that property's rollout). Confirm the default expense/bank ledger names match the property's TallyPrime chart of accounts.
Summary table
| # | Decision | Chosen (single-GSTIN Property #1) | Compliance anchor | Status |
|---|---|---|---|---|
| 1 | Inter-unit food transfer | Internal stock-transfer, no GST + GSTIN auto-detect | CGST §7/§25(4), Sch I para 2 | 🔨 Build |
| 2 | Transfer valuation | Weighted-Average Cost (WAC) | Ind AS 2 / AS 2 | 🔨 Build |
| 3 | Indenting approval | Single-approval + par auto-DRAFT | Internal control / SOC-2 | 🔨 Build |
| 4 | E-way bill | Per-state config; inter-state-always + ₹50k intra | Rule 138 CGST | 🔨 Build (Adaequare gated for live filing) |
| 5 | PHONE + SPA SAC | 998414 @18% · 999723 @18% — ratify | SAC 9984 / CBIC 999723 | ✅ Shipped (record sign-off) |
| 6 | §269ST | Hard block, no override + delete deprecated fields | §269ST / §271DA / §273B | ✅ Shipped → 🟡 hardening |
| 7 | GST-2.0 tax-config | Effective-dated band + per-property rows + seed/validate | CBIC GST-2.0, IGST §12 | ✅ Shipped → 🟡 hardening |
| 8 | Tally day-book export | Both delivery · full day-book · configurable ledgers · refund→Payment · advance→liability · full journal set (TDS + round-off + advance) | Tally GST voucher (split read-not-recomputed) · Ind AS 115 (advance=liability) | ✅ Built (D1–D6c on test) · ⏳ CA golden-sample gate + operator close |
Implementation backlog created by these decisions
- Build (commissary/procurement module — Decisions 1–4): GSTIN-pair transfer-tax resolver (
INTERNAL/DEEMED_SUPPLY); WAC costing on transfer + GL COGS; indent approval workflow + par auto-draft; per-state e-way-bill threshold rule engine (live filing deferred to Adaequare creds). - Hardening (Decision 6): remove deprecated §269ST override DTO fields (cross-package, lockfile-in-same-commit).
- Hardening (Decision 7): load dated GST-2.0 seed + pre-IRN per-property tax-config validation.
- Record (Decision 5): finance sign-off on PHONE/SPA SAC + close the
FINANCE-RATIFYcode comment. Build (Tally day-book — Decision 8): D1–D6c.✅ DONE — the entire D-7 build is CODE-COMPLETE and on test (AB#5213): D1 ledger map + D2 Receipt + D3 Payment(+refund-as-payment) + D4a Round-off + D4b TDS + D4c Advance-conversion journals + D5 HTTP-push channel + D6a export button + D6b auth-service TenantConfig CRUD API (activates the D1/D5 per-property reads) + D6c ledger-map/push-config editors — each gated (code-review + compliance/security as applicable) before ship. OPERATOR-OWNED REMAINDER: the CA golden-sample TallyPrime round-trip ship-gate before any real-tenant rollout (the counter-legs — refund / round-off / TDS / advance-conversion all → "Sundry Debtors" — are flagged in-code for CA ratification vs dedicated control ledgers), then step-6 Closed-Verified + staging/prod promotion.
Relevant code anchors
hmp-shared-packages/packages/compliance/src/lib/cash-ceiling/cash-ceiling.ts— §269ST hard block (override removed, R3)hmp-frontdesk-service/src/folios/accommodation-gst.util.ts— ₹7,500 band + intra-state splithmp-frontdesk-service/src/folios/consumers/pbx-folio-charge.constants.ts— PHONE SAC 998414 @18%hmp-frontdesk-service/src/folios/folio-tax-resolver.service.ts— canonical SAC map + per-property tax resolution
Commissary module — hospitality-SME review (2026-06-20): GO + refinements to Decisions 1–4
The Phase-5 Central-Kitchen/Commissary module (EP-007, cards AB#9044 US-007-101 … 9047 US-007-104) implements locked Decisions 1–4. The hospitality-SME domain-reviewed the build slice and returned GO for the single-GSTIN Property #1 launch (INTERNAL path only; Rule-28/DEEMED deferred) with these decisions recorded:
- A — Property-type matrix: Single hotel / Resort / Chain-same-GSTIN → INTERNAL (delivery
challan, no GST, WAC). Chain multi-GSTIN/cross-state → DEEMED_SUPPLY (deferred US-007-105).
No-commissary archetypes (BnB / PG / Hostel) → module INERT, return a benign
200 {commissaryConfigured:false}, never a thrown error. Vishramgruh/seva canteen: par-auto-DRAFT DOES apply (do NOT make it manual-only); the canteen issue/billing uses a NON-GST issue slip + donation receipt (reuseOutlet.taxInvoiceMode=NON_GST_SLIP), NOT a tax invoice (many trusts are GST-unregistered/exempt). Resort = the heaviest INTERNAL-path user. - B — Rule-28 valuation default (DEFERRED US-007-105, does NOT block launch): default
commissaryTransferValuationMode = WAC_AS_OMV(markup 0); cost+10% (Rule 30) is the FALLBACK only when the recipient is NOT fully ITC-eligible. Rule 28 first proviso is decisive: a commissary→outlet deemed supply has the recipient outlet fully ITC-eligible → the declared invoice value IS deemed the open-market value → net GST ≈ 0. KeepwacUnitCost(Ind AS 2 / COGS) anddeemedTransferValue(Rule 28 GST base) as SEPARATE columns even at markup-0. - C — E-way per-state seed (P0, blocks US-007-104 correctness): inter-state always >₹50k (confirmed). Intra-state is NOT a flat ₹50k — MH / DL / TN / WB = ₹1,00,000 (Property #1 is likely MH → a hardcoded ₹50k is the #1 error); most other states ₹50k; Gujarat has an intra-city carve-out. Seed WITH effective dates (per-state notification dating). An INTERNAL sub-threshold move still requires a Rule-55 delivery challan — "e-way not required" ≠ "no document".
- D — Indent approval (P0, blocks US-007-103): single approval gate + value-threshold +
par-auto-DRAFT (never auto-issue) is the right base, BUT must enforce maker-checker SoD:
requester ≠ approver (
INDENT_SEGREGATION_OF_DUTIES, mirror the shipped GRNgrnEnforceSegregationOfDutiespolicy). Three-way (requester ≠ approver ≠ issuer) is the chain follow-on; two-way is the launch floor. - E — GO verdict: slice sequence 101 → 102 → 103 (with SoD) → 104 (with the ₹1L seed + Rule-55-always) is build-ready for Property #1. Turns to REWORK only if 103 ships without the requester≠approver SoD or 104 ships with a flat ₹50k intra-state seed.
As-built so far: US-007-101 (AB#9044) SHIPPED to test — CommissaryUnit + Outlet gstin/gstStateCode + the pure INTERNAL/DEEMED resolver (null/same-GSTIN → INTERNAL, never throws). Remaining: 102 (TransferNote + WAC + separate deemed-value column), 103 (indent + SoD), 104 (e-way engine + ₹1L seed). Live e-way filing stays Adaequare-GSP-credential gated (operator).