EP-012C — Tax & Compliance Reports RTM
EPIC: EP-012C Tax & Compliance Reports Parent EPIC: EP-012 Analytics, Reporting & Night Audit Reconciled: 2026-05-25 (Wave 1 of 21 parallel agents) Pattern: see
./PLAYBOOK.mdand the pilot at./EP-002-rtm.md. Source of truth for pass-rate:hmp-docs/docs/platform-registry/2026-05-16-13-flow-demo-readiness.md(flow-8 checkout + e-invoice was 0/15 — the worst on the platform; AP-T08-003/006/007 + W2.A.1 + AP-T07-004 + FU-55 landed since).Catalog vs spec note: the catalog (
01-epic-catalog.mdlines 351-370) currently lists EP-012 as one flat EPIC with storiesUS-012-001..020. The EP-012C spec doc (epics/EP-012C-tax-compliance-reports.md) declares its own 9-story slate (US-012-04,US-012-05,US-012-14,US-012C-01..06). This RTM follows the EP-012C spec doc per task instructions and surfaces the dual numbering as driftD-001(P0). The catalog story most engineers reach for first isUS-012-005 — GST Reports (GSTR-1, GSTR-3B Preparation), which corresponds to specUS-012-04 + US-012-05.
Status legend
| Status | Meaning |
|---|---|
Built | Code exists + tests exist + tests pass |
Built-Untested | Code exists, no automated test coverage (or test exists but is a stub) |
Partial | Code exists for some acceptance criteria, gaps for others |
Stub | Code is a placeholder / TODO / returns mock data |
Not Started | No code |
Summary
| Metric | Count |
|---|---|
| Total stories (EP-012C spec) | 9 |
| Catalog claims (EP-012-005 surrogate) | Partial |
| Verified-Built | 0 |
| Built-Untested | 0 |
| Partial | 5 (US-012-04, US-012-05, US-012-14, US-012C-02, US-012C-06) |
| Stub | 1 (US-012C-01 CDNR — credit notes only exist as CreditNote model + generateCreditNote IRN path; no GSTR-1 Section 9 export) |
| Not Started | 3 (US-012C-03 Regional Tax, US-012C-04 Compliance Calendar, US-012C-05 STR Export) |
| Drift findings | 11 (D-001..D-011; 5 P0 / 4 P1 / 2 P2) |
| [SME-PROPOSED] refinements | 18 |
US-012-04 — GST GSTR-1 Report Generation
Catalog status (proxy: US-012-005): Partial (P0, 13 points) Verified status: Partial — Two parallel implementations exist (frontdesk + analytics); both incomplete vs the NIC schema v3.4 requirement
Code locations:
hmp-frontdesk-service/src/compliance/gstr-reports.controller.ts—GET /compliance/gstr1,GET /compliance/gstr3b(manager+/accountant)hmp-frontdesk-service/src/compliance/gstr-reports.service.ts—Gstr1ExportService.generateGstr1()(B2B / B2C / HSN summary from folio data)hmp-analytics-service/src/gstr/gstr.controller.ts:43-100—GET /reports/gstr3b?month=YYYY-MM&propertyId=UUID&forceRefresh=true(cache-first viaMvGstr3bSnapshot, AP-T10-005)hmp-analytics-service/src/gstr/gstr.service.ts—GstrService.computeGstr3b()aggregates fromEventStorefolio.posted eventshmp-analytics-service/src/reporting/reports/gst.definition.ts—gst-reportv2 report (finance category; composite overMvGstr3bSnapshot + MvEInvoiceRegister; formatsjson/csv/pdf/xlsx)hmp-analytics-service/prisma/schema.prisma—MvGstr3bSnapshot { tenantId, propertyId, month, taxableB2b, taxableB2c, igstAmount, cgstAmount, sgstAmount, hsnBreakdown JSON, computedAt }hmp-web-dashboard/src/app/(dashboard)/settings/compliance/page.tsx— Compliance settings page
Unit + integration tests:
hmp-analytics-service/src/gstr/gstr.controller.spec.ts— controller specshmp-analytics-service/src/reporting/reports/gst.definition.spec.ts—gst-reportv2 definition testhmp-analytics-service/src/consumers/einvoice.consumer.spec.ts—EInvoiceConsumerupsert tests (feedsMvEInvoiceRegister)hmp-frontdesk-service/src/folios/__tests__/gsa-067-igst-split.spec.ts— IGST vs CGST+SGST split assertion at folio post-timehmp-frontdesk-service/src/folios/fu-55-folio-event-dual-emit.spec.ts— 8 specs covering canonicalFOLIO_POSTEDpayload (segment / countryCode / roomTypeCode / ratePlanCode / roomNights / taxAmount)hmp-frontdesk-service/src/folios/folios.service.spec.ts—postChargeGST split persistence (AP-T07-001, intra/inter/omitted-defaults)hmp-frontdesk-service/src/folios/consumers/pos-folio-charge.consumer.spec.ts— 29 specs incl. 9 AP-T07-004 cases (explicit-split honour / derive intra-vs-inter / supplier-state fallback / outbox envelope)
Flow tests:
hmp-infrastructure/testing/flow-tests/src/flows/gst-tax-flags.flow.spec.ts— covers P0-5 (isTaxExempt), P0-6 (isReverseChargeApplicable), P0-7 (GET /compliance/gstr1 + gstr3b respond with rows + totals), P0-11 (TDS). PASS-rate TBD — part of the broader 13-flow baseline (2026-05-16-13-flow-demo-readiness.md).hmp-infrastructure/testing/flow-tests/src/flows/nic-einvoice-live.flow.spec.ts— readiness smoke for NIC live-mode flip (asserts EInvoiceModule fails LOUDLY whenEINVOICE_MOCK=false+ missing creds; mock response envelope shape parity with NIC v1.04).
Real-backend smokes:
- none specific for GST (compliance page covered indirectly via dashboard smokes)
E2E (Playwright): none specific
Use cases referencing this story:
- task brief cites
_data/use-cases/uc-gstr3b-prep.yaml— NOT YET AUTHORED (drift D-002 — proposed sidecar)
SME notes:
- Two parallel GSTR-1 implementations:
Gstr1ExportServicein frontdesk computes from liveFoliorows (take: 1000cap, single-month) while analytics-service exposesgst-reportv2 overMvGstr3bSnapshot + MvEInvoiceRegister. Gold-Standard charter ("one canonical impl not two") is violated. The catalog mentions only one surrogate row (US-012-005); spec splits into US-012-04 GSTR-1 + US-012-05 GSTR-3B; reality is two services × two stories = four code paths that can drift. Recommendation: retiregstr-reports.service.tsin frontdesk, route/compliance/gstr1through the gateway togst-reportv2 (which already has the right MV backing + RBAC + export formats). - GSTN portal schema v3.4 validation is NOT in code anywhere. The acceptance criterion "JSON output passes schema validation against the GST government schema v3.4" has zero implementation. Risk: portal rejection on first upload, hotel files late, ₹50–200/day penalty. Per the spec's "Business Objective" this is THE objective.
- B2C state code uses property's state, not place-of-supply (guest's state). Per US-012-005 review GAP-2, the frontdesk
Gstr1ExportServicederives B2C state code fromproperty.state.substring(0, 2)— that's the supplier state, not the recipient state. Section 7 of GSTR-1 requires place-of-supply at the recipient state for unregistered consumers. - HSN/SAC code map is incomplete. Today hardcoded to
996311(room) and996331(restaurant). Missing:998114(laundry),999719(spa),9963parent for accommodation,9996for restaurant parent — spec lines 42-43 enumerate 9963/996311-996329/9996/9997 — none of9996/9997/998114/999719are wired. Channel-service POS items have an HSN field but the GSTR-1 path doesn't read it (it readstransactionCodeand hardcodes the SAC). - NIC IRP rate limits (1000 IRN/sec sustained, 2500 burst per GSTIN — published in NIC e-invoice IRP v1.04 docs): not enforced anywhere. A batch checkout night-audit run could swamp NIC and trigger throttling; payload retry-after handling is absent in
nic-einvoice.adapter.ts(30-s timeout only, noRetry-Afterheader parsing).
Drift findings:
D-001 — [P0] [Catalog-vs-spec-numbering] Catalog (01-epic-catalog.md:355) lists US-012-005 — GST Reports (GSTR-1, GSTR-3B Preparation) as a single Partial P0 row. The EP-012C spec doc declares US-012-04 + US-012-05 + US-012-14 + US-012C-01..06 as nine separate stories. Two referent docs disagreeing on story-IDs is a pilot-blocking drift (same shape as EP-002 D-007).
- [SME-PROPOSED] Adopt the EP-012C spec doc numbering as canonical. Add an alias line to the catalog row:
> Sub-epic EP-012C reshapes this into US-012-04 (GSTR-1) + US-012-05 (GSTR-3B) + US-012-14 (audit-log) + US-012C-01..06 (CDNR, rate tiers, regional, calendar, STR, IGST). See epics/EP-012C-tax-compliance-reports.md.
D-002 — [P0] [Story-has-no-UC + India-compliance-critical] Task brief cites _data/use-cases/uc-gstr3b-prep.yaml and uc-frontdesk-einvoice-irn.yaml + uc-frontdesk-einvoice-b2b.yaml. None of these YAMLs exist in docs/requirements/_data/use-cases/. The GSTR-3B and IRN flows are spec-text-only with no executable use-case definition.
- [SME-PROPOSED] Author 3 new use-case YAMLs under
use-case-proposals/:uc-gstr3b-prep-SME-PROPOSED.yaml— given Finance Manager picks month, when she callsGET /api/reports/gstr3b?month=YYYY-MM&propertyId=UUID, then response includes B2B/B2C/IGST/CGST/SGST + HSN breakdown; cache-first.uc-frontdesk-einvoice-irn-SME-PROPOSED.yaml— given folio close on a property with GSTIN + non-demo tenant, when frontdesk auto-fires e-invoice, thenEInvoiceJobrow is queued withdocumentType=INV; on COMPLETED, IRN + ackNumber + signedQRCode denormed ontoFolio(AP-T08-006).uc-frontdesk-einvoice-b2b-SME-PROPOSED.yaml— given folio with linkedCompany.gstin, when IRN generated, thensupplyType='B2B'+buyerDetails.gstin = company.gstin+ inter-state IGST split when company state ≠ property state (AP-T08-003).
D-003 — [P0] [Cross-service-drift + duplicate-impl] Two GSTR-1 implementations exist: hmp-frontdesk-service/src/compliance/gstr-reports.service.ts (frontdesk-owned, live folio scan, take: 1000 cap) AND hmp-analytics-service/src/reporting/reports/gst.definition.ts (analytics v2 report, MV-backed). Gold-Standard charter forbids parallel implementations. Risk: numbers don't reconcile between the two surfaces (already known via US-012-005 review GAP-2: B2C state code differs; GAP-9: tax split differs).
- [SME-PROPOSED] Mark
gstr-reports.service.tsdeprecated; route the/compliance/gstr1HTTP path through the gateway togst-reportv2 (analytics-service) which already has MV backing, RBAC, scheduling, multi-format export (json/csv/pdf/xlsx), audit, and 5-year retention. Frontdesk module deletion follows after one release cycle (mirrors the FU-55 dual-emit retirement plan).
D-004 — [P0] [Story-incomplete + India-compliance-critical] No GST portal schema v3.4 validation anywhere in code. The acceptance criterion "JSON output passes schema validation against the GST government schema v3.4" (US-012-04 AC-1) has zero implementation. Portal rejection on first upload is the platform's stated north-star failure mode.
- [SME-PROPOSED] Add AC: "Both GSTR-1 and GSTR-3B JSON outputs SHALL be validated against the GST portal schema (currently v3.4) via
ajvorzodschema at the boundary. Schema files (downloaded fromhttps://www.gst.gov.in/download/returns) live inhmp-shared-packages/packages/common/src/gst-schemas/. A CI gate runs the validation on a corpus of 50 synthetic + 5 real-anonymized fixtures (last 6 months) per release." - [SME-PROPOSED] Add AC: "B2C state-code SHALL be derived from guest's billing address state (place-of-supply), not property state. When guest has no billing state, fall back to property state with a
CTX-PLACE-OF-SUPPLY-FALLBACKwarning logged for ops review."
D-005 — [P1] [Story-incomplete + integration-risk] NIC IRP rate-limit handling missing. nic-einvoice.adapter.ts has a 30-s timeout but no parsing of Retry-After headers, no exponential backoff on HTTP 429, no per-GSTIN concurrency cap. A night-audit run that posts 80 room charges + 80 e-invoice IRN requests in a 60-s window can saturate the 1000-IRN/sec sustained limit if the property runs multiple GSTINs through one bot.
- [SME-PROPOSED] Add AC: "
NicEInvoiceAdapterSHALL respectRetry-Afterresponse header and back off with exponential delay (10 s → 60 s → 300 s, capped at 600 s), and SHALL enforce a per-GSTIN concurrency cap of 50 in-flight requests via a Redis semaphore. Test: 60-request burst against a mocked-429 adapter completes in ≥3 batches without losing any request."
US-012-05 — GST GSTR-3B Summary Report
Catalog status (proxy: US-012-005): Partial (P0, 8 points)
Verified status: Partial — Output tax liability rows shipped (MvGstr3bSnapshot + gst-report v2); ITC (Input Tax Credit) section is always 0 with explanatory note; net-tax-payable computation lacks ITC offset
Code locations:
hmp-analytics-service/src/gstr/gstr.service.ts—computeGstr3b(): aggregates folio.posted events fromEventStorefor a calendar month, returns B2B/B2C split + CGST/SGST/IGST totals + HSN breakdown grouped by HSN/SAC code, caches inMvGstr3bSnapshot. PassforceRefresh=trueto bypass.hmp-analytics-service/src/gstr/gstr.controller.ts:43-100—GET /reports/gstr3b?month=YYYY-MM&propertyId=UUID(Roles: SUPER_ADMIN, ADMIN, MANAGER, ACCOUNTS)hmp-frontdesk-service/src/compliance/gstr-reports.controller.ts—GET /compliance/gstr3b(legacy/duplicate per D-003)hmp-analytics-service/src/consumers/folio.consumer.ts— populatesMvRevenueBySource+MvDailySalesRegisterfromfolio.posted(canonical event with FU-55 dual-emit fields:taxAmount,segment,countryCode,roomTypeCode,ratePlanCode,roomNights)hmp-analytics-service/src/consumers/einvoice.consumer.ts— populatesMvEInvoiceRegisterfromeinvoice.completed(idempotent upsert by IRN)hmp-analytics-service/src/reporting/reports/gst.definition.ts—gst-reportv2 composite (merges GSTR-3B + IRN-level e-invoice rows)
Unit + integration tests:
hmp-analytics-service/src/gstr/gstr.controller.spec.ts— controller specshmp-analytics-service/src/reporting/reports/gst.definition.spec.ts— v2 report definition testhmp-analytics-service/src/consumers/einvoice.consumer.spec.ts— register population- (No
gstr.service.spec.ts—computeGstr3b()is untested directly; coverage only via controller spec mock paths)
Flow tests:
hmp-infrastructure/testing/flow-tests/src/flows/gst-tax-flags.flow.spec.ts— P0-7 portion asserts/compliance/gstr3breturns rows + totals (soft-skip if 404).
Real-backend smokes: none
Use cases referencing this story:
- task brief cites
_data/use-cases/uc-gstr3b-prep.yaml— proposed in D-002
SME notes:
- ITC section is a known stub. Per US-012-005 review GAP-10 + AC-7: ITC always 0 with explanatory note. Real reason: HMP does not capture purchase invoices (vendor master + AP invoices) — that's
hmp-asset-servicevendor master scope, but no integration exists. GST law (Sec 16 CGST Act) requires net tax payable = output tax − eligible ITC; current GSTR-3B JSON understates ITC by 100%, overstates net payable. Tenant filing this verbatim WILL overpay. - No
gstr.service.spec.tsfile. ThecomputeGstr3b()service that fillsMvGstr3bSnapshotis the single source of GSTR-3B truth and has zero direct test coverage. Controller spec mocks the service — does not validate the SQL/aggregation arithmetic. - Folio
take: 1000cap (frontdesk path) silently truncates output for high-volume months. A 200-room hotel doing 5,000+ transactions/month would hit this without warning. The analytics path (EventStore stream) does not have this cap but ALSO does not paginate — relies on the consumer's order delivery; OOM risk on backfill of historical data. - GSTR-3B filing deadline: 20th of each month (CGST Rule 61); GSTR-1 by 11th. Late filing: ₹50/day + ₹50/day for nil returns up to ₹5,000 cap per return; interest 18% p.a. on unpaid tax. Spec narrates this but no "filing calendar" exists (US-012C-04 is Not Started).
- IRN cancellation 24-hour window: NIC IRP allows IRN cancellation only within 24h of generation; after that the original IRN is permanent and corrections require a credit note.
EInvoiceControllerhas/einvoice/cancelbut no time-window enforcement — a cancellation after 24h will silently fail at NIC, surfacing only via the FAILED job branch. Spec body doesn't mention this constraint.
Drift findings:
D-006 — [P0] [Story-incomplete + India-compliance-critical] GSTR-3B ITC section hardcoded to 0; no purchase-invoice model exists. Net tax payable in the JSON is gross output tax — files literally as-is will OVERPAY tax. Spec line 158 acknowledges this as "a known gap" but the JSON downloaded by Finance Manager has no marker that the ITC=0 row is a stub.
- [SME-PROPOSED] Add AC: "GSTR-3B JSON output SHALL include a
metadata.itcStubbed: trueflag and a human-readable warning in the response envelope when ITC = 0. The dashboard download button SHALL render a confirmation modal: 'ITC not computed by HMP. You must add eligible ITC from your purchase records before filing to avoid overpaying GST. Proceed?'. Manual override required." - [SME-PROPOSED] Add UC
uc-gstr3b-itc-manual-override-SME-PROPOSED.yaml: given a generated GSTR-3B withitcStubbed=true, when Finance Manager enters ITC totals per rate bucket, then aGstr3bItcOverriderow is persisted (tenantId, propertyId, month, itc5pct, itc12pct, itc18pct, itc28pct, attestation), and the cachedMvGstr3bSnapshotis invalidated. Source-of-truth for audit trail.
D-007 — [P1] [Test-vs-code] gstr.service.ts::computeGstr3b() has no direct spec. The arithmetic that fills MvGstr3bSnapshot.cgstAmount/sgstAmount/igstAmount/hsnBreakdown is untested. A regression in the SQL aggregation would not be caught by the controller spec (which mocks the service).
- [SME-PROPOSED] Add
hmp-analytics-service/src/gstr/gstr.service.spec.tscovering: (a) empty month returns zero totals + emptyhsnBreakdown, (b) single B2B folio with explicit CGST+SGST yields correct totals, (c) inter-state B2B folio with explicit IGST yields IGST-only totals, (d) HSN aggregation groups multiple folios with the same SAC code, (e)forceRefresh=truebypasses cache, (f) idempotency: two invocations against the same EventStore range produce byte-identical output.
US-012-14 — Audit Log with Integrity Chain
Catalog status (proxy: US-012-014): Built (P0, 8 points) — note: catalog row says Built, spec aligns
Verified status: Partial — MvAnomaly + ReportRun audit-trail exist; no SHA-256 hash-chain AuditLog entity is implemented anywhere. Spec's "tamper-evident hash chain" requirement is uncoded.
Code locations:
hmp-analytics-service/src/reporting/audit/—ReportAuditServicewritesReportRunrows on every v2 report execution (userId, filters, format, IP, UA, duration, rowCount, presigned export URL); 5-year retention per CERT-In + GST.hmp-analytics-service/src/anomaly/—MvAnomalyrows + dismiss audit (dismissedBy,dismissedAt,dismissReason) — anomaly trail, not financial event trail.hmp-shared-packages/packages/security/src/audit/—@hmp/securityaudit decorator@Audit()used across services; writes to per-service audit tables.hmp-web-dashboard/src/app/(dashboard)/settings/compliance/page.tsx— compliance UI; no audit-log viewer with hash-chain status indicator.
Unit + integration tests:
- v2 reporting framework tests (
ReportAuditServicetest files implicit inreporting/) — coverage of audit-row writes - (No
audit-loghash-chain spec exists — the entity doesn't exist)
Flow tests: none specific (ReportRun writes are exercised indirectly by any v2 report test)
Real-backend smokes: none
Use cases referencing this story: none
SME notes:
- Spec misrepresents reality. EP-012C spec lines 73-74 + 87 + 175-198 + 251 + 259 + 265 describes
AuditLog { id, entryHash, previousHash, eventType, payload, createdAt }withentryHash = SHA-256(previousHash + eventType + payload + createdAt)andGET /auditlog/verifyreturning the first tampered entry. None of this exists in code.MvAnomaly.entryHashdoesn't exist;EventStorehas no hash column;ReportRunhas no chain link; no/auditlogcontroller in analytics-service or anywhere else. - Catalog says Built; reality says Not Started. This is a P0 story-vs-code drift identical to the EP-002 D-011 review-stale pattern but inverted: catalog optimistic, code absent.
- What DOES exist:
ReportRunis excellent for report-execution audit (who ran what when, presigned URL of the artifact) — but it's not the financial-event tamper-evident chain that DPDPA Art. 8(5) + RBI / IRDAI audit requirements + CERT-In 6-hour breach-notification context demand for financial trail integrity. - Hash-chain design today: would land naturally on
EventStore(which already has the event-sourcing append-only shape), addingentryHash+previousHashcolumns + a chain-verify endpoint. Requires migration + backfill (compute hashes for existing rows in time-order).
Drift findings:
D-008 — [P0] [Catalog-says-Built + spec-says-Built + code-absent] Tamper-evident hash-chain audit log claimed in catalog US-012-014 "Built" and in EP-012C spec as a Built story with AuditLog { entryHash, previousHash } model + GET /auditlog/verify endpoint — none of this exists. Closest match is ReportRun (report-execution trail) and MvAnomaly (anomaly dismiss trail), neither of which is hash-chained. New reviewer trusting the spec/catalog would believe a CERT-In or RBI audit could be answered with this story — it cannot.
- [SME-PROPOSED] Demote catalog status to
Stub(orNot Started). - [SME-PROPOSED] Author plan to extend
EventStorewithentryHash String?+previousHash String?columns + a daily cron that backfills rows increatedAtorder computingentryHash = SHA-256(previousHash || eventType || payload || createdAt). AddGET /auditlog/verify?fromDate=&toDate=&propertyId=returning the first tampered entry ID + chain-break range, RBACCOMPLIANCE_OFFICER | SUPER_ADMIN | ADMIN. Pin via spec athmp-docs/docs/superpowers/specs/2026-NN-NN-eventstore-hash-chain-design.md. - [SME-PROPOSED] Until shipped, explicitly disclaim in the dashboard compliance page that audit-log integrity is via
ReportRun(per-execution) not per-financial-event, and link to the plannedEventStorehash-chain story.
US-012C-01 — CDNR (Credit/Debit Note Register)
Catalog status: Not Started (P0, 5 points)
Verified status: Stub — CreditNote model + IRN-generation path exist (AP-T08-007); GSTR-1 Section 9 export does NOT include them
Code locations:
hmp-frontdesk-service/prisma/schema.prisma—CreditNote { id, folioId, creditNoteNumber (unique), reason (RefundReason), cgstAmount, sgstAmount, igstAmount, creditNoteIrn String?, status (DRAFT | APPROVED | IRN_GENERATED) }hmp-frontdesk-service/src/einvoice/einvoice.service.ts—generateCreditNote(folioId, originalIrn, propertyId)— queue-routed viaEInvoiceJobdocumentType='CRN'(AP-T08-007); idempotent viafindActiveCreditNoteJob; processor branches to write IRN ontoCreditNote.creditNoteIrn(NOTFolio.irn).hmp-frontdesk-service/src/einvoice/einvoice.controller.ts—POST /einvoice/credit-note(manager+/accountant)hmp-frontdesk-service/src/einvoice/einvoice-job.processor.ts—documentType-aware completion (AP-T08-007)- (No GSTR-1 Section 9 CDNR export endpoint or report-definition exists)
Unit + integration tests:
hmp-frontdesk-service/src/einvoice/einvoice.service.spec.ts—generateCreditNotedescribe block (queue-routing + idempotency)hmp-frontdesk-service/src/einvoice/einvoice-job.processor.spec.ts— CRN-vs-INV branch + retry/DLQ
Flow tests: none
Real-backend smokes: none
Use cases referencing this story: none
SME notes:
- The Credit Note generation works (queue-routed, idempotent, dead-letter handled). The GSTR-1 reporting half does not. Spec AC-EP-012C-03: "CDNR section includes all credit notes linked to original invoices with correct IRN references." — there is no code that reads
CreditNote.creditNoteIrnand emits a Section 9 block in the GSTR-1 JSON. - This is a 2026-04-29 implementation (
AP-T08-007) sitting alone — the GSTR-1 export pipeline was never extended to consume it.
Drift findings:
D-009 — [P0] [Story-incomplete + India-compliance-critical] CreditNote IRN-generation path is shipped end-to-end (queue, retry, DLQ, idempotency) but the GSTR-1 export does not emit a Section 9 CDNR block referencing the original invoice. A cancellation processed through HMP creates a CreditNote with valid IRN but never reaches the GST portal — guest's tax liability stays inflated, hotel's outward-supply totals stay inflated.
- [SME-PROPOSED] Author new story
US-012C-01a"GSTR-1 Section 9 CDNR Export" with ACs: (a)gst-reportv2 SHALL pullCreditNoterows for the period via a newCreditNoteServiceClientcall to frontdesk; (b) emit Section 9 with fields(creditNoteNumber, creditNoteDate, originalInvoiceNumber, originalInvoiceDate, originalIrn, creditNoteIrn, reason, taxableValue, cgstAmount, sgstAmount, igstAmount); (c) Section 9 row count + net taxable reduction surfaced in GSTR-3B Table 3.1 outward-supply adjustments.
US-012C-02 — GST Rate Tier Configuration
Catalog status: Partial (P0, 5 points)
Verified status: Partial — Folio split (CGST/SGST/IGST columns) shipped via AP-T07-001 + AP-T07-004 + W2.A.1 migration 20260523120000; tariff-based rate-tier (0/12/18%) selection is NOT centralized; ad-hoc lookup at folio post-time
Code locations:
hmp-frontdesk-service/prisma/schema.prisma—FolioTransaction.cgstAmount/sgstAmount/igstAmount/customerState/placeOfSupply(W2.A.1 columns; migration20260523120000_add_folio_transaction_gst_splitfinally added them; previously schema-only drift — see frontdesk CLAUDE.md Gotcha 29)hmp-frontdesk-service/src/folios/folios.service.ts:582-597—postChargenow passes split fields straight through (AP-T07-001)hmp-frontdesk-service/src/folios/consumers/pos-folio-charge.consumer.ts:341-348—deriveGstSplit(): explicit producer split honoured; rolled-uptaxAmount+customerStatederived intra-vs-inter via property GSTIN/state first 2 digits (AP-T07-004)hmp-property-service/src/taxes/—TaxConfigurationmodel (rate + inclusive/exclusive flag — AP-T02-005)- (No central
GstRateTierResolverservice that picks 0% / 5% / 12% / 18% from tariff band — each posting site applies its own rate)
Unit + integration tests:
hmp-frontdesk-service/src/folios/folios.service.spec.ts— 3 AP-T07-001 specs (intra-state, inter-state, omitted-defaults)hmp-frontdesk-service/src/folios/consumers/pos-folio-charge.consumer.spec.ts— 29 specs incl. 9 AP-T07-004 caseshmp-frontdesk-service/src/folios/__tests__/gsa-067-igst-split.spec.ts— IGST vs CGST+SGST switching
Flow tests:
hmp-infrastructure/testing/flow-tests/src/flows/gst-tax-flags.flow.spec.ts— P0-5/P0-6/P0-7 paths
Real-backend smokes: none
Use cases referencing this story: none
SME notes:
- Indian GST rate tiers for hospitality (notification 11/2017-CT-Rate as amended, eff. 18-Jul-2022): room tariff <₹1,000/night → exempt (0%); ₹1,001–₹7,500 → 12%; >₹7,500 → 18%. Restaurant: 5% (without ITC) standalone or 18% if attached to a hotel >₹7,500/night room. Spec line 43 captures this. Code does NOT — no rate-tier resolver exists; each charge-posting site uses its own number (default 18% in
einvoice.service.tsline 25:DEFAULT_GST_RATE = 18). - Risk: a room sold at ₹999 with the wrong default 18% rate creates a CGST+SGST overcollection — when the guest disputes, refund is the only fix and the GST liability already filed must be reversed via credit note (US-012C-01 D-009 issue).
- Inclusive vs exclusive flag exists on
TaxConfiguration(AP-T02-005) but is silent on the rate plan side — see EP-002 D-008 for the related drift. - Migration risk:
20260523120000_add_folio_transaction_gst_splitis the column-add migration. Frontdesk CLAUDE.md Gotcha 29 calls out that pre-2026-05-23 the columns existed only inschema.prisma— a freshmigrate deploywould crash on first POS charge. CI environments that ranprisma db pushpapered over the drift — risk: a clean-slate prod deploy without this migration would corrupt every POS-driven GST split.
Drift findings:
D-010 — [P1] [Story-incomplete + India-compliance-critical] No central rate-tier resolver. DEFAULT_GST_RATE = 18 in einvoice + per-site rate selection in folios/POS means tariff-band logic is implicit, not enforced. A room sold at the ₹999 / ₹1,001 / ₹7,500 / ₹7,501 boundary can be mis-classified silently.
- [SME-PROPOSED] Author
@hmp/common/src/gst/rate-tier-resolver.tsexportingresolveGstRateTier(roomTariffPerNight: number, supplyType: 'ACCOMMODATION'|'RESTAURANT'|'LAUNDRY'|'SPA', restaurantAttachedToLuxuryHotel: boolean): {rate: 0|5|12|18, sacCode: string, basis: string}. Wire as the single source for rate selection in: folios.service.postCharge, pos-folio-charge.consumer, einvoice.service.generateIrnForFolio. Add 12 specs covering all boundary cases + amendment-effective-date variants.
US-012C-03 — Regional Tax Management (Luxury Tax / Tourism Levy)
Catalog status: Not Started (P1, 5 points) Verified status: Not Started
Code locations: NONE FOUND
Unit + integration tests: NONE FOUND
Flow tests: NONE FOUND
Real-backend smokes: NONE FOUND
Use cases referencing this story: none
SME notes:
- "Not Started" is honest — no
RegionalTaxmodel, no UI, no folio integration. Spec line 209-227 calls out Goa tourism levy (5%), Maharashtra luxury tax (tariff >₹7,500), Rajasthan tourism development fee as examples. - Regional taxes are India-specific levies in addition to GST — they appear as separate folio line items and on e-invoice but are NOT part of GSTR-1/3B (those are GST-only). A property in Goa today using HMP cannot legally invoice — the tourism levy column is missing.
- Property #1 readiness: this story is launch-critical IF property is in any of the regional-tax states (Goa, Maharashtra, Rajasthan, J&K, several others). Spec marks it P1 — should be P0 for any non-Karnataka/Delhi/UP launch.
Drift findings: (none net new — status matches reality)
- [SME-PROPOSED] Add AC: "Regional tax SHALL be configurable per property with
(stateCode, taxName, taxCode, basis: 'PERCENT'|'FLAT', value, applicableTariffBand?, hsnCode?). Applied at folio charge-posting time as a separateFolioTransactionrow withtransactionType='TAX_REGIONAL'. Shown as a separate line on invoice + e-invoice. Bumped to P0 for any launch in Goa/Maharashtra/Rajasthan/J&K." - [SME-PROPOSED] Add UC
uc-regional-tax-goa-tourism-levy-SME-PROPOSED.yaml: given property in Goa with 5% tourism levy configured, when room charge ₹8,000 posted, then folio shows ₹8,000 room + ₹1,440 GST 18% + ₹400 tourism levy + ₹9,840 total; tourism levy appears as separate line on e-invoice; NOT included in GSTR-1 GST totals.
US-012C-04 — Compliance Calendar & Due-Date Alerts
Catalog status: Not Started (P1, 5 points) Verified status: Not Started
Code locations: NONE FOUND
Unit + integration tests: NONE FOUND
Flow tests: NONE FOUND
Real-backend smokes: NONE FOUND
Use cases referencing this story: none
SME notes:
- No filing-deadline reminder system. Tenant relies on accountant or memory. GSTR-1 by 11th, GSTR-3B by 20th, TDS by 7th (most sections), TCS by 10th, Form 26AS reconciliation quarterly, e-invoice IRN generation 24h cancellation window, professional tax monthly, ESIC/EPF 15th.
- Closest existing capability: analytics-service
ReportSchedulerService(BullMQ cron) — could schedule a monthly "GSTR-1 reminder" notification to the Finance Manager via DistributionService (email/WhatsApp). No business code yet — just infra.
Drift findings: (none — story-vs-reality matches: Not Started)
- [SME-PROPOSED] Add AC: "Compliance calendar SHALL surface upcoming due dates (next 30 days) for: GSTR-1, GSTR-3B, TDS (per section), TCS, e-invoice IRN cancellation window (per IRN), Form 26AS reconciliation, professional tax, ESIC, EPF. Alerts SHALL fire T-7d + T-3d + T-1d via notification-service email + dashboard banner."
US-012C-05 — STR Data Export
Catalog status: Not Started (P3, 8 points) Verified status: Not Started
Code locations: NONE FOUND
Unit + integration tests: NONE FOUND
Flow tests: NONE FOUND
Real-backend smokes: NONE FOUND
Use cases referencing this story: none
SME notes:
- STR (Smith Travel Research) Destination Report format: CSV with columns Property ID / Date / Rooms Available / Rooms Sold / Occupancy% / ADR / RevPAR. HMP has these metrics natively (
MvDailyKpi+MvDailyOccupancy); a generator is one newstr-export.definition.tsaway. - P3 priority correct — STR is a paid subscription product opt-in, not regulatory. Indian Tier-2/3 hotels rarely participate; major chains (Marriott, IHG, Hyatt) require it via brand contract.
Drift findings: (none — Not Started matches)
US-012C-06 — IGST for Inter-State Bookings
Catalog status: Partial (P1, 5 points) Verified status: Partial — IGST split correctly applied at folio post-time (AP-T07-004 derive logic); B2C inter-state path defaults to property state (US-012-005 review GAP-2)
Code locations:
hmp-frontdesk-service/src/folios/consumers/pos-folio-charge.consumer.ts:341-348—deriveGstSplit(): property GSTIN/state first 2 digits compared to customer state → intra (CGST+SGST) vs inter (IGST). Honours explicit producer split.hmp-frontdesk-service/src/folios/__tests__/gsa-067-igst-split.spec.ts— covers IGST vs CGST+SGSThmp-frontdesk-service/src/einvoice/einvoice.service.ts:53-87—generateIrnForFolio(B2B path AP-T08-003):buyerDetails.stateCodederived from company GSTIN first 2 digits, enabling inter-state IGST when different from property statehmp-analytics-service/src/gstr/gstr.service.ts— aggregates IGST/CGST/SGST per folio event intoMvGstr3bSnapshot
Unit + integration tests:
gsa-067-igst-split.spec.ts(frontdesk)- AP-T07-004 9-case suite in
pos-folio-charge.consumer.spec.ts - B2B path 5 specs (AP-T08-003)
Flow tests:
gst-tax-flags.flow.spec.tscovers tax flags but not the inter-state path explicitly
Real-backend smokes: none
Use cases referencing this story:
- task brief cites
uc-frontdesk-einvoice-b2b.yaml— does not exist (D-002)
SME notes:
- B2B path works — company GSTIN drives the stateCode; inter-state IGST applies cleanly.
- B2C path is buggy — per US-012-005 review GAP-2, B2C state code in
Gstr1ExportServiceusesproperty.state.substring(0, 2)(supplier state), NOT guest's billing state (place-of-supply). For a property in Maharashtra receiving a B2C guest from Karnataka, the supply is inter-state per Sec 7 IGST Act and IGST should apply — current code emits CGST+SGST against Maharashtra state code regardless. Risk: input-tax-credit mismatch on the guest side (if business traveller later expenses); state revenue allocation incorrect. - Guest billing-state field exists on
Guest(cross-service read model in frontdesk) but the GSTR-1 export does not read it. Fix: same place D-003 retiresGstr1ExportService— the analytics-sidegst-reportv2 reads guest country/state from FU-55FOLIO_POSTEDpayload (countryCodeenriched).
Drift findings:
D-011 — [P1] [Story-incomplete + India-compliance-critical] B2C inter-state IGST path missing. Property in Maharashtra invoicing a Karnataka guest's walk-in stay emits CGST+SGST instead of IGST. Affects both per-transaction tax calc AND GSTR-1 Section 7 (B2C inter-state) classification.
- [SME-PROPOSED] Add AC: "B2C charges SHALL resolve place-of-supply from the guest's billing address state (frontdesk
Guest.billingStateread-model field synced from guest-service). When billing state ≠ property state, IGST SHALL apply (full rate, no CGST/SGST). When billing state is null, logB2C-POS-FALLBACK-TO-PROPERTYand use property state with a 7-day ops review SLA to populate the billing state." - [SME-PROPOSED] Add 5 specs to
gsa-067-igst-split.spec.ts: B2C intra-state → CGST+SGST; B2C inter-state with billing state → IGST; B2C null billing state → CGST+SGST + warning log; B2B unregistered (URP) inter-state → IGST per company-state-fallback; B2B null company stateCode → fail withIRN-BLOCKED-MISSING-COMPANY-STATE.