এই ভলিউমে · ভলিউম 05
অ্যাকাউন্টিং
হিসাব তালিকা জার্নাল ও লেজার বিলিং ও ইনভয়েসিং পেমেন্ট ও রসিদ পেমেন্ট রিকনসিলিয়েশন মাল্টি-কারেন্সি ডেফার্ড রেভিনিউ কমিশন অ্যাকাউন্টিং কর (VAT/GST) পিরিয়ড ক্লোজ
বাংলা সারসংক্ষেপ

লাইন-লেভেলে মাল্টি-কারেন্সি (IAS 21)। প্রতিটি JE লাইনে transaction, functional, reporting currency। FX রেট পোস্টিং-সময়ে ক্যাপচার। পিরিয়ড শেষে: closing-rate রিভ্যালুয়েশন। বাংলাদেশের পার্টনারদের জন্য functional currency সাধারণত BDT।

Chapter 5.6 — Multi-Currency Accounting

chapter: 05-accounting/06-multi-currency
version: 1.0.0
status: stable
last_reviewed: 2026-05-26
owners: [accounting, platform-engineering]

1. Purpose

Travel is intrinsically multi-currency. A Dhaka agency sells in BDT, pays Emirates in AED via IATA, settles with a Bangkok hotel in THB, and reports financials in BDT. This chapter explains how travoBooks models currency at every layer — from the operational event to the financial statement — and how foreign-currency translation and revaluation are kept consistent and auditable.

2. Why this matters in modern travel accounting

The most common multi-currency defect in travel back-offices is late translation: storing transactions as functional-currency-only and applying a single end-of-month rate. The result is silent loss of fidelity. By the time someone looks at margin on a USD-priced ticket, the original USD is gone.

travoBooks' rule is: never lose the original. Every monetary value is stored with its currency code. Functional-currency equivalents are additional, captured at the moment of posting with the rate used.

3. Industry relevance

IATA settles in airline currencies, often via local clearing in agency currency. Hotel suppliers quote in their currency. Payment gateways settle in their support currency, applying their own FX. The agency must reconcile across all of this. A multi-currency-correct system makes that reconciliation mechanical; an incorrect one makes it a manual nightmare.

4. Compliance considerations

Standard Implication
IAS 21 Distinguishes functional currency (entity reports in) from presentation currency (statements presented in). Requires consistent translation.
IFRS 9 / IAS 21 Monetary items revalued at closing rate each period; differences to P&L (realised vs unrealised).
Local GAAP variants May require official central-bank rates (e.g. Bangladesh Bank, RBI) on specific dates. travoBooks supports a "regulator rate" source per partner.
Tax authorities Often require the rate on transaction date (not period close) for VAT base calculation.

5. Business logic

5.1 Three currencies on every transaction

flowchart LR TC[Transaction Currency<br/>e.g. USD] -- rate at event time --> FC[Functional Currency<br/>e.g. BDT] FC -- rate at report time --> RC[Reporting Currency<br/>e.g. USD for group]
  • Transaction currency — the currency the event actually happened in. Permanent.
  • Functional currency — the partner's statutory reporting currency. Stored on every JE line via fx_rate and functional_amount.
  • Reporting currency — chosen at report-rendering time; computed from functional balances.

5.2 FX rate model

FxRate
├── id
├── source            (provider | manual | regulator | partner)
├── source_ref        (e.g. "openexchangerates", "bb-official", "user-upload")
├── from_currency
├── to_currency
├── rate              (precise; stored as DECIMAL(18,8) minimum)
├── effective_at      (timestamp; rates are point-in-time)
├── effective_date    (date; for "official rate of this date" use)
├── created_at
└── partner_id        (NULL if platform-shared; else partner-specific)

Rate selection at posting time follows a deterministic precedence:

  1. Partner-specific rate with effective_at ≤ event_time, latest first.
  2. Platform-shared rate with effective_at ≤ event_time, latest first.
  3. Fall back to provider live fetch (if enabled).

If all three fail, the post is rejected with FX_UNAVAILABLE.

