#909: CurrencyConversionCache.kt

projectforge-business/src/main/kotlin/org/projectforge/business/fibu/CurrencyConversionCache.kt Type: Spring Component (cache)
Package: org.projectforge.business.fibu
Path: projectforge-business/src/main/kotlin/org/projectforge/business/fibu/CurrencyConversionCache.kt
Author: Kai Reinhard (k.reinhard@micromata.de) 268 lines · 143 code · 97 comments · 28 blank

An in-memory cache for currency pairs and their conversion rates. Extends AbstractCache, implementing the ProjectForge caching pattern with lazy refresh. Also acts as a BaseDOModifiedListener for CurrencyPairDO to invalidate the cache on inserts/updates. Provides bidirectional rate lookup with automatic inverse-rate computation.

Architecture

Inheritance & Interfaces

Internal Data Structures

FieldTypePurpose
currencyPairMapMap<Long, CurrencyPairDO>Primary lookup by database primary key
currencyPairLookupMapMap<Pair<String,String>, Long>Quick lookup by (source, target) → pair id

Refresh Strategy

The refresh() method runs inside a read-only transaction via persistenceService.runIsolatedReadOnly(). It loads all non-deleted CurrencyPairDO entries and all their non-deleted rates (sorted by validFrom DESC). Rates are grouped by currency pair id and assigned to the pair's transient rates list.

Inverse Rate Lookup

findCurrencyPairForConversion(source, target) first tries a direct lookup (e.g. EUR→USD). If not found, it tries the inverse direction (USD→EUR) and returns a CurrencyPairLookup with useInverseRate=true, signaling the caller to use 1/rate.

Fallback to Oldest Rate

findActiveRate() with useFallbackToOldestRate=true will log a warning and return the oldest available rate when no rate is valid for the requested date. This is explicitly documented as potentially non-compliant for financial accounting.

Data Class: CurrencyPairLookup

PropertyTypeDescription
pairCurrencyPairDOThe matching currency pair
useInverseRateBooleanIf true, the caller should apply 1/conversionRate

Key Methods

MethodDescription
getCurrencyPair(id)Retrieve a currency pair by its primary key
findCurrencyPair(src, tgt)Direct lookup by source and target currency codes
findCurrencyPairForConversion(src, tgt)Bidirectional lookup with inverse rate support
getConversionRate(pairId, date)Finds the active rate for a pair at a given date (optionally inverse)
getActiveRateDate(pairId, date)Returns the validFrom of the active rate
getRates(pairId)Returns all rates for a currency pair

Lifecycle

  1. @PostConstruct: registers itself with currencyPairDao as a listener and stores the singleton in the companion object.
  2. On first cache access via any public method, checkRefresh() triggers refresh().
  3. When a CurrencyPairDO is modified, afterInsertOrModify() calls setExpired().
  4. All rate-level operations (in CurrencyConversionServiceSupport) also call cache.setExpired() directly.

Git History

868d6abb7 2025 -> 2026
f4a839e3a CurrencyConversion: Show date of active rates in list view.
472fbc977 WIP: currency conversion with Claude Code.
8c2bf04f0 WIP: currency conversion with Claude Code.
abdac2e1b WIP: currency conversion with Claude Code.
e1323795d WIP: currency conversion with Claude Code.
357ebee61 WIP: currency conversion with Claude Code.