Skip to main content

EP-005B: Folios & Billing

Parent Epic: EP-005 Front Desk Operations Split Note: EP-005 has been split into sub-epics EP-005A, EP-005B, EP-005C, EP-005D. This file covers folio management, invoice generation, payment recording, refunds, and company/city ledger.

FieldValue
Epic IDEP-005B
Epic NameFolios & Billing
Parent EpicEP-005 Front Desk Operations
OwnerFront Desk Module Lead
Created2026-03-27
Last Updated2026-03-27
StatusIn Progress
PriorityP0 - Critical

1. Business Objective​

Eliminate the 2-4% revenue leakage that Indian hotels typically experience from manual billing errors, missed room charges, and unrecorded minibar items. Provide a single, GST-compliant folio system that tracks every rupee from check-in to check-out, supports complex scenarios (split bills, company billing, folio merges), and generates IRN-stamped e-invoices that guests can claim for ITC. For a 100-room hotel doing ₹80L/month, even 1% leakage = ₹80,000/month lost.


2. Audience​

RoleInteraction
Front Desk AgentPost charges, record payments, transfer items, split folios
Front Desk ManagerVoid transactions, override rates, approve refunds, lock folios
Finance ManagerReconcile folios, manage company ledger, review aging
GuestView folio on kiosk/web, receive e-invoice via email/WhatsApp

3. Scope​

3.1 In Scope​

  • Folio lifecycle: create, view, post charges, record payments, void, transfer, split, merge, lock, settle
  • Charge types: room charge (auto-posted), minibar, room service, laundry, telephone, damages, sundry
  • Payment methods: cash (INR), card (Razorpay), UPI/QR (dynamic QR generation), NEFT/bank transfer, city ledger
  • GST e-invoice generation for B2B and B2C guests (IRN via NIC sandbox / mock)
  • Credit note / cancellation invoice for void charges and refunds
  • E-invoice with GSTIN validation on company bookings
  • Folio split: separate items to guest-pay vs. company-pay sub-folios
  • Folio merge: combine two guest folios (e.g., consecutive stays, shared room)
  • Folio transfer: move line items between folios (e.g., minibar to incidentals folio)
  • Company / city ledger: bill-to-company postings, credit limit management, monthly statement
  • Refund management: partial and full refunds, Razorpay refund API call, cash refund ledger
  • UPI QR payment: dynamic QR per folio with auto-reconciliation on payment notification
  • Cash drawer integration (ESC/POS printer stub, petty cash log)
  • Folio PDF export and email dispatch
  • Idempotent payment creation (prevent duplicate charge on retry)

3.2 Out of Scope​

  • Check-in / check-out workflow (EP-005A)
  • Night audit rate posting (EP-005C)
  • POS outlet billing (EP-007)
  • Banquet/event billing (EP-008)
  • Full ERP / Tally integration (EP-020)

4. Current Implementation Status​

4.1 Backend — frontdesk-service​

Controller / EndpointStatusNotes
POST /foliosBuiltCreate folio for reservation
GET /folios/:idBuiltFull folio with line items, payments, balance
POST /folios/:id/chargesBuiltPost any charge type with GST breakdown
POST /folios/:id/paymentsBuiltRecord payment; idempotent via paymentRef
POST /folios/:id/voidBuiltVoid charge, creates reversal line item
POST /folios/:id/transferBuiltTransfer items to another folio
POST /folios/:id/splitBuiltSplit into two folios by item selection
POST /folios/mergeBuiltMerge two folios (Phase 5 deliverable)
POST /folios/:id/settleBuiltZero balance and lock folio
POST /folios/:id/lockBuiltLock folio (no further charges); requires manager role
POST /einvoice/generateBuiltIRN generation via NIC mock; GSTIN validation on B2B
POST /einvoice/:id/cancelBuiltCredit note / cancellation invoice
GET /einvoice/:idBuiltRetrieve IRN details and QR code
POST /folios/:id/refundPartialInitiates Razorpay refund; cash refund ledger not yet built
GET /company-ledgerNot StartedCompany account listing and aging
POST /company-ledger/:id/postNot StartedPost charge to company city ledger
GET /company-ledger/:id/statementNot StartedMonthly statement PDF
POST /folios/:id/upi-qrPartialQR generation works; auto-reconciliation webhook pending
ESC/POS receipt printingBuiltTemplate in libs/common/src/lib/printer/

4.2 Dashboard — 02-hmp-web-dashboard​

