# How TREK Handles Budget Tracking and Multi-Currency: A Technical Deep Dive

> Discover how the TREK app manages budget tracking and multi-currency expenses. It uses a dedicated add-on, real-time exchange rates, and WebSocket for seamless collaboration.

- Repository: [Maurice/TREK](https://github.com/mauriceboe/TREK)
- Tags: deep-dive
- Published: 2026-07-03

---

**TREK implements a dedicated Budget add-on that stores every expense with its own ISO-4217 currency code and an exchange_rate field, converting all amounts to the trip's base currency using real-time rates from exchangerate.host while broadcasting updates via WebSocket for collaborative synchronization.**

TREK is an open-source trip planning platform that solves complex financial coordination through robust budget tracking and multi-currency support. Unlike simple expense splitters, TREK maintains full audit trails by preserving original currencies alongside conversion rates, ensuring every collaborator sees consistent, real-time financial data regardless of their local currency.

## Data Schema Design for Multi-Currency Expenses

### Budget Item Structure

Every expense in TREK is stored as a **budget item** defined in [`shared/src/budget/budget.schema.ts`](https://github.com/mauriceboe/TREK/blob/main/shared/src/budget/budget.schema.ts). This schema mandates three critical fields for multi-currency handling:

- **`currency`**: An ISO-4217 code (nullable) representing the transaction's original currency
- **`total_price`**: The raw numeric amount in the original currency
- **`exchange_rate`**: A numeric factor converting the amount to the trip's base currency

This design ensures **full auditability** by preserving the original transaction data alongside the conversion metadata. The system never overwrites the original amount, allowing future recalculation if rates change or if users need to verify historical conversions.

### Trip-Level Base Currency

The reference currency for all aggregations resides in [`shared/src/trip/trip.schema.ts`](https://github.com/mauriceboe/TREK/blob/main/shared/src/trip/trip.schema.ts). Each trip record specifies a base currency (e.g., EUR) that serves as the canonical unit for all financial summaries, MCP tool responses, and UI displays. This standardization prevents currency confusion when collaborators from different countries view shared expenses.

## Server-Side Conversion and Persistence Logic

### Automatic Exchange Rate Resolution

TREK sources live exchange rates through [`server/src/services/currencyService.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/services/currencyService.ts), a lightweight wrapper around the public **exchangerate.host** API. This service requires no API key and caches rates for the duration of the request to minimize external calls.

When `currencyService.getRate(itemCurrency, baseCurrency)` is invoked, it returns the current conversion factor, which the system then stores permanently with the budget item. This snapshot approach locks the rate at the time of transaction, preventing historical amounts from fluctuating with market changes.

### Budget Service Operations

The [`server/src/services/budgetService.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/services/budgetService.ts) handles the complete lifecycle of budget items with specific multi-currency logic:

1. **Default fallback**: If the client omits a currency, the service substitutes the trip's base currency
2. **Rate fetching**: Calls `currencyService.getRate()` to retrieve the conversion factor
3. **Persistence**: Stores the `exchange_rate` alongside the original `total_price` and `currency`
4. **Broadcast**: Emits a `budget:updated` WebSocket event to synchronize all connected clients

This sequence ensures that every write operation applies consistent conversion logic before persisting to the database.

```typescript
// Conceptual flow from budgetService.ts
async function createBudgetItem(data, tripBaseCurrency) {
  const currency = data.currency || tripBaseCurrency;
  const rate = await currencyService.getRate(currency, tripBaseCurrency);
  
  return await db.budgetItems.create({
    ...data,
    currency,
    exchange_rate: rate,
    total_price: data.amount
  });
}

```

## Real-Time UI and Collaborative Aggregation

### Per-Item Currency Selection

The [`client/src/components/budget/BudgetForm.vue`](https://github.com/mauriceboe/TREK/blob/main/client/src/components/budget/BudgetForm.vue) component provides a **currency selector** that defaults to the trip's base currency but allows users to specify any ISO-4217 code. This flexibility accommodates scenarios where travelers pay for accommodations in USD while their trip is budgeted in EUR.

When a user selects a non-base currency, the form submits both the raw amount and the chosen currency code, triggering the server-side conversion pipeline described above.

### Unified Financial Reporting

All aggregated views in [`client/src/components/budget/BudgetSummary.vue`](https://github.com/mauriceboe/TREK/blob/main/client/src/components/budget/BudgetSummary.vue) compute totals using the stored conversion rates:

- **Total trip cost**: Sum of `(total_price × exchange_rate)` for all items
- **Per-category breakdown**: Grouped sums using the same conversion formula
- **Per-person splits**: Distributed costs calculated after currency normalization

This approach guarantees that every collaborator sees identical figures regardless of who added the expense or which currency was used for the original transaction.

## AI Integration via MCP Tools

TREK exposes budget data through Model Context Protocol (MCP) tools defined in [`MCP.md`](https://github.com/mauriceboe/TREK/blob/main/MCP.md). The `budget-overview` and `budget-per-person` tools return **pre-converted amounts** already normalized to the trip base currency, enabling AI assistants and third-party integrations to consume financial data without implementing their own conversion logic.

Access to these tools is governed by permission scopes defined in [`wiki/MCP-Scopes.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/MCP-Scopes.md). Only users with `budget:write` scope or admins possessing `budget_edit` can modify items, ensuring that currency conversion and persistence occur only through authorized channels.

## Summary

- TREK stores original currencies and exchange rates in [`shared/src/budget/budget.schema.ts`](https://github.com/mauriceboe/TREK/blob/main/shared/src/budget/budget.schema.ts) for full auditability.
- The [`server/src/services/currencyService.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/services/currencyService.ts) fetches live rates from exchangerate.host without requiring API keys.
- All aggregations use the stored `exchange_rate` to convert `total_price` into the trip base currency defined in [`shared/src/trip/trip.schema.ts`](https://github.com/mauriceboe/TREK/blob/main/shared/src/trip/trip.schema.ts).
- Real-time synchronization occurs via WebSocket events (`budget:updated`) broadcast from [`server/src/services/budgetService.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/services/budgetService.ts).
- MCP tools provide AI-ready financial data already normalized to the base currency.

## Frequently Asked Questions

### How does TREK handle currency conversion for budget items?

When a budget item is created, `budgetService.createBudgetItem()` calls `currencyService.getRate()` to fetch the current exchange rate from exchangerate.host between the item's currency and the trip's base currency. This rate is stored in the `exchange_rate` field alongside the original `total_price`, allowing the system to calculate converted values on-demand while preserving the original transaction data.

### What happens if a user doesn't specify a currency for an expense?

The system defaults to the trip's base currency as defined in [`shared/src/trip/trip.schema.ts`](https://github.com/mauriceboe/TREK/blob/main/shared/src/trip/trip.schema.ts). The budget service detects the missing currency value and falls back to this default before processing, ensuring every item has a valid ISO-4217 code without requiring manual entry for local expenses.

### Can multiple users see budget totals in their own currencies simultaneously?

No, TREK standardizes all displays to the trip's base currency. While each budget item stores its original currency, all aggregated views in [`BudgetSummary.vue`](https://github.com/mauriceboe/TREK/blob/main/BudgetSummary.vue) and MCP tools convert amounts using the stored `exchange_rate` before display. This ensures all collaborators see identical financial figures during real-time WebSocket synchronization via the `budget:updated` event.

### Which permissions are required to modify budget items?

Users need the `budget:write` scope to create or update items, while administrators with `budget_edit` can override existing entries. These permissions are enforced in [`server/src/services/budgetService.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/services/budgetService.ts) to ensure that only authorized actors can trigger currency conversion and persistence logic.