Global Architecture & Enterprise Rules

Prepared: 26 May 2026 | Confidential

Global Database & Normalization Strategy

To support an enterprise-grade multi-tenant SaaS environment, these rules strictly apply across all 86 sub-modules.

1. Multi-Tenancy & Security
EVERY table (except global configs) MUST contain tenant_id (UUID) and branch_id (BIGINT). The API middleware layer automatically intercepts every request and injects WHERE tenant_id = ?. This prevents cross-tenant data leaks.
2. Audit & Immutability (IFRS Standard)
Financial records (Journals, Invoices, Receipts) are NEVER executed via SQL DELETE. They are updated to status='VOIDED'. All POST/PUT/DELETE API calls trigger an async write to sys_audit_logs recording JSON diffs.
3. Currency & Precision Math
FLOAT/DOUBLE are forbidden. Monetary fields use DECIMAL(19,4). Transactions store foreign_amount, exchange_rate, and base_amount. Ledgers balance strictly on base_amount.
4. Event-Driven Architecture
AR and AP modules do NOT write directly to the GL. They emit domain events (e.g., InvoicePostedEvent). A separate GoLang Ledger Service consumes these events to generate mathematically balanced Double-Entry journals.

Module 0: System, Security & Access Control

1. Module Purpose & Core Connectivity

Purpose: The foundational gatekeeper of the SaaS. It manages tenant provisioning, multi-branch physical layouts, irrefutable audit trails, user authentication (AuthN), and granular role-based authorization (AuthZ).

Travel Industry Relevance: Travel agencies operate across multiple branches (e.g., Dhaka HQ, Dubai Branch). A ticketing agent in Dhaka must not see Dubai's B2B invoices. Furthermore, travel systems are high-fraud environments; an immutable audit trail is required by IATA and financial auditors to track exactly who altered a ticket price.

Connectivity to Accounting & Currency:
core_tenants.base_currency is the mathematical anchor for the entire SaaS. If Mod 2 (AR) issues an invoice in USD, it queries Mod 7 (FX Rates) to convert it to the base currency defined here before posting to Mod 1 (GL).
Connectivity to Financial Periods:
The gl_fiscal_periods table acts as the master lock. By closing a period here, the system enforces GAAP/IFRS standards by physically blocking all APIs from writing or voiding records in that historical timeframe.

2. Complete Database Schema (System & Security)

A. Tenant, Branch & System Currency Schema

Table: core_tenants
tenant_idUUIDPK. Unique Agency ID.
company_nameVARCHAR(100)e.g., 'Global Travel Ltd'
base_currencyCHAR(3)e.g., 'BDT'. Drives all GL logic.
tax_reg_noVARCHAR(50)Used for Govt Tax Reports (Mod 12)
Table: core_branches
branch_idBIGINTPK, Auto-increment
tenant_idUUIDFK -> core_tenants
branch_codeVARCHAR(10)e.g., 'HQ-DAC', 'BR-DXB'
branch_nameVARCHAR(100)Physical location name
Table: gl_fiscal_periods
period_idBIGINTPK
tenant_idUUIDFK
start_dateDATEBoundary start
end_dateDATEBoundary end
statusENUM'OPEN', 'CLOSED'

B. RBAC (Role-Based Access Control) & Users

Table: sys_permissions (System Global)
perm_idVARCHAR(50)PK. e.g., 'ar.invoice.edit'
moduleVARCHAR(50)e.g., 'Accounts Receivable'
resourceVARCHAR(50)e.g., 'Invoice', 'Journal', 'Customer'
action_typeENUM'VIEW', 'CREATE', 'EDIT', 'DELETE', 'VOID', 'APPROVE'
descriptionVARCHAR(255)'Allows editing an existing AR invoice'
Table: sys_roles (Tenant Specific)
role_idBIGINTPK
tenant_idUUIDFK
role_nameVARCHAR(50)'Ticketing Agent', 'CFO'
hierarchy_levelINT1 (Admin) to 99 (View-only)
Table: sys_role_permissions (Mapping)
role_idBIGINTComposite PK / FK
perm_idVARCHAR(50)Composite PK / FK

