diff --git a/modules/fx-rates/assets/fx-rates.js b/modules/fx-rates/assets/fx-rates.js index 84e8213..9005900 100644 --- a/modules/fx-rates/assets/fx-rates.js +++ b/modules/fx-rates/assets/fx-rates.js @@ -26,16 +26,23 @@ const renderSnapshot = (snapshot) => { const rates = snapshot && snapshot.rates ? snapshot.rates : null; const entries = rates ? Object.entries(rates) : []; + const visibleCurrencies = preferredCurrencies.length + ? new Set(preferredCurrencies) + : null; if (!nodes.ratesBody) { return; } - if (!entries.length) { + const filteredEntries = visibleCurrencies + ? entries.filter(([code]) => visibleCurrencies.has(String(code || '').trim().toUpperCase())) + : entries; + + if (!filteredEntries.length) { nodes.ratesBody.innerHTML = 'Noch keine Wechselkurse fuer die ausgewaehlten Waehrungen gespeichert.'; return; } - nodes.ratesBody.innerHTML = entries.map(([code, rate]) => { + nodes.ratesBody.innerHTML = filteredEntries.map(([code, rate]) => { const formatted = typeof rate === 'number' ? rate.toLocaleString('de-DE', { maximumFractionDigits: 8 }) : 'n/a'; diff --git a/modules/fx-rates/src/Domain/FxRatesService.php b/modules/fx-rates/src/Domain/FxRatesService.php index 476e23a..33f67de 100644 --- a/modules/fx-rates/src/Domain/FxRatesService.php +++ b/modules/fx-rates/src/Domain/FxRatesService.php @@ -214,30 +214,32 @@ final class FxRatesService } $inverse = $this->repository->listDirectHistory($toCurrency, $fromCurrency, $this->normalizeTimestamp($from), $this->normalizeTimestamp($to), $limit); - if ($inverse === []) { - return []; - } + if ($inverse !== []) { + $result = []; + foreach ($inverse as $row) { + $inverseRate = is_numeric($row['rate'] ?? null) ? (float) $row['rate'] : null; + if ($inverseRate === null || $inverseRate <= 0) { + continue; + } - $result = []; - foreach ($inverse as $row) { - $inverseRate = is_numeric($row['rate'] ?? null) ? (float) $row['rate'] : null; - if ($inverseRate === null || $inverseRate <= 0) { - continue; + $result[] = [ + 'fetch_id' => $row['fetch_id'] ?? null, + 'base_currency' => $fromCurrency, + 'target_currency' => $toCurrency, + 'rate' => 1 / $inverseRate, + 'rate_date' => $row['rate_date'] ?? null, + 'provider' => $row['provider'] ?? null, + 'fetched_at' => $row['fetched_at'] ?? null, + 'is_exact_pair' => false, + ]; } - $result[] = [ - 'fetch_id' => $row['fetch_id'] ?? null, - 'base_currency' => $fromCurrency, - 'target_currency' => $toCurrency, - 'rate' => 1 / $inverseRate, - 'rate_date' => $row['rate_date'] ?? null, - 'provider' => $row['provider'] ?? null, - 'fetched_at' => $row['fetched_at'] ?? null, - 'is_exact_pair' => false, - ]; + if ($result !== []) { + return array_map(fn (array $row): array => $this->localizeRateResult($row), $result); + } } - return array_map(fn (array $row): array => $this->localizeRateResult($row), $result); + return $this->crossHistory($fromCurrency, $toCurrency, $from, $to, $limit); } public function runScheduledRefresh(array $context = []): array @@ -647,6 +649,49 @@ final class FxRatesService return $filtered; } + private function crossHistory(string $fromCurrency, string $toCurrency, ?string $from = null, ?string $to = null, int $limit = 200): array + { + $fromAt = $this->normalizeTimestamp($from); + $toAt = $this->normalizeTimestamp($to); + $candidates = array_reverse($this->repository->listRecentFetches(max($limit * 4, 50))); + $result = []; + + foreach ($candidates as $fetch) { + $fetchedAt = (string) ($fetch['fetched_at'] ?? ''); + if ($fetchedAt === '') { + continue; + } + + if ($fromAt !== null && strcmp($fetchedAt, $fromAt) < 0) { + continue; + } + if ($toAt !== null && strcmp($fetchedAt, $toAt) > 0) { + continue; + } + + $snapshot = $this->repository->getSnapshotByFetchId((int) ($fetch['id'] ?? 0), [$fromCurrency, $toCurrency]); + if ($snapshot === null) { + continue; + } + + $rates = is_array($snapshot['rates'] ?? null) ? $snapshot['rates'] : []; + $resolved = $this->resolveRateFromSnapshot($snapshot, $rates, $fromCurrency, $toCurrency); + if ($resolved === null) { + continue; + } + + $result[] = $this->localizeRateResult($resolved + [ + 'fetch_id' => $fetch['id'] ?? null, + ]); + + if (count($result) >= $limit) { + break; + } + } + + return $result; + } + private function localizeFetch(?array $fetch): ?array { if (!is_array($fetch)) {