ComponentStatusNotes
Folio PageBuiltCharges, payments, transfers, PDF export
Folio Management PageBuiltPhase 5: folio list, merge, split UI
E-Invoice generation buttonBuiltTriggers IRN and sends to guest email
Company Ledger PageNot Started-
Refund Management UIPartialRazorpay refunds only; cash refund form missing

5. Gap Analysis​

#GapImpactEffortPriority
1Company / city ledger APIs not builtHighMP1
2Cash refund ledger missing (only Razorpay refund is wired)MediumSP1
3UPI QR auto-reconciliation webhook not implementedMediumSP1
4GST GSTR-1 line items from folios need validated schemaHighMP0
5Folio lock requires manager RBAC check (role check missing)LowSP2

6. User Stories​

Story IDTitlePriorityPointsSprintStatus
US-005-03Folio Creation & ChargesP051Built
US-005-04Folio Payment RecordingP051Built
US-005-05Folio Transfer & SplitP052Built
US-005-06Folio SettlementP052Built
US-005-11GST E-Invoice GenerationP083Built
US-005B-01Folio MergeP154Built
US-005B-02Folio Void & Credit NoteP153Built
US-005B-03Folio Lock (Manager Approval)P234Partial
US-005B-04Company / City LedgerP184Not Started
US-005B-05Refund ManagementP154Partial
US-005B-06UPI QR Payment & Auto-ReconcileP154Partial
US-005B-07Minibar & Room Service Charge PostP133Built
US-005B-08Folio PDF Export & EmailP133Built

7. Detailed User Stories​


US-005-03: Folio Creation & Charges​

As a Front Desk Agent, I want to post charges to a guest folio during their stay, So that all items are captured accurately for settlement at check-out.

Priority: P0 | Points: 5 | Sprint: 1 | Status: Built

Implementation Status​

POST /folios/:id/charges accepts { chargeType, amount, taxCode, description, qty }. Automatically calculates CGST + SGST (or IGST for inter-state). Room charges are auto-posted by the night audit engine.

Acceptance Criteria​

Scenario: Post minibar charge to folio
Given guest "Rajesh Sharma" has folio F-1001
When the agent posts a minibar charge: "Kingfisher Beer x2" at INR 400
Then the folio shows a charge of INR 400 + GST (5%) = INR 420
And the line item timestamp is recorded

Scenario: Auto room charge posting
Given a room rate of INR 5,000/night with 12% GST
When night audit runs at 00:01
Then a room charge of INR 5,600 (INR 5,000 + INR 600 GST) is posted to the folio

US-005-11: GST E-Invoice Generation​

As a Front Desk Agent, I want to generate a GST-compliant e-invoice with a valid IRN number at check-out, So that business guests can claim Input Tax Credit and the hotel meets statutory compliance.

Priority: P0 | Points: 8 | Sprint: 3 | Status: Built

Implementation Status​

EInvoiceModule in frontdesk-service generates IRN via NIC sandbox (or mock when EINVOICE_MOCK=true). Supports B2B (GSTIN validation) and B2C. Credit notes for cancellations. SAC code 9963 for accommodation, 996331 for restaurant (if combined folio).

Acceptance Criteria​

Scenario: B2B e-invoice with valid GSTIN
Given guest company "Tata Consultancy Services" with GSTIN "27AAACT2727Q1ZW"
When agent generates invoice at check-out for INR 12,000
Then IRN is generated and embedded as QR code in the PDF
And invoice includes: Seller GSTIN, Buyer GSTIN, HSN/SAC codes, CGST, SGST
And IRN is registered within 30 seconds

Scenario: B2C invoice (no GSTIN)
Given guest has no company GSTIN
When invoice is generated
Then a B2C tax invoice is issued without buyer GSTIN
And a QR code with invoice summary is included (as per e-invoice regulations)

US-005B-04: Company / City Ledger​

As a Finance Manager, I want to manage a company city ledger where corporate guests' bills are charged directly to the company account and settled monthly, So that corporate clients can receive consolidated invoices and the hotel can manage credit risk.

Priority: P1 | Points: 8 | Sprint: 4 | Status: Not Started

Implementation Notes​

  • New CompanyLedgerModule in frontdesk-service
  • POST /company-ledger — create company account (name, GSTIN, credit limit, payment terms)
  • POST /company-ledger/:id/post — move folio balance to company account on checkout
  • GET /company-ledger/:id/aging — aging buckets: current, 30d, 60d, 90d+
  • GET /company-ledger/:id/statement — PDF statement with all pending invoices
  • GSTIN validated using @IsGSTIN() decorator from libs/common