C. Users, Auth & Audit

Table: sys_users
user_idBIGINTPK
tenant_idUUIDFK
role_idBIGINTFK -> sys_roles
emailVARCHAR(100)Idx: UNIQUE(tenant, email)
pwd_hashVARCHAR(255)Argon2 / Bcrypt
statusENUM'ACTIVE', 'LOCKED'
Table: sys_user_branches (Isolation)
user_idBIGINTComposite PK / FK
branch_idBIGINTComposite PK / FK
is_defaultBOOLEANDefault branch on login
Table: sys_audit_logs (Immutability)
log_idBIGINTPK
user_idBIGINTFK
actionVARCHAR(20)'CREATE', 'UPDATE', 'VOID'
table_nameVARCHAR(50)e.g., 'ar_invoices'
old_dataJSONRecord state before action
new_dataJSONRecord state after action

Sample Data: RBAC Permission Mapping (Granular Sub-Tasks)

tenant_idrole_namehierarchy_levelMapped Permissions (sys_role_permissions)Business Logic & Sub-task Control
Akij-UUIDAgency Admin1* (All Permissions)Full system access. Can CREATE, VIEW, EDIT, DELETE, and APPROVE across all modules.
Akij-UUIDChief Accountant10gl.journal.view, gl.journal.approve, sys.period.editCan VIEW journals and APPROVE them, but cannot CREATE them. Can EDIT fiscal period status to CLOSED.
Akij-UUIDTicketing Agent50ar.invoice.view, ar.invoice.createCan VIEW and CREATE invoices. CANNOT EDIT, DELETE, or VOID them. If an error is made, they must request an accountant to void it.

3. Authentication & Authorization Workflows

AuthN (Authentication) & Brute Force Prevention

1. User Submits Email/Pass 2. Go API queries Redis IP/Email Counters 3. Verify Argon2 Hash 4. Generate JWT
Exception Scenario (Rate Limiting): If a user fails login > 5 times in 10 minutes, Redis flags the email. The Go backend updates sys_users.status = 'LOCKED'. Subsequent attempts return HTTP 423 Locked. An Admin (Level 1) must unlock the account.

AuthZ (Granular Action Authorization & Branch Data Isolation)

Role, Action & Branch Scope Middleware:
When the JWT is generated, it contains { tenant_id, role_id, permissions: ['ar.invoice.view', 'ar.invoice.create'], allowed_branch_ids: [1, 2] }.
1. Action Check (Sub-task): If a user attempts a PUT /api/v1/invoices/99 (Edit), middleware verifies 'ar.invoice.edit' exists in the JWT. If missing, it immediately throws HTTP 403 Forbidden.
2. Branch Scope Check: The middleware appends branch isolation to the database query: UPDATE ar_invoices ... WHERE tenant_id = JWT.tenant_id AND branch_id IN (JWT.allowed_branch_ids).

4. Core API Payloads & Form Fields

User Setup Form (POST /api/v1/system/users)

Field / ParamUI / Data TypeReqValidation & Allowed ValuesAccounting & System Impact
emailEmail / StringYStandard email regex. UNIQUE(tenant_id, email).Primary login identifier. Must be verified.
passwordPasswordY**Req on creation. Min 8 chars, 1 Upper, 1 Num.Never stored plain-text. Hashes immediately upon receipt.
role_idDropdownYMust exist in sys_roles for this tenant_id.Injects permissions into user session. Dictates UI rendering.
branch_idsMulti-SelectYArray of BIGINT. Must belong to tenant.Inserts into sys_user_branches. Sets row-level physical data boundaries.

Fiscal Period Management (POST /api/v1/system/fiscal-periods)

Field / ParamUI / Data TypeReqValidation & LogicAccounting Impact
start_dateDate PickerYMust be exactly 1 day after previous period's end_date.Creates the chronological boundary for Journal Entries.
end_dateDate PickerYMust be >= start_date. Usually Month-End.Defines the cut-off for Trial Balance generation.
statusToggleYRequires sys.period.close permission.CRITICAL: Once 'CLOSED', the DB triggers/middleware reject ANY financial write dated within this range.

