Skip to content

Sales

The Sales module is the customer-facing funnel: Leads → Quotations → Bookings → Customers. Everything a sales executive does before a customer becomes a paying traveller lives here.

Where it lives

All Sales pages sit under src/pages/sales/ and a few siblings:

  • src/pages/leads/Leads.tsx:65 — Leads pipeline (routed at /sales/leads)
  • src/pages/sales/Quotations.tsx — Quotations list + dialog
  • src/pages/sales/Bookings.tsx:68 — Bookings list + detail peek
  • src/pages/sales/BookingDetail.tsx:177 — Booking detail page
  • src/pages/sales/BookingWizard.tsx:4 — New-booking wizard wrapper
  • src/pages/sales/Customers.tsx — Customers master
  • src/pages/sales/bookingsMath.ts, customersMath.ts, quotationsMath.ts — pure helpers (stats, filters, patch-merge)

Routes (all under /sales/*, see src/App.tsx:158):

Route Component Permission
/sales/customers Customers customers.view
/sales/leads Leads leads.view
/sales/quotations Quotations quotations.view
/sales/bookings Bookings bookings.view
/sales/bookings/new BookingWizard bookings.create
/sales/bookings/:id BookingDetail bookings.view

1. The sales pipeline

flowchart LR
    Inquiry([Customer inquiry]) --> Lead[/Lead/]
    Lead -->|Contact + qualify| Qual{Qualified?}
    Qual -->|no| Closed[Close lead]
    Qual -->|yes| Convert{Convert path}
    Convert -->|Quote customer| Quote[/Quotation/]
    Convert -->|Skip quote| Book[/Booking/]
    Quote -->|Send to customer| Sent[sent]
    Sent -->|Accept| Accepted
    Sent -->|Expire / reject| Dead[rejected or expired]
    Accepted --> Book
    Book -->|Ops + Finance approve| Confirmed([Confirmed booking])
    Closed --> End([End])
    Dead --> End
    Confirmed --> End

Each step is a distinct page with its own permission, its own API surface, and its own status machine.

2. Leads (/sales/leads)

src/pages/leads/Leads.tsx:65 is a lightweight CRM for inbound inquiries — customers who called, WhatsApp'd, filled a web form, or walked in.

Fields captured on create

defaultLeadForm (Leads.tsx:54):

  • fullName, phone, email, city
  • travelTypeumrah, hajj, umrah_plus, custom (Leads.tsx:47)
  • groupSize
  • preferredMonth
  • message — freeform

Status pipeline

statusOptions (Leads.tsx:39):

Status Meaning
new Just captured; no outbound contact yet
contacted Rep has reached out
qualified Budget / intent confirmed; ready to quote
converted Became a quotation or booking
closed Dead lead (no-intent / duplicate / stale)

Actions

Action Permission Path
View page leads.view Route
Create lead leads.edit Header button
Update status leads.edit Detail dialog
Convert to quotation quotations.create Detail dialog → calls convertLeadToQuotation
Convert to booking bookings.create Detail dialog → calls convertLeadToBooking

Services: src/services/leadService.tsfetchLeads, createLead, updateLeadStatus, convertLeadToQuotation, convertLeadToBooking.

3. Quotations (/sales/quotations)

src/pages/sales/Quotations.tsx is the quote management workspace. A quotation is a non-binding price offer the customer accepts or rejects; accepted quotes convert to bookings.

Quotation form

Shape defined in QuotationFormData (Quotations.tsx:65):

  • customerId, groupId, agentId (if sold through a partner)
  • packageDetails:
    • flights[], hotels[], meals[] — inventory IDs + display labels (operators pick from dropdowns)
    • visa, duration, roomType, requestedAmount, specialRemarks
    • passengers[] — same shape as booking passengers
  • totalAmount, discount, currency
  • validUntil — expiry date (ISO)
  • statusdraft / sent / accepted / rejected / expired
  • notes

Status lifecycle

stateDiagram-v2
  [*] --> draft: Create
  draft --> sent: Send to customer
  sent --> accepted: Customer approves
  sent --> rejected: Customer declines
  sent --> expired: Past validUntil (auto)
  accepted --> [*]: Convert to booking
  rejected --> [*]
  expired --> [*]
  draft --> [*]: Delete

isQuotationExpired (src/pages/sales/quotationsMath.ts) is the client-side flag.

Actions

Action Permission Source
View list quotations.view Route
Create quotations.create Header button
Edit quotations.create (treat-as-edit) Row action
Send to customer quotations.create sendQuotation (Quotations.tsx:54, line 645)
Convert to booking bookings.create + quotations.create createBookingFromQuotation (Quotations.tsx:58, line 515) — also flips status to accepted if not already (Quotations.tsx:561)
Delete quotations.create Row action

Quotations permissions are under-split

Edit / send / delete all currently gate on quotations.create. docs/PERMISSIONS.md §6.3 marks this as a future split into quotations.edit / quotations.delete. Do not introduce new finer checks without also updating the matrix and seed migration.

Services: src/services/quotationService.tsfetchQuotations, createQuotation, updateQuotation, updateQuotationStatus, sendQuotation.

4. Bookings (/sales/bookings)

Full coverage: Bookings.

In short: the paying-customer record, created by the Booking Wizard (see Booking Wizard) or converted from a lead / quotation. Gated by bookings.* permissions.

5. Customers (/sales/customers)

src/pages/sales/Customers.tsx is the master list of every individual person Al Huda has ever transacted with. A customer row is the durable identity that multiple bookings and payments reference.

Key facets

  • Passport-based dedup — the primary natural key; the wizard and import dialog both use it to prevent duplicates.
  • Profile fields — name, DOB, gender, title, nationality (default INDIAN), full Indian address block, phone, email.
  • Hajj-specific fields — blood group, PAN, Aadhaar (populated from Hajj bookings, used on future bookings).
  • SourceDirect vs Through Business Partner (links to sourceAgentId).
  • Documents — Drive-backed uploads (passport, visa, tickets) via customers.edit.

Actions

Action Permission
View list + detail customers.view
Create customers.create
Edit customers.edit
Delete customers.delete
Upload / delete document customers.edit
View ledger customers.view
Export CSV customers.view
Import CSV customers.create

Services: src/services/customerService.ts.

6. How the Sales pages talk to each other

flowchart LR
  subgraph Leads page
    A[Lead row] -- Convert to Quote --> Q[Quotation]
    A -- Convert to Booking --> B[Booking]
  end
  subgraph Quotations page
    Q -- Convert (accepted) --> B
  end
  subgraph Customers page
    C[Customer master]
  end
  B --> C
  Q --> C
  A --> C
  • Lead conversion calls createQuotation or createBookingFromLead; both take the lead's captured fields and create a Customer row on the fly if the contact isn't already in the master.
  • Quotation → Booking uses createBookingFromQuotation, which copies passengers, rates, and payer onto a fresh Booking, then updates the quotation status to accepted (Quotations.tsx:515).
  • Every booking update that changes a customer-relevant field (e.g. phone) propagates back to the Customer row so the next booking starts from fresh data.

7. Permissions summary

The canonical source is docs/PERMISSIONS.md §6.1 through §6.4. Condensed:

Module view create edit delete
Leads leads.view leads.edit
Quotations quotations.view quotations.create quotations.create quotations.create
Bookings bookings.view bookings.create bookings.edit bookings.delete
Customers customers.view customers.create customers.edit customers.delete

Role bundles that carry full sales-module access: CEO, GM, IT_ADMIN, ADMIN_HR, SALES_MANAGER, B2B_MANAGER. SALES_EXEC and B2B_EXEC lack customers.delete but hold everything else in the funnel (see docs/PERMISSIONS.md §5.2).

8. What the Sales module does NOT contain

For developer context, so you don't go hunting in the wrong folder:

  • Finance (payments, vouchers, journals) lives under /financesrc/pages/finance/Finance.tsx. Payments are triggered from the Bookings list via the "Add Payment" button (gated by finance.create) but the posting handler lives in finance.
  • Approvals queue (for ops + finance to approve bookings) is under /approvalssrc/pages/approvals/Approvals.tsx. Sales routes them there via "Send Back" and "Resubmit".
  • Partners / agents master is under /agentssrc/pages/partners/Partners.tsx. Sales only selects an existing partner; creation / edit happens there.
  • Groups is a peer module, not a child of Sales — /groups. See Groups.