এই ভলিউমে · ভলিউম 03
গ্রাহক ও সরবরাহকারী
গ্রাহক সরবরাহকারী
বাংলা সারসংক্ষেপ

গ্রাহক মাস্টার ডেটা: কর্পোরেট অ্যাকাউন্ট, ভ্রমণকারী, এজেন্সি অ্যাকাউন্ট। সংরক্ষিত: যোগাযোগ, KYC, ক্রেডিট লিমিট, পেমেন্ট টার্ম। স্যাংশন্স স্ক্রিনিং স্বয়ংক্রিয়। ক্রেডিট হোল্ডে বুকিং পেমেন্ট ছাড়া তৈরি যাবে না।

Chapter 3.1 — Customers

1. Purpose

This chapter defines the Customer entity in travoBooks — the party to whom the partner sells travel services. It covers the customer master record, the corporate hierarchy model (companies → branches → travellers), credit limits and credit hold behaviour, customer-side accounting impact (AR), KYC capture, and the full lifecycle from creation through dormancy and archival.

In travoBooks terminology: - Customer = the buyer of travel from the partner (a corporate client, a retail walk-in, a travel agency sub-account, or an OTA end-user). - Traveller = the person who actually travels; may differ from customer (a corporate buys, an employee flies). - Supplier is the inverse role (airline, hotel, GDS) — covered in Chapter 3.2.

2. Why it matters in modern travel accounting

The customer record is the left side of the platform's accounts-receivable picture. Every invoice, receipt, credit note, and dunning escalation traces back to a customer record. The quality of this data determines: - whether the partner can collect on credit sales (matched payment → invoice → customer) - whether revenue is recognised against the right counterparty for IFRS 15 - whether VAT/GST invoicing meets local jurisdictional requirements (buyer tax ID, registered address) - whether AML / sanctions screening can be performed before issuance

A weak customer master breaks every downstream module.

3. Industry relevance

Travel agencies typically serve mixed customer bases: - Retail walk-ins — cash-equivalent, low data, single transaction. - Corporate accounts — credit terms, monthly invoicing, cost-centre reporting, travel-policy enforcement. - Sub-agencies / IATA cascade — wholesale customers, net-rate sales, BSP impact. - OTA / online buyer — high volume, low touch, gateway payments. - Group / event clients — one-off but large, custom invoice templates.

travoBooks' customer model accommodates all of these through a single record with type-discriminated behaviour.

4. Compliance considerations

  • IFRS 15 — customer is the legal counterparty of the performance obligation; revenue is recognised against this entity.
  • VAT / GST — invoice must show buyer's legal name, tax registration ID, and registered address (jurisdiction-dependent).
  • AML / sanctions — corporate customers must be screened against OFAC, UN, EU, and local lists at onboarding and re-screened periodically.
  • GDPR / data-protection — PII captured for travellers must have lawful basis; deletion-on-request workflow.
  • IATA Resolution 850m / TAAP — for sub-agency customers, additional accreditation data may be required.

5. Business logic

5.1 Customer types

Type Code Behaviour
Walk-in (Cash) WALKIN Default to immediate payment; minimal KYC
Corporate CORPORATE Credit terms, monthly statements, cost-centre tracking
Sub-Agency SUBAGENT Wholesale pricing, net rates, may have own customers
OTA / Online End-User OTA_END_USER High volume, gateway payment only, light record
Group GROUP One-off large bookings; custom invoice
Internal / Staff INTERNAL Used for staff travel; separate AR account

5.2 Customer record structure

Field Type Notes
customer_id BIGINT PK Stable internal ID
partner_id BIGINT FK Tenant scope
customer_code VARCHAR(32) Partner-visible code; unique per partner
customer_type ENUM See above
legal_name VARCHAR(255) For invoicing
display_name VARCHAR(255) UI-friendly
tax_id VARCHAR(64) VAT / GST / BIN
tax_jurisdiction_code CHAR(2) ISO-3166
billing_email VARCHAR(255) Primary invoice recipient
billing_address TEXT Structured address sub-fields
default_currency CHAR(3) Invoice currency default
payment_terms_days SMALLINT NET-N days
credit_limit DECIMAL(18,2) In partner functional currency
credit_limit_currency CHAR(3) Usually partner functional
credit_hold BOOLEAN Manual block flag
pricing_profile_id BIGINT FK Markup / discount rules
cost_centre_required BOOLEAN Force cost-centre on bookings
parent_customer_id BIGINT FK NULL For branch-of-company hierarchy
kyc_status ENUM(pending, verified, failed, expired)
sanctions_screen_at DATETIME Last screening
sanctions_status ENUM(clear, hit_pending_review, hit_blocked)
status ENUM(active, dormant, suspended, archived) Lifecycle
notes TEXT Internal
created_at, updated_at, created_by, updated_by Audit columns

