ssad
All checks were successful
Deploy / deploy-staging (push) Successful in 5s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-04-29 02:43:40 +02:00
parent ef87dc6cd3
commit 235630ee1e
2 changed files with 73 additions and 21 deletions

View File

@@ -26,16 +26,23 @@
const renderSnapshot = (snapshot) => { const renderSnapshot = (snapshot) => {
const rates = snapshot && snapshot.rates ? snapshot.rates : null; const rates = snapshot && snapshot.rates ? snapshot.rates : null;
const entries = rates ? Object.entries(rates) : []; const entries = rates ? Object.entries(rates) : [];
const visibleCurrencies = preferredCurrencies.length
? new Set(preferredCurrencies)
: null;
if (!nodes.ratesBody) { if (!nodes.ratesBody) {
return; return;
} }
if (!entries.length) { const filteredEntries = visibleCurrencies
? entries.filter(([code]) => visibleCurrencies.has(String(code || '').trim().toUpperCase()))
: entries;
if (!filteredEntries.length) {
nodes.ratesBody.innerHTML = '<tr><td colspan="2">Noch keine Wechselkurse fuer die ausgewaehlten Waehrungen gespeichert.</td></tr>'; nodes.ratesBody.innerHTML = '<tr><td colspan="2">Noch keine Wechselkurse fuer die ausgewaehlten Waehrungen gespeichert.</td></tr>';
return; return;
} }
nodes.ratesBody.innerHTML = entries.map(([code, rate]) => { nodes.ratesBody.innerHTML = filteredEntries.map(([code, rate]) => {
const formatted = typeof rate === 'number' const formatted = typeof rate === 'number'
? rate.toLocaleString('de-DE', { maximumFractionDigits: 8 }) ? rate.toLocaleString('de-DE', { maximumFractionDigits: 8 })
: 'n/a'; : 'n/a';

View File

@@ -214,10 +214,7 @@ final class FxRatesService
} }
$inverse = $this->repository->listDirectHistory($toCurrency, $fromCurrency, $this->normalizeTimestamp($from), $this->normalizeTimestamp($to), $limit); $inverse = $this->repository->listDirectHistory($toCurrency, $fromCurrency, $this->normalizeTimestamp($from), $this->normalizeTimestamp($to), $limit);
if ($inverse === []) { if ($inverse !== []) {
return [];
}
$result = []; $result = [];
foreach ($inverse as $row) { foreach ($inverse as $row) {
$inverseRate = is_numeric($row['rate'] ?? null) ? (float) $row['rate'] : null; $inverseRate = is_numeric($row['rate'] ?? null) ? (float) $row['rate'] : null;
@@ -237,8 +234,13 @@ final class FxRatesService
]; ];
} }
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 public function runScheduledRefresh(array $context = []): array
{ {
@@ -647,6 +649,49 @@ final class FxRatesService
return $filtered; 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 private function localizeFetch(?array $fetch): ?array
{ {
if (!is_array($fetch)) { if (!is_array($fetch)) {