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.
| Field | Value |
|---|---|
| Epic ID | EP-005B |
| Epic Name | Folios & Billing |
| Parent Epic | EP-005 Front Desk Operations |
| Owner | Front Desk Module Lead |
| Created | 2026-03-27 |
| Last Updated | 2026-03-27 |
| Status | In Progress |
| Priority | P0 - 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
| Role | Interaction |
|---|---|
| Front Desk Agent | Post charges, record payments, transfer items, split folios |
| Front Desk Manager | Void transactions, override rates, approve refunds, lock folios |
| Finance Manager | Reconcile folios, manage company ledger, review aging |
| Guest | View 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 / Endpoint | Status | Notes |
|---|---|---|
POST /folios | Built | Create folio for reservation |
GET /folios/:id | Built | Full folio with line items, payments, balance |
POST /folios/:id/charges | Built | Post any charge type with GST breakdown |
POST /folios/:id/payments | Built | Record payment; idempotent via paymentRef |
POST /folios/:id/void | Built | Void charge, creates reversal line item |
POST /folios/:id/transfer | Built | Transfer items to another folio |
POST /folios/:id/split | Built | Split into two folios by item selection |
POST /folios/merge | Built | Merge two folios (Phase 5 deliverable) |
POST /folios/:id/settle | Built | Zero balance and lock folio |
POST /folios/:id/lock | Built | Lock folio (no further charges); requires manager role |
POST /einvoice/generate | Built | IRN generation via NIC mock; GSTIN validation on B2B |
POST /einvoice/:id/cancel | Built | Credit note / cancellation invoice |
GET /einvoice/:id | Built | Retrieve IRN details and QR code |
POST /folios/:id/refund | Partial | Initiates Razorpay refund; cash refund ledger not yet built |
GET /company-ledger | Not Started | Company account listing and aging |
POST /company-ledger/:id/post | Not Started | Post charge to company city ledger |
GET /company-ledger/:id/statement | Not Started | Monthly statement PDF |
POST /folios/:id/upi-qr | Partial | QR generation works; auto-reconciliation webhook pending |
| ESC/POS receipt printing | Built | Template in libs/common/src/lib/printer/ |
4.2 Dashboard — 02-hmp-web-dashboard
| Component | Status | Notes |
|---|---|---|
| Folio Page | Built | Charges, payments, transfers, PDF export |
| Folio Management Page | Built | Phase 5: folio list, merge, split UI |
| E-Invoice generation button | Built | Triggers IRN and sends to guest email |
| Company Ledger Page | Not Started | - |
| Refund Management UI | Partial | Razorpay refunds only; cash refund form missing |
5. Gap Analysis
| # | Gap | Impact | Effort | Priority |
|---|---|---|---|---|
| 1 | Company / city ledger APIs not built | High | M | P1 |
| 2 | Cash refund ledger missing (only Razorpay refund is wired) | Medium | S | P1 |
| 3 | UPI QR auto-reconciliation webhook not implemented | Medium | S | P1 |
| 4 | GST GSTR-1 line items from folios need validated schema | High | M | P0 |
| 5 | Folio lock requires manager RBAC check (role check missing) | Low | S | P2 |
6. User Stories
| Story ID | Title | Priority | Points | Sprint | Status |
|---|---|---|---|---|---|
| US-005-03 | Folio Creation & Charges | P0 | 5 | 1 | Built |
| US-005-04 | Folio Payment Recording | P0 | 5 | 1 | Built |
| US-005-05 | Folio Transfer & Split | P0 | 5 | 2 | Built |
| US-005-06 | Folio Settlement | P0 | 5 | 2 | Built |
| US-005-11 | GST E-Invoice Generation | P0 | 8 | 3 | Built |
| US-005B-01 | Folio Merge | P1 | 5 | 4 | Built |
| US-005B-02 | Folio Void & Credit Note | P1 | 5 | 3 | Built |
| US-005B-03 | Folio Lock (Manager Approval) | P2 | 3 | 4 | Partial |
| US-005B-04 | Company / City Ledger | P1 | 8 | 4 | Not Started |
| US-005B-05 | Refund Management | P1 | 5 | 4 | Partial |
| US-005B-06 | UPI QR Payment & Auto-Reconcile | P1 | 5 | 4 | Partial |
| US-005B-07 | Minibar & Room Service Charge Post | P1 | 3 | 3 | Built |
| US-005B-08 | Folio PDF Export & Email | P1 | 3 | 3 | Built |
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
CompanyLedgerModulein 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 checkoutGET /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 fromlibs/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
| Dependency | Type | Epic / Service | Status |
|---|---|---|---|
| Reservation (folio link) | Upstream | EP-003 / reservation | Available |
| Auth (manager role for void/lock) | Upstream | EP-001 / auth | Available |
| Razorpay payment gateway | External | libs/integrations | Built |
| NIC e-invoice API (IRN generation) | External | EInvoiceModule (mock) | Built |
| GSTIN validator | Internal | libs/common | Built |
| SAC code library | Internal | libs/common | Built |
| ESC/POS printer | Internal | libs/common | Built |
| Night audit (auto room charge) | Sibling | EP-005C / frontdesk | Available |
| Kafka (payment events) | Infra | Docker / Kafka | Available |
9. Acceptance Criteria (Sub-Epic Level)
- AC-EP-005B-01: All folio operations (charge, payment, void, transfer, split, merge, settle) produce an accurate balance with a full audit trail.
- AC-EP-005B-02: GST e-invoices are generated with valid IRN (or mock IRN in demo mode) within 30 seconds of request.
- AC-EP-005B-03: B2B invoices validate GSTIN format using the Luhn-mod-36 checksum from
libs/common. - AC-EP-005B-04: Idempotent payment creation prevents duplicate charges when the client retries on timeout.
- AC-EP-005B-05: Company city ledger enforces credit limits and generates monthly PDF statements.
- 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@Integrationdecorator;EINVOICE_MOCK=truebypasses 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) Razorpayuses paise internally — all HMP amounts are in INR; adapter divides by 100 before calling Razorpay (fixed in EP-015 implementation report)