5.3 Customer hierarchy

Corporate customers can have branches — a parent_customer_id self-reference creates a tree:

Beta Corporation (parent)
├── Beta Corp — Dhaka HQ
├── Beta Corp — Chittagong
└── Beta Corp — Singapore
    └── Beta Corp — Singapore IT Dept (cost centre as branch)

Behaviour: - Invoices may be issued to branch or parent based on customer-config flag invoice_to. - Credit limit may be shared (parent enforces) or per-branch (each enforces its own). - Statements roll up from children to parent on partner-config flag.

5.4 Credit management

A customer with customer_type = CORPORATE typically operates on credit. The platform enforces:

Check Logic
Credit limit check at booking outstanding_ar + booking_total ≤ credit_limit
Credit hold If credit_hold = TRUE, no new credit sales accepted
Overdue threshold If oldest open invoice > payment_terms_days + grace_days, optional auto credit hold
Sanctions block If sanctions_status = hit_blocked, no transactions at all
KYC block If kyc_status IN (failed, expired) and partner.kyc_strict = TRUE, block

Credit limit can be temporarily exceeded via the credit override workflow (maker-checker; see Ch 2.4).

Outstanding AR per customer is a real-time materialised value updated by the JE engine — every INSERT into journal_entry_lines against AR control with customer_id adjusts the customer_balances table in the same transaction.

5.5 Pricing profiles

A customer may be linked to a pricing_profile_id that overrides default markup rules: - Markup type — fixed, percentage, tiered - Per-product overrides — air, hotel, transfer - Negotiated supplier rates — agreed net rates per supplier - Discount eligibility — flat discount or promo-code allow-list

The pricing profile is read at booking-time by the offer-pricing engine.

6. Inputs → processing → outputs

Create customer

Input: form / API payload with the fields above.

Processing: 1. Validate uniqueness of (partner_id, customer_code) and (partner_id, tax_id) if provided. 2. Resolve parent_customer_id if present; verify same partner_id. 3. Insert customers row. 4. Trigger sanctions screening async (queue job). 5. Initialise customer_balances row with zero balance. 6. Emit customer.created event for webhooks.

Output: customer_id, customer_code, redirect to detail page.

Update credit limit

Input: {customer_id, new_credit_limit, reason}

Processing: 1. Permission check (customers.credit.update.partner or .branch:{id}). 2. If increase exceeds threshold (e.g., > BDT 1,000,000), require maker-checker. 3. Write customer_credit_history row capturing old/new/reason/actor. 4. Update customers.credit_limit. 5. Audit log entry. 6. Emit customer.credit_limit.changed event.

Output: Updated record.

7. Module dependencies

Direction Module
Depends on Multi-Tenancy (Ch 1.1), User Management (Ch 2.x), Tax Configuration (Ch 5.9)
Depended on by Bookings, Invoicing, Payments, Reporting, Webhooks, Dunning, Statements

8. Security & permissions

Permission Allows
customers.read.partner View any customer in the partner
customers.read.own View customers owned (created/managed) by the user
customers.create.partner Create new customer
customers.update.partner Edit master data
customers.credit.update.partner Adjust credit limit
customers.credit.override.partner Approve a single transaction breaching limit
customers.archive.partner Soft-archive
customers.delete.platform Hard-delete (platform admin only)

Tax IDs and KYC documents are sensitive — access logged separately for SOC 2.

9. Validation rules

Code Condition
CUSTOMER_CODE_DUPLICATE (partner_id, customer_code) already exists
CUSTOMER_TAX_ID_DUPLICATE Same tax_id used by another customer in the same partner
CUSTOMER_PARENT_DIFFERENT_PARTNER Parent customer belongs to a different partner
CUSTOMER_PARENT_CYCLE Hierarchy would create a cycle
CUSTOMER_INVALID_CURRENCY default_currency not in partner_currencies
CUSTOMER_NEGATIVE_CREDIT_LIMIT Credit limit < 0
CUSTOMER_KYC_REQUIRED Tries to transact while KYC pending in strict-mode partner
CUSTOMER_SANCTIONS_BLOCKED Sanctions screening flagged the record
CUSTOMER_ARCHIVED Operation on archived customer
CUSTOMER_CREDIT_EXCEEDED Booking would exceed credit limit without override
CUSTOMER_CREDIT_ON_HOLD Credit hold active
CUSTOMER_OVERDUE_LOCKED Auto credit hold from overdue policy

