In this volume · VOLUME 04
Travel Operations
Booking Lifecycle Ticketing & PNR Refunds & Cancellations Voids, Reissues & Adjustments Non-Air Products ADM / ACM Memos

Chapter 4.1 — Booking Lifecycle

1. Purpose

The booking is the atomic unit of operational state in travoBooks. Every ticket issued, every hotel night reserved, every transfer scheduled, every invoice line that ultimately feeds the GL begins life as a booking. This chapter defines the full lifecycle — from search through hold, confirmation, issuance, in-life servicing, completion, and archival — and the precise state transitions, ledger impacts, validation rules, and module interactions at each step.

2. Why it matters in modern travel accounting

The booking is the bridge between the operational world (PNRs, segments, traveller names) and the financial world (revenue, AP/AR, deferred revenue, commission). A correctly modelled booking lifecycle: - preserves an auditable chain from search → offer → issuance → settlement - supports same-transaction ledger posting (no operational "ghost" state without matching financial state) - handles the messy reality of supplier-side state drift (PNR cancelled airline-side without notice) - supports refunds, voids, reissues, and partial cancellations without losing history

3. Industry relevance

The travoBooks booking model accommodates the full spectrum of travel inventory: - Air — GDS-issued (Amadeus, Sabre), LCC API, NDC direct, BSP-settled or non-BSP - Hotel — prepaid wholesaler, pay-at-property, direct contract - Ground — transfers, car rental - Insurance — per-policy - Tour / Package — multi-component - Ancillary — baggage, seat, meal, lounge

Each product type slots into the same lifecycle skeleton with type-specific behaviour at the issuance and servicing steps.

4. Compliance considerations

  • IFRS 15 — revenue recognition timing is driven by the booking's service-date field; air revenue recognised on flown-date, hotel on check-in-or-stay basis, packages on performance milestones.
  • IATA Resolution 850m — accuracy of ticket data, agent code on every transaction.
  • PCI DSS — no card data stored in booking records; only gateway tokens.
  • GDPR — traveller PII has lawful basis and retention schedule.
  • BSP rules — issuance must occur within the supplier's permitted timeframe; void window strictly enforced.

5. Business logic

5.1 Booking states

State Description Reversible? Ledger impact
DRAFT Search results selected, customer assigned, not yet held Yes (auto-expire 30m) None
HELD Supplier hold (PNR with TimeLimit; hotel option date) Yes (until timelimit) None
PENDING_PAYMENT Awaiting customer payment (gateway/cash) Yes None
PENDING_APPROVAL Above-threshold; awaiting approver Yes None
ISSUED Supplier confirmed; ticket / voucher created No (forward only) JE posted
PARTIALLY_USED Some segments flown / nights stayed No Revenue recognition progresses
COMPLETED All performance obligations satisfied No Final revenue recognition complete
CANCELLED_BEFORE_ISSUE Cancelled in DRAFT/HELD/PENDING N/A None
CANCELLED_AFTER_ISSUE Void or full refund No Reversing JE posted
PARTIALLY_REFUNDED Partial refund processed No Refund JE posted
EXPIRED Held but timelimit passed without action N/A None
ARCHIVED Completed / cancelled and older than retention threshold No None

5.2 Booking record structure

Field Type Notes
booking_id BIGINT PK Internal stable ID
partner_id BIGINT FK Tenant
booking_reference VARCHAR(16) Partner-facing, human-readable (TVB-2026-000123)
customer_id BIGINT FK Buyer
branch_id BIGINT FK NULL Selling branch
agent_user_id BIGINT FK Who created
cost_centre_id BIGINT FK NULL Customer cost centre tag
product_type ENUM(AIR, HOTEL, GROUND, INSURANCE, TOUR, ANCILLARY)
state ENUM Above
transaction_currency CHAR(3) Selling currency
gross_amount DECIMAL(18,2) Customer pays
net_supplier_amount DECIMAL(18,2) Supplier cost
markup_amount DECIMAL(18,2) Partner margin
commission_amount DECIMAL(18,2) Commission expected
tax_amount DECIMAL(18,2) Customer-facing tax
service_fee_amount DECIMAL(18,2) Partner service fee
service_date_start DATE First service day (flight depart / check-in)
service_date_end DATE Last service day (last segment / checkout)
payment_status ENUM(UNPAID, PARTIAL, PAID, REFUNDED) Computed
issued_at DATETIME NULL Set on transition to ISSUED
cancelled_at DATETIME NULL
completed_at DATETIME NULL
external_pnr VARCHAR(16) NULL Supplier locator
external_record_locator VARCHAR(64) NULL Hotel confirmation / NDC orderId
metadata JSON Type-specific payload
Audit columns