5. Reporting Impact: System Audit Log & Access Review

Report Objective: Provide compliance officers, IATA regulators, and internal auditors a transparent view of all system activities, specifically highlighting unauthorized edits or voided tickets.

  • Data Source Tables: sys_audit_logs JOIN sys_users.
  • Filters & Parameters: Date Range, User ID, Table Name (e.g., ar_invoices), Action Type (UPDATE).
  • Business Decision Impact: Identifies internal fraud. If an auditor sees an agent frequently editing PNR base fares after an invoice is issued, management can restrict the ar.invoice.edit permission via Mod 0.2 and enforce the issuance of Credit Notes (Mod 2.5) instead.
  • Sample Output Preview (JSON Diff):
    [2026-05-26 14:32:11] User: agent_john | Action: UPDATE | Table: ar_invoices | Record ID: 9942 
    Old_Data: {"total_base": 10000.00, "status": "POSTED"} 
    New_Data: {"total_base": 8000.00, "status": "POSTED"}

Module 1: General Ledger (GL) Engine

Purpose & Travel Relevance: The mathematical core. In travel, high-volume ticketing requires an automated ledger that maps AR/AP events into balanced debits and credits without human intervention.

Sub-modules: 1.1 Chart of Accounts, 1.2 Journal Entries, 1.3 GL Viewer, 1.4 Consolidation, 1.5 Trial Balance, 1.6 Adjustments.

1. Database Schema

Table: gl_accounts (COA)
account_idBIGINTPK
parent_idBIGINTFK (Self) Hierarchy
account_codeVARCHAR(20)e.g., '1000'
account_typeENUMASSET, LIAB, EQUITY, REV, EXP
is_leafBOOLEANTrue = Accepts entries
Table: gl_journal_entries (Headers)
journal_idBIGINTPK
entry_dateDATEChecked vs Fiscal Period
reference_noVARCHAR(50)Link to AR/AP Invoice
sourceVARCHAR(20)'MANUAL', 'AR', 'AP'
statusENUM'DRAFT', 'POSTED', 'VOID'
Table: gl_journal_lines (The Ledger)
line_idBIGINTPK
journal_idBIGINTFK -> Header
account_idBIGINTFK -> gl_accounts
base_debitDECIMAL(19,4)Converted to base currency
base_creditDECIMAL(19,4)Converted to base currency

2. Workflow & Approvals (Sub-module 1.2: Journal Entries)

1. UI Data Entry 2. Middleware (Fiscal Period Check) 3. Sum(DR) == Sum(CR) Validation 4. Save as DRAFT 5. Manager Approval 6. Status: POSTED (Immutable)

3. API Payload & Accounting Logic

The Golden Rule: The database transaction is strictly wrapped. If Sum(base_debit) - Sum(base_credit) != 0.0000, the API returns HTTP 422 and rolls back the MySQL transaction.
POST /api/v1/gl/journals
{
  "entry_date": "2026-05-26", "currency_code": "USD", "exchange_rate": 110.50,
  "reference_no": "MANUAL-ADJ-001", "notes": "BSP Month End Adjustment",
  "lines": [
    { "account_id": 5010, "debit_base": 11050.00, "credit_base": 0.00, "memo": "Cost of Sales" },
    { "account_id": 2010, "debit_base": 0.00, "credit_base": 11050.00, "memo": "BSP Payable" }
  ]
}

Module 2: Sales & Accounts Receivable (AR)

Purpose & Travel Relevance: B2B/B2C billing. Travel invoices require capturing Passenger Name Records (PNRs), IATA Ticket Numbers, Base Fares, and complex Airline Taxes (YQ/XT) separately from Agency Fees.

Sub-modules: 2.1 Customer Master, 2.2 Invoicing, 2.3 Sales Register, 2.4 Receipts, 2.5 Credit Notes, 2.6 Refund, 2.7 Aging, 2.8 Multi-Currency AR.