10. Error handling

Validation errors surface inline in the UI; API responses use standard 400 envelope:

{
  "error": {
    "code": "CUSTOMER_CODE_DUPLICATE",
    "message": "A customer with this code already exists.",
    "field": "customer_code",
    "details": { "existing_customer_id": 12345 }
  }
}

Credit-block at booking-time surfaces with the override pathway visible to authorised roles only.

11. Real-world examples

Example A — Onboarding a corporate customer

Beta Corporation onboarded: - customer_code = BETA-DHK-001 - legal_name = Beta Corporation Ltd. - tax_id = BD-BIN-123456789 (Bangladesh BIN) - default_currency = BDT - payment_terms_days = 30 - credit_limit = 5,000,000 BDT - Hierarchy: parent record only.

Sanctions screening: clear. KYC: verified after document upload.

First booking issued 5 days later: BDT 80,000 → JE posts a debit to AR-Trade (1101) with customer_id = beta_id and credit to revenue/supplier-payable.

Example B — Credit hold from overdue

Beta has three open invoices totalling BDT 320,000 at month-end; oldest is 42 days past due. Partner policy: auto credit hold at terms + 7 days.

At 06:00 the nightly job evaluates and flips credit_hold = TRUE, posts an audit entry, and emails the accountant + a courtesy notice to Beta's billing contact. New bookings now return CUSTOMER_CREDIT_ON_HOLD until a payment clears or override is granted.

Example C — Branch invoicing

Beta has 4 branches. Configuration: - invoice_to = branch - statement_rollup = parent

Each branch is invoiced separately (own AR sub-ledger). Statements consolidate to parent at month-end. AR aging report can be viewed at parent level (rolled) or branch level (granular).

12. Step-by-step workflow

flowchart TD A[Create Customer] --> B{Type?} B -->|Walk-in| C[Minimal KYC; Save] B -->|Corporate| D[Capture Tax ID + Address + Credit Terms] B -->|Sub-Agency| E[Capture Wholesale Pricing Profile] D --> F[Queue Sanctions Screening] E --> F F --> G{Screening Result} G -->|Clear| H[Status = Active] G -->|Hit| I[Status = Suspended → Manual Review] H --> J[Customer Available for Bookings] I --> K[Compliance Officer Reviews] K -->|Cleared| H K -->|Confirmed Hit| L[Archived; Cannot Transact]

13. Database tables touched

Table Role
customers Master record
customer_addresses Multiple addresses (billing, shipping, registered)
customer_contacts Multiple contact persons
customer_credit_history Audit of credit-limit changes
customer_balances Real-time AR balance, materialised
customer_kyc_documents Uploaded KYC artefacts (S3 refs)
customer_sanctions_screenings Screening run history
customer_pricing_profiles Negotiated rates
audit_logs Every change

14. Future scalability

  • Self-service customer portal (Phase 2) — corporates view their own invoices, statements, pay online.
  • API-driven onboarding — sub-agencies provisioned via partner API.
  • Continuous sanctions screening — daily delta-screen against updated watchlists, not just on creation.
  • Customer scoring — payment behaviour score influencing credit-policy automation.
  • Multi-partner customer linking (Phase 3) — same legal entity recognised across partners in a group for consolidated reporting.

15. Common pitfalls

  • ⚠️ Don't reuse a customer code across partners as if it were global. It's scoped to partner_id — same legal entity at two partners gets two records.
  • ⚠️ Don't update tax_id silently — invoices already issued under the old tax_id are immutable; new invoices use the new ID.
  • 🔒 KYC documents must never be returned in list responses. Detail-fetch only, with separate audit log.
  • ⚠️ Don't hard-delete customers with historical bookings. Archive only — financial records depend on the FK.
  • ⚠️ Currency at customer level is a default, not a constraint. A USD-default corporate may still buy a BDT ticket; the invoice currency is per-transaction.