5.3 Precision and rounding

  • Amounts stored as DECIMAL(18,2) for normal currencies; DECIMAL(18,3) for currencies that use three decimals (BHD, KWD, OMR); DECIMAL(18,0) for currencies without subunit (JPY, KRW, VND).
  • Rates stored as DECIMAL(18,8).
  • The system maintains a currency_minor_units reference: how many decimal places each currency uses.
  • Rounding rule: banker's rounding (round half to even) by default per JE line; configurable per partner to half-away-from-zero where required by local convention.

5.4 Translation rules

Different categories of accounts are translated differently per IAS 21:

Account category Translation rate
Monetary assets/liabilities (cash, AR, AP) Closing rate at reporting date
Non-monetary assets at historical cost Historical rate at acquisition
Non-monetary assets at fair value Rate at fair-value date
Income and expenses Rate at transaction date (or average for the period)
Equity Historical rate (when contributed)

travoBooks encodes these by tagging each account with a fx_translation_class and the period-end revaluation job applies the right rate.

5.5 Realised vs unrealised FX

  • Realised FX gain/loss — crystallised on settlement (e.g., USD invoice paid in USD when functional is BDT and the BDT-equivalent at receipt date differs from the BDT-equivalent recorded at invoice issuance). Posted to 409x / 609x.
  • Unrealised FX gain/loss — on period close, monetary balances are revalued at the closing rate. Difference vs prior carrying amount → 4099 / 6099. Reversed at the start of the next period (so the next realised settlement is computed from the original rate).

5.6 Currency on master entities