1. Database Schema (Travel-Specific Design)

Table: ar_invoices (Header)
invoice_idBIGINTPK
customer_idBIGINTFK
invoice_noVARCHAR(50)Idx: UNIQUE
total_baseDECIMAL(19,4)
gl_journal_idBIGINTLink to auto-posted GL
Table: ar_invoice_lines (Metadata)
line_idBIGINTPK
invoice_idBIGINTFK
pnr_noVARCHAR(10)'X7B9Q2'
ticket_noVARCHAR(20)IATA: '176-1234567890'
base_fareDECIMALCost of ticket
supplier_taxDECIMALYQ/XT taxes
agency_feeDECIMALAgency markup
Table: ar_receipts (Payments)
receipt_idBIGINTPK
customer_idBIGINTFK
bank_acct_idBIGINTFK -> Treasury
amountDECIMALTotal received
allocatedDECIMALTracks Knock-off

2. Form Parameters (Sub-module 2.2: Customer Invoicing)

Field NameUI / Data TypeReqValidation & LogicFinancial Impact
customer_idDropdownYIF (Current AR + New Invoice > Credit Limit) THEN Warn Manager.Determines AR sub-ledger mapping.
pnr_noTextY**Req for Air. Regex: ^[A-Z0-9]{6}$.Used for profitability reports (Mod 13.4).
ticket_noTextY*DB Index: UNIQUE(tenant_id, ticket_no) blocks double billing.Critical for BSP Recon (Mod 3.2).

3. Automation Logic: Invoice to GL Mapping

When an agent clicks "Save Invoice", the system maps the Travel Data to the GL instantly:
DR 1200 - Accounts Receivable (Customer) : Total Invoice Amount
CR 4010 - Air Ticket Sales Revenue : Base Fare + Agency Fee
CR 2010 - BSP Payable (Liability) : Base Fare + Airline Taxes (Owed to Airline)
CR 2050 - Output VAT Payable : Govt VAT calculated on Agency Fee (Mod 12)

Module 3: Accounts Payable (AP) & BSP Settlements

Purpose & Travel Relevance: Tracking money owed to Suppliers (Airlines, GDS, Hotels), managing BSP reconciliations, and calculating PLB (Productivity Linked Bonus) commissions.

Sub-modules: 3.1 Supplier Master, 3.2 Supplier Invoices, 3.3 Purchase Register, 3.4 Commission, 3.5 Vendor Credits, 3.6 Debit Notes, 3.7 Payment Runs, 3.8 Multi-Currency AP.

1. Database Schema

Table: ap_suppliers
supplier_idBIGINTPK
supplier_typeENUM'BSP', 'NON-BSP', 'HOTEL'
credit_daysINTAuto-calculates Due Date
Table: ap_invoices (BSP Bills)
bill_idBIGINTPK
supplier_idBIGINTFK
billing_periodVARCHAR'BSP-MAY-P1'
total_amountDECIMALAmount owed to airline
Table: ap_commissions (PLB)
comm_idBIGINTPK
supplier_idBIGINTFK
target_volumeDECIMALSegment target for PLB
accrued_amountDECIMALUnrealized back-end revenue

2. Workflow: BSP Settlement Automation (Sub-module 3.2)

1. Upload BSP File (TXT/CSV) 2. GoLang Parser Extracts Tickets 3. Match vs ar_invoice_lines 4. Variance Check Engine 5. Auto-Post AP Bill & GL Entry

3. Exception Handling (ADM Risk)

BSP Variance Logic: IF Billed_Fare (from Airline) > Accrued_Fare (from AR Invoice) THEN Flag as 'ADM_RISK'. The agency undercharged the customer. The AP Bill is held. If valid, an ADM (Module 4.4) is generated: DR Agency Debit Memo Expense, CR BSP Payable.

Modules 4 & 5: Classifications & Fixed Assets