Child tables: booking_segments, booking_passengers, booking_tickets, booking_rooms, booking_services, booking_ancillaries, booking_history, booking_documents.

5.3 Booking → Ledger contract

The state transition PENDING → ISSUED is the single point of ledger entry for a new booking. In the same database transaction:

  1. Update bookings.state = 'ISSUED', set issued_at.
  2. Insert child booking_tickets / booking_rooms / etc.
  3. Compute JE lines using supplier principal_or_agent, supplier settlement_mode, customer payment terms, tax configuration.
  4. Insert balanced journal_entries header + journal_entry_lines.
  5. Update customer_balances (debit AR) and supplier_balances / BSP-payable (credit).
  6. Insert audit_logs event.
  7. Enqueue booking.issued webhook.

If any step fails, the entire transaction rolls back — no orphan operational state, no orphan ledger state. This is the architectural invariant of travoBooks.

5.4 Holds & timelimits

GDS PNRs and hotel options have supplier-side timelimits. travoBooks tracks hold_expires_at on the booking. A scheduled job sweeps: - 30 minutes before expiry: notify selling agent. - At expiry without action: cancel supplier-side, transition booking to EXPIRED.

This avoids "ADM for unticketed PNR" liability.

5.5 Payment-before-issuance

By default, travoBooks supports issue-then-collect (credit customers) and collect-then-issue (cash customers) per customer.payment_terms_days:

  • terms_days = 0 and customer_type = WALKIN → cash flow: PENDING_PAYMENT → payment captured → ISSUED.
  • terms_days > 0 and credit limit available → credit flow: PENDING_APPROVAL (if needed) → ISSUED → invoice → payment per terms.

5.6 Approval workflow

If gross_amount > partner.booking_approval_threshold or customer.credit_exceeded_without_override, booking enters PENDING_APPROVAL. Approver (with bookings.approve.partner) reviews, approves or rejects. Approval action transitions to ISSUED (if all else green) or back to DRAFT (rejected with reason).

5.7 In-life servicing

After ISSUED, several events can occur: - Flown segment — passive GDS event; updates booking_segments.status = 'FLOWN'; triggers partial revenue recognition (Ch 5.7). - No-show — supplier marks unused; affects refund eligibility, may trigger fee. - Reissue — exchange ticket; new JE for fare difference + reissue fee; preserves original FK chain. - Void — only within void window (typically same calendar day for BSP); reverses original JE in full. - Refund — full or partial; see Ch 4.3. - Schedule change / SKCHG — supplier-initiated; may force re-confirmation; usually no financial impact unless customer cancels in response.

5.8 Booking → Invoice relationship

Bookings do not directly create customer invoices. Invoice generation is a separate event governed by customer.invoice_policy: - per_booking_auto_issue — invoice issued immediately on booking issuance. - consolidated_weekly / consolidated_monthly — bookings accumulate; invoice generated on schedule. - on_demand — accountant runs invoice generation manually.

Until invoiced, the AR debit is recorded against 1102 — Unbilled AR (a sub-ledger of trade AR). On invoice creation, JE reclassifies from unbilled to billed.

This separation accommodates corporate clients who require one monthly invoice covering hundreds of bookings.

6. Inputs → processing → outputs

Create booking (search → issue)