Entity Default currency Notes
Customer default_invoice_currency A customer can be invoiced in multiple currencies; this is the default.
Supplier default_settlement_currency The currency the partner pays them in (often the airline's BSP currency).
Bank account One currency per bank account A USD bank account is a separate row from a BDT account.
Tax profile The jurisdiction's currency Tax is computed in that currency and translated to functional.

5.7 Group consolidation

For groups with partners in different functional currencies, the group reporting currency is set at the group level. Group reports translate each partner's functional balances at the appropriate rate (closing for balance sheet, average for P&L) and aggregate. Translation differences post to a group-level "Cumulative Translation Adjustment" memo balance.

6. Inputs → processing → outputs

6.1 Configuring currencies for a partner

partner_currencies:
  functional: "BDT"
  enabled: ["BDT", "USD", "EUR", "AED", "INR", "GBP", "THB"]
  display_default: "BDT"
  fx_provider: "openexchangerates"
  fx_provider_base: "USD"
  fx_refresh_cron: "@hourly"
  regulator_rate_source: "bangladesh-bank"   # optional, for VAT base calc

6.2 Processing on a foreign-currency JE line

  1. Compute functional_amount = transaction_amount × fx_rate(transaction_currency → functional_currency, effective_at = event_time).
  2. Round to functional currency's minor units.
  3. Persist both transaction and functional amounts plus the rate used.
  4. The JE's balance check (debits = credits) is performed in functional currency.

6.3 Period-end revaluation outputs

For each monetary account with non-functional balances, the period-close job:

  1. Identifies open balances (e.g., open AR invoices, open AP bills, foreign-currency bank balances).
  2. Translates each at the closing rate.
  3. Computes delta vs the carrying functional amount.
  4. Posts unrealised FX gain/loss JE.
  5. Writes a reversing JE dated the first day of the next period.

7. Module dependencies

  • Reads: currencies reference, fx_rates, account fx_translation_class, period state, partner_currencies.
  • Writes: fx_rates (on provider refresh), JEs (revaluation), audit logs.
  • Consumed by: every accounting and reporting module.

8. Security & permissions

Permission Allows
currency.read.partner View enabled currencies
currency.enable.partner Enable/disable currencies
fx.read.partner View rates
fx.override.partner Provide a manual rate (with reason, audited)
fx.revalue.partner Run period revaluation

🔒 Manual FX rate entries require a reason text and are audited; clusters of these are surfaced in the partner's monthly control report.

9. Validation rules

Rule Code
Currency code is ISO 4217 CURRENCY_INVALID
Currency is enabled for partner CURRENCY_DISABLED
FX rate available at effective_at ≤ event_time FX_UNAVAILABLE
FX rate not in the future FX_FUTURE
Manual rate within ±10% of platform-shared rate of the same date (configurable) FX_OUTLIER (warning, not block, unless strict mode on)
Account currency_mode = functional_only cannot accept foreign currency lines ACCOUNT_FUNCTIONAL_ONLY
Closing rate must exist on period end date for revaluation FX_CLOSE_RATE_MISSING

10. Error handling

  • FX provider downtime: the system uses the most recent successful rate ≤ event time, with source = "cached_after_outage". If outage exceeds 24 hours, an alert escalates.
  • Rate outlier: flagged in audit; the user can confirm with reason or override.
  • Period revaluation partial failure: the entire revaluation is transactional; partial states are not visible.

11. Real-world example

Scenario. Partner P-001 (functional BDT). On 5 May 2026 they issue an invoice to a US customer: USD 10,000. The recorded FX rate is 1 USD = 109.5 BDT (so functional 1,095,000 BDT).

On 31 May (period close), the invoice is still open. The closing rate is 1 USD = 110.2 BDT. The closing carrying value would be 1,102,000 BDT.

Period revaluation JE on 31 May:

Acct 🅓 🅒
1022 AR – US Customer (unrealised) 7,000
4099 Unrealised FX Gain 7,000

Reversing JE dated 1 June:

Acct 🅓 🅒
4099 Unrealised FX Gain 7,000
1022 AR – US Customer (unrealised) 7,000

On 10 June, the customer pays USD 10,000. Spot rate that day: 1 USD = 110.8 BDT. Cash received in BDT-equivalent: 1,108,000.

Receipt JE (10 June):

Acct 🅓 🅒
1011 USD Bank 1,108,000
1022 AR – US Customer 1,095,000
4091 Realised FX Gain 13,000

Result: total FX gain on this invoice across periods is BDT 13,000 — all realised. The period-end unrealised gain washed out via the reversing entry. No double-counting; both periods' P&L reflect what actually happened economically.

12. Step-by-step — period revaluation

sequenceDiagram autonumber participant Sched participant RJ as Revaluation Job participant FX as FX Service participant GL as GL Service participant JE as Journal Svc participant DB Sched->>RJ: run partner=P001, period=2026-05 RJ->>FX: getClosingRates(period.end, enabled currencies) FX-->>RJ: rates map RJ->>GL: openMonetaryBalancesByAccount(period.end) GL-->>RJ: rows loop For each (account, currency) RJ->>RJ: delta = current_functional - revalued_functional alt delta != 0 RJ->>JE: post(unrealised JE, dated period.end) RJ->>JE: post(reversing JE, dated next_period.start) end end RJ->>DB: write revaluation_run row (audit)

13. Database tables touched

  • currencies — ISO reference + minor units.
  • partner_currencies — what's enabled.
  • fx_rates — rate store.
  • fx_rate_sources — providers configured.
  • journal_entries, journal_entry_lines — both transaction and functional amounts.
  • revaluation_runs — audit of each revaluation execution.

14. Future scalability

Need Approach
Hyperinflation accounting (IAS 29) Apply general price index to non-monetary items in qualifying economies. Add inflation_index reference.
Hedge accounting (IFRS 9) Add hedging-relationship records linking exposure (e.g. open AP) to instrument (forward contract).
Crypto / non-fiat Treat as commodity in equity-stake terms; not legal tender; out of scope for Phase 1 but the data model accepts new ISO-like codes.
Sub-minute rate granularity for high-frequency partners Already supported via effective_at timestamp. Just refresh more often.

15. Common pitfalls

  • ⚠️ Storing only functional amounts and discarding the original. The original is needed for VAT base, for reconciliation, for refund eligibility, and for restatement.
  • ⚠️ Using a single end-of-month rate for all month's transactions ("average rate"). Allowed by IAS 21 for P&L when transaction rates are impractical, but for a system that has transaction rates, use them.
  • ⚠️ Forgetting to reverse the unrealised entry on the first day of the next period; otherwise double-counting on realisation.
  • ⚠️ Using a manual override rate to "smooth" margins. The audit trail will surface this and it is a control breach.
  • 🔒 Never round at multiple stages; round once, at the line level, to functional minor units.

Next: 07-deferred-revenue.md — revenue recognition timing and the deferral mechanism.