Sub-modules: 4.1 Service Type, 4.2 Purpose, 4.3 Txn Mapping, 4.4 ADM, 4.5 ACM, 5.1 Asset Register, 5.2 Depreciation, 5.3 Disposal, 5.4 Capitalization

1. Database Schema

Table: travel_adm_acm (Debit/Credit Memos)
memo_idBIGINTPK
memo_typeENUM'ADM' (Expense), 'ACM' (Income)
supplier_idBIGINTFK -> Airline issuing memo
ticket_noVARCHAR(20)Link to original ticket
amountDECIMAL(19,4)
Table: fa_assets (Asset Register)
asset_idBIGINTPK
asset_nameVARCHAR(100)e.g., 'Dell Server Rack'
purchase_valueDECIMAL(19,4)Capitalized amount
depreciation_methodENUM'STRAIGHT_LINE'
accum_depreciationDECIMAL(19,4)Updated monthly by Cron

2. Automation Logic (Sub-module 5.2: Depreciation)

Monthly Cron Job (Runs Last Day of Month):
Iterates fa_assets where status = 'ACTIVE'.
Calc: Monthly_Depr = (purchase_value - salvage_value) / useful_life_months.
Auto-posts Journal: DR Depreciation Expense | CR Accumulated Depreciation.

Modules 6 & 7: Treasury & Multi-Currency FX

Sub-modules: 6.1 Bank Master, 6.2 Cash Mgmt, 6.3 Bank Recon, 6.4 Forecasting, 6.5 MFS, 6.6 Payment Gateways | 7.1 FX Rates, 7.2 FX Gains/Losses, 7.3 Hedging

1. Database Schema

Table: tr_bank_accounts
bank_acct_idBIGINTPK
gl_account_idBIGINTFK -> Exact GL mapping
account_typeENUM'BANK', 'MFS', 'GATEWAY'
Table: tr_bank_statements (Uploads)
stmt_idBIGINTPK
bank_acct_idBIGINTFK
amountDECIMAL(+) Deposit, (-) Withdrawal
matched_gl_idBIGINTFK -> gl_journal_lines.
Table: fx_exchange_rates
rate_idBIGINTPK
currency_pairCHAR(7)e.g., 'USD/BDT'
rate_dateDATEFetched daily via API
rate_valueDECIMAL(15,6)High precision

2. Financial Algorithms

Bank Recon Engine (6.3): Match Rule 1 (Exact): IF Stmt.Amount == GL.Amount AND Stmt.Ref == GL.Ref THEN match(). Match Rule 2 (Tolerance): IF Stmt.Amount == GL.Amount AND GL.Date within 3 days of Stmt.Date THEN soft_match().

FX Gain/Loss Revaluation (7.2): Month-end. Finds unpaid ar_invoices in foreign currency. Compares original exchange_rate to today's fx_exchange_rates. Auto-generates GL Journal: DR AR, CR Unrealized FX Gain (if base value increased).

Modules 8 & 9: Budgeting & Enterprise Procurement

Sub-modules: 8.1 Budget Plan, 8.2 Budget vs Actual | 9.1 Vendor Master, 9.2 PR, 9.3 PO, 9.4 Goods Receipt, 9.5 3-Way Match

1. Database Schema (3-Way Match Pattern)

Table: prc_purchase_orders (PO)
po_idBIGINTPK
vendor_idBIGINTFK
total_amountDECIMALEncumbers budget in Mod 8
Table: prc_po_lines (Order)
po_line_idBIGINTPK
qty_orderedINT
unit_priceDECIMALApproved price
Table: prc_goods_receipts (GR)
gr_line_idBIGINTPK
qty_receivedINTActual delivery qty

2. Workflow: 3-Way Invoice Matching (Sub-module 9.5)

1. PO Approved (Qty: 10, Price: $100) 2. GR Received (Qty: 10) 3. AP Invoice Entered (Qty: 10, Price: $110) 4. MATCH EXCEPTION
Validation Rule: Before AP Invoice becomes 'APPROVED_FOR_PAYMENT', system joins po_lines -> gr_lines -> ap_invoice_lines. If (Invoice.UnitPrice > PO.UnitPrice * 1.05), payment is blocked and routed for executive approval.