Input flow: 1. Search request → offers returned from GDS/NDC/LCC/hotel cache. 2. Selection → POST /bookings with offer payload, customer_id, travellers. 3. Hold → call supplier Air_SellFromRecommendation or equivalent. 4. Price-verify (re-fetch fare) → check against quoted; if delta > tolerance, surface to agent. 5. Payment captured (if collect-first) OR credit available (if credit). 6. Issue → call supplier issuance API (Air_TicketIssue, Hotel_BookingConfirm). 7. Persist tickets / confirmations. 8. Post JE. 9. Generate documents (e-ticket, voucher). 10. Notify customer.

Output: booking_id, booking_reference, attached documents, JE references.

7. Module dependencies

Direction Module
Depends on Customers, Suppliers, Tax Config, Supplier Integrations (GDS/NDC/LCC/Hotel), Payment Gateway (if collect-first), JE Engine, Document Generator, Notifications
Depended on by Invoicing, Reporting, Reconciliation, Webhooks, Refund, Reissue, ADM/ACM, Deferred Revenue, Commission Accounting
graph LR SEARCH[Offer Search] --> BOOK[Booking] CUST[Customer Master] --> BOOK SUP[Supplier Master] --> BOOK TAX[Tax Config] --> BOOK BOOK --> JE[JE Engine] BOOK --> DOC[Document Gen] BOOK --> NOTIF[Notifications] BOOK --> INV[Invoice Engine] BOOK --> REF[Refund Module] BOOK --> REC[Reconciliation] BOOK --> WH[Webhooks] JE --> GL[(General Ledger)]

8. Security & permissions

Permission Allows
bookings.read.partner / .branch:{id} / .own Read
bookings.create.partner / .branch:{id} / .own Create
bookings.update.partner (limited fields) Edit non-financial fields
bookings.issue.partner Trigger ticketing
bookings.void.partner Same-day void
bookings.refund.partner Refund
bookings.reissue.partner Exchange ticket
bookings.approve.partner Approve PENDING_APPROVAL
bookings.override_credit.partner Issue past credit limit
bookings.cancel_after_issue.partner Post-issue cancellation

PCI scope is bounded — card data flows through gateway iframe / hosted page; only the gateway token persists.

9. Validation rules

Code Condition
BOOKING_CUSTOMER_REQUIRED No customer assigned
BOOKING_SUPPLIER_INACTIVE Selected supplier inactive
BOOKING_OFFER_EXPIRED Offer TTL expired (price-quote no longer valid)
BOOKING_PRICE_CHANGED Price-verify shows delta beyond tolerance
BOOKING_CREDIT_EXCEEDED Customer credit limit breached
BOOKING_CREDIT_HOLD Customer on credit hold
BOOKING_APPROVAL_REQUIRED Above approval threshold
BOOKING_HOLD_EXPIRED Acted on after timelimit
BOOKING_PAYMENT_REQUIRED Tries to issue without payment for cash customer
BOOKING_DUPLICATE_TRAVELLER Same traveller twice in same booking
BOOKING_PNR_NAME_MISMATCH Traveller name vs PNR mismatch
BOOKING_TICKET_TIMELIMIT_PAST Ticketing deadline missed
BOOKING_VOID_WINDOW_CLOSED Void attempted past supplier window
BOOKING_PERIOD_CLOSED Edit attempted on booking whose period is closed

10. Error handling

Errors at the supplier integration layer are mapped to canonical travoBooks codes (BOOKING_SUPPLIER_REJECTED) with the raw supplier error preserved in booking_history.supplier_response for support diagnosis. Customer-facing messages are friendly; agent-facing messages include the raw code.

Idempotency: every booking create / issue / refund call carries an Idempotency-Key header. Replays return the original result rather than creating duplicates — critical for resilience against network retries.

11. Real-world examples

Example A — Cash retail air booking

Walk-in customer buys a Biman DAC-CGP ticket for BDT 8,500.

  • DRAFT → HELD (PNR created)
  • Customer pays cash BDT 8,500 → PENDING_PAYMENT → ISSUED
  • Same transaction posts:
Account 🅓 Debit 🅒 Credit
1001 Cash on Hand 8,500
2011 BSP Payable BD 8,000
4031 Service Fee Revenue 500

(Service fee illustrative; real ticket fare structure varies.)

Example B — Corporate credit air booking