Acceptance Criteria​

Scenario: Post check-out bill to company account
Given company "Infosys Ltd" has a city ledger account with credit limit INR 5,00,000
And guest "Vikram Nair" checks out with folio balance INR 18,000
When agent selects "Post to Infosys City Ledger"
Then folio is marked settled
And INR 18,000 is added to Infosys account receivable
And remaining credit limit updates to INR 4,82,000

Scenario: Credit limit exceeded
Given Infosys account has INR 4,90,000 outstanding (limit INR 5,00,000)
When agent tries to post INR 18,000 more
Then system warns "Credit limit exceeded by INR 8,000"
And requires Finance Manager approval to proceed

US-005B-05: Refund Management​

As a Front Desk Manager, I want to process full or partial refunds for guests, So that revenue adjustments are tracked, Razorpay credits are issued, and the cash drawer balances correctly.

Priority: P1 | Points: 5 | Sprint: 4 | Status: Partial

Implementation Status​

Razorpay refund API (refunds.create()) is wired. Cash refund posting to a petty cash ledger and manager approval workflow are not yet implemented.

Acceptance Criteria​

Scenario: Card refund via Razorpay
Given guest paid INR 3,000 by card (payment_id: "pay_abc123")
When manager approves partial refund of INR 1,000
Then Razorpay refund of INR 1,000 is initiated
And folio shows credit line item "Refund" -INR 1,000
And confirmation shows refund reference and expected credit timeline (5-7 days)

Scenario: Cash refund
Given guest paid INR 3,000 cash and overpaid by INR 500
When manager processes cash refund of INR 500
Then petty cash log entry is created: "Refund to guest, folio F-1001, INR 500"
And cash drawer balance decreases by INR 500

8. Dependencies​

DependencyTypeEpic / ServiceStatus
Reservation (folio link)UpstreamEP-003 / reservationAvailable
Auth (manager role for void/lock)UpstreamEP-001 / authAvailable
Razorpay payment gatewayExternallibs/integrationsBuilt
NIC e-invoice API (IRN generation)ExternalEInvoiceModule (mock)Built
GSTIN validatorInternallibs/commonBuilt
SAC code libraryInternallibs/commonBuilt
ESC/POS printerInternallibs/commonBuilt
Night audit (auto room charge)SiblingEP-005C / frontdeskAvailable
Kafka (payment events)InfraDocker / KafkaAvailable

9. Acceptance Criteria (Sub-Epic Level)​

  1. AC-EP-005B-01: All folio operations (charge, payment, void, transfer, split, merge, settle) produce an accurate balance with a full audit trail.
  2. AC-EP-005B-02: GST e-invoices are generated with valid IRN (or mock IRN in demo mode) within 30 seconds of request.
  3. AC-EP-005B-03: B2B invoices validate GSTIN format using the Luhn-mod-36 checksum from libs/common.
  4. AC-EP-005B-04: Idempotent payment creation prevents duplicate charges when the client retries on timeout.
  5. AC-EP-005B-05: Company city ledger enforces credit limits and generates monthly PDF statements.
  6. AC-EP-005B-06: Refunds (card and cash) are tracked in the folio with manager approval audit trail.

10. Technical Notes​

  • Folio controller: 01-hmp-backend/apps/frontdesk-service/src/folio/folio.controller.ts
  • E-invoice module: 01-hmp-backend/apps/frontdesk-service/src/einvoice/
  • Razorpay adapter: 01-hmp-backend/libs/integrations/src/lib/payment/razorpay/ — uses @Integration decorator; EINVOICE_MOCK=true bypasses NIC API call
  • GSTIN validator: 01-hmp-backend/libs/common/src/lib/gstin/ — @IsGSTIN() class-validator decorator
  • SAC codes: 01-hmp-backend/libs/common/src/lib/sac-codes/ — 18 hospitality codes, getSacCodeForService() helper
  • ESC/POS printer: 01-hmp-backend/libs/common/src/lib/printer/ — folio and POS receipt templates
  • Folio Kafka event topics: FOLIO_CHARGE_POSTED, FOLIO_PAYMENT_RECEIVED, FOLIO_SETTLED, FOLIO_TRANSFERRED (Phase 6.3 event constants)
  • Razorpay uses paise internally — all HMP amounts are in INR; adapter divides by 100 before calling Razorpay (fixed in EP-015 implementation report)