Modules 10, 11 & 12: Settlements, Vendors & Global Tax

Sub-modules: 10.1 PG Recon, 10.3 Agent Commission, 10.5 Escrow | 11.2 Contracts/SLA, 11.4 KPIs | 12.1 VAT/GST, 12.2 TDS/AIT, 12.4 IFRS

1. Database Schema (Tax & SLA)

Table: tax_configurations (Rules Engine)
tax_idBIGINTPK
tax_codeVARCHAR(20)'VAT-15', 'TDS-5'
tax_typeENUM'INPUT_VAT', 'OUTPUT_VAT', 'TDS'
rate_percentageDECIMAL(5,2)e.g., 15.00
gl_account_idBIGINTFK -> Liability (VAT Payable)
Table: vnd_contracts (SLA Management)
contract_idBIGINTPK
supplier_idBIGINTFK
start_date / end_dateDATERenewal alerts (Mod 14.4)
commission_tierJSONe.g., {"target": 100000, "rate": 2.5}

2. API & Logic (Sub-module 12.2: TDS / Withholding Tax)

Business Use Case: Govts require deducting tax at source when paying suppliers. When generating a Payment Run (Mod 3.7), Tax Engine evaluates Supplier's Tax Profile.
POST /api/v1/tax/calculate-tds
// Payload: Paying a $1,000 invoice to IT Supplier (Requires 5% TDS)
{ "supplier_id": 8802, "invoice_amount": 1000.00 }

// Response
{
  "net_payment_to_supplier": 950.00, "tax_deducted": 50.00,
  "gl_postings": [
    { "account": "2060 (TDS Payable Authority)", "credit": 50.00 },
    { "account": "1010 (Bank Account)", "credit": 950.00 },
    { "account": "2010 (Accounts Payable)", "debit": 1000.00 }
  ]
}

Modules 13 & 14: Analytics, Reporting & Dashboards

Sub-modules: 13.1 P&L, 13.2 Balance Sheet, 13.3 Cash Flow, 13.4 Rev by Booking, 13.7 KPIs | 14.1 Exec Dash, 14.2 AR/AP Aging Widget

1. Database Schema (Materialized Reporting Layer)

Table: gl_monthly_balances (Core View)
tenant_idUUIDComposite PK 1
account_idBIGINTComposite PK 2
period_monthCHAR(7)'2026-05' Composite PK 3
opening_balanceDECIMALRolled over from prev month
net_debit / net_creditDECIMALSum of month's activity
closing_balanceDECIMALFormula: Open + Net_DR - Net_CR
Table: ar_aging_summary (Cron Generated)
customer_idBIGINTPK
current_dueDECIMALNot yet due
days_1_30DECIMALOverdue 1-30 days
days_90_plusDECIMALHigh risk receivables

2. Dashboard Architecture & Data Flow (Mod 14)

Enterprise Performance Rule: The Dashboard NEVER queries ar_invoices or gl_journal_lines directly.
Workflow:
1. A GoLang background worker runs every 15 minutes.
2. It queries the Materialized Views (gl_monthly_balances, ar_aging_summary).
3. It pushes aggregated JSON payloads to Redis (Key: tenant:{id}:dash_stats).
4. The React frontend fetches from Redis, achieving ~20ms response times for Executive KPI rendering.

3. Financial Report Definitions (Mod 13)

  • 13.1 Profit & Loss: Filters gl_monthly_balances for account_type IN ('REVENUE', 'EXPENSE'). Calc: Net Profit = Total Revenue - Total COGS - Total Opex.
  • 13.2 Balance Sheet: Filters gl_monthly_balances for account_type IN ('ASSET', 'LIABILITY', 'EQUITY'). Validation: Total Assets MUST = (Total Liabilities + Total Equity + Current Net Profit).
  • 13.4 Revenue by Booking: Joins ar_invoice_lines and dict_services. Groups profitability by PNR, Sector (Domestic vs Intl), and Corporate Client.