Beta Corp issues an Emirates DAC-DXB ticket for USD 600. - terms_days = 30, credit available, no approval needed. - DRAFT → HELD → ISSUED (single transition; no PENDING_PAYMENT). - Tax / commission / supplier breakdown per Chapter 5.1 worked example. - Customer balance increases by USD 600 (functional translation in JE). - Invoice generated on month-end consolidated cycle.

Example C — Same-day void

Agent ticketed BDT 12,000 EK-DAC-DXB at 10:00. Customer cancels at 14:00 same day. - Agent triggers void → supplier Air_CancelTST and BSP same-day void. - Original JE reversed in full (new JE with opposite sign, reverses_je_id set). - Booking transitions to CANCELLED_AFTER_ISSUE with reason VOIDED_SAME_DAY.

Example D — Schedule change → partial refund

EK reschedules customer's outbound flight by 6 hours; customer refuses. Partial refund per fare rules. - Agent processes refund → supplier Air_RefundQuote → confirmed amount. - Booking transitions to PARTIALLY_REFUNDED. - Refund JE: debit Supplier-Payable / credit AR (or Cash for customer refund); refund fees handled per fare rules; commission recall if applicable.

12. Step-by-step workflow

stateDiagram-v2 [*] --> DRAFT: Agent selects offer DRAFT --> HELD: Supplier hold acquired DRAFT --> CANCELLED_BEFORE_ISSUE: Agent abandons DRAFT --> EXPIRED: Auto-expire 30m HELD --> PENDING_PAYMENT: Cash customer HELD --> PENDING_APPROVAL: Over threshold HELD --> ISSUED: Credit customer, auto-approved PENDING_PAYMENT --> ISSUED: Payment captured PENDING_PAYMENT --> CANCELLED_BEFORE_ISSUE: Payment failed PENDING_APPROVAL --> ISSUED: Approved PENDING_APPROVAL --> DRAFT: Rejected HELD --> EXPIRED: Timelimit passed ISSUED --> PARTIALLY_USED: First segment flown PARTIALLY_USED --> COMPLETED: All used ISSUED --> COMPLETED: All used (single-segment) ISSUED --> CANCELLED_AFTER_ISSUE: Void window ISSUED --> PARTIALLY_REFUNDED: Partial refund PARTIALLY_REFUNDED --> CANCELLED_AFTER_ISSUE: Subsequent full refund COMPLETED --> ARCHIVED: Retention period elapsed CANCELLED_AFTER_ISSUE --> ARCHIVED: Retention period elapsed

13. Database tables touched

Table Role
bookings Main record
booking_segments Air segments (DEP/ARR/flight number/cabin/fare basis)
booking_passengers Travellers (subset of customers / standalone)
booking_tickets Ticket numbers (e-ticket)
booking_rooms Hotel rooms
booking_services Generic service rows (transfer, insurance, tour)
booking_ancillaries Bag, seat, meal
booking_history State transitions, every change
booking_documents Refs to S3 documents (e-ticket PDF, voucher)
journal_entries / journal_entry_lines Financial impact
customer_balances / supplier_balances Materialised

14. Future scalability

  • Booking import — bulk-import from external GDS mid-office systems during onboarding.
  • Group booking module — sub-passenger management for large groups (Phase 2).
  • Continuous booking enrichment — pull post-issuance updates from GDS PSS (refund waivers, change records).
  • Predictive risk scoring — flag bookings likely to refund / ADM at issuance.
  • Multi-currency wallet — partner-held customer credit balance per currency.

15. Common pitfalls

  • ⚠️ Never persist operational ISSUED state without the matching JE. Single transaction or nothing — this invariant is sacred.
  • ⚠️ Holds expire silently if you don't sweep. Implement the timelimit job before launching air booking.
  • ⚠️ Don't change customer/supplier on an issued booking. Cancel and re-issue with audit trail.
  • 🔒 Card data does not belong on the booking. Token only.
  • ⚠️ Service-date drives revenue timing. If service-date is wrong, revenue lands in the wrong period — common BSP-import bug.
  • ⚠️ Idempotency keys must be respected. A retried POST that creates a duplicate booking is a double-billing event.