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

This commit is contained in:
2026-05-02 03:36:09 +02:00
parent 6d59d94273
commit 7f038f03e8
6 changed files with 189 additions and 64 deletions

View File

@@ -426,6 +426,11 @@ final class DashboardPage
if (empty($apiResult['ok'])) {
throw new RuntimeException((string) ($apiResult['message'] ?? 'API-Abruf fehlgeschlagen.'));
}
$fxResult = \module_fn('boersenchecker', 'fx_prepare_fetch', $this->defaultReportCurrency, [$quoteCurrency], $this->fxMaxAgeHours);
if (empty($fxResult['ok'])) {
throw new RuntimeException((string) ($fxResult['message'] ?? 'FX-Aktualisierung fehlgeschlagen.'));
}
$fxFetchId = is_numeric($fxResult['fetch_id'] ?? null) ? (int) $fxResult['fetch_id'] : null;
if ($this->isApiSnapshotStale($latestApiQuote, (string) ($apiResult['fetched_at'] ?? ''))) {
$displayTime = (string) \module_fn(
'boersenchecker',
@@ -443,7 +448,7 @@ final class DashboardPage
(float) $apiResult['price'],
$quoteCurrency,
(string) $apiResult['fetched_at'],
(string) $apiResult['source']
(string) \module_fn('boersenchecker', 'fx_source_with_fetch_id', (string) $apiResult['source'], $fxFetchId)
);
if (!empty($storeResult['inserted'])) {
return 'Alpha-Vantage-Kurs fuer ' . (string) $row['instrument_name'] . ' gespeichert.';
@@ -498,6 +503,16 @@ final class DashboardPage
}
if ($bulkRows !== []) {
$quoteCurrencies = array_values(array_unique(array_filter(array_map(
fn (array $row): string => $this->normalizeCurrency((string) ($row['quote_currency'] ?? '')),
$bulkRows
))));
$fxResult = \module_fn('boersenchecker', 'fx_prepare_fetch', $this->defaultReportCurrency, $quoteCurrencies, $this->fxMaxAgeHours);
if (empty($fxResult['ok'])) {
throw new RuntimeException((string) ($fxResult['message'] ?? 'FX-Aktualisierung fehlgeschlagen.'));
}
$fxFetchId = is_numeric($fxResult['fetch_id'] ?? null) ? (int) $fxResult['fetch_id'] : null;
$bulkResult = \module_fn('boersenchecker', 'alpha_vantage_fetch_quotes', $bulkRows);
if (empty($bulkResult['ok'])) {
throw new RuntimeException((string) ($bulkResult['message'] ?? 'Alpha-Vantage-Abruf fehlgeschlagen.'));
@@ -526,7 +541,7 @@ final class DashboardPage
(float) $apiResult['price'],
$quoteCurrency,
(string) $apiResult['fetched_at'],
(string) $apiResult['source']
(string) \module_fn('boersenchecker', 'fx_source_with_fetch_id', (string) $apiResult['source'], $fxFetchId)
);
if (!empty($storeResult['inserted'])) {
$fetched++;
@@ -698,7 +713,7 @@ final class DashboardPage
if (is_array($latestQuote) && is_numeric($latestQuote['price'] ?? null)) {
$currentOriginal = $quantity * (float) $latestQuote['price'];
$currentTotalBase = $this->convertAmount($currentOriginal, (string) $latestQuote['currency'], $baseCurrency);
$currentTotalBase = $this->convertAmount($currentOriginal, (string) $latestQuote['currency'], $baseCurrency, (string) ($latestQuote['source'] ?? ''));
$position['latest_price'] = (float) $latestQuote['price'];
$position['latest_currency'] = (string) $latestQuote['currency'];
$position['latest_quoted_at'] = (string) $latestQuote['quoted_at'];
@@ -796,7 +811,7 @@ final class DashboardPage
]);
}
private function convertAmount(?float $amount, string $from, string $to): ?float
private function convertAmount(?float $amount, string $from, string $to, ?string $quoteSource = null): ?float
{
if ($amount === null) {
return null;
@@ -808,13 +823,9 @@ final class DashboardPage
return $amount;
}
$fxService = \module_fn('boersenchecker', 'fx_service');
if (!$fxService || !method_exists($fxService, 'convert')) {
return null;
}
try {
$value = $fxService->convert($amount, $from, $to);
$fetchId = (int) (\module_fn('boersenchecker', 'fx_extract_fetch_id', (string) $quoteSource) ?? 0);
$value = \module_fn('boersenchecker', 'fx_convert_with_fetch', $amount, $from, $to, $fetchId > 0 ? $fetchId : null);
return is_numeric($value) ? (float) $value : null;
} catch (\Throwable) {
return null;

View File

@@ -15,6 +15,7 @@ final class HomePage
private string $positionTable;
private string $quoteTable;
private string $defaultReportCurrency;
private float $fxMaxAgeHours;
private int $marketDataMinIntervalMinutes;
public function __construct()
@@ -26,6 +27,10 @@ final class HomePage
$settings = \modules()->settings('boersenchecker');
$this->defaultReportCurrency = strtoupper(trim((string) ($settings['report_currency'] ?? 'EUR'))) ?: 'EUR';
$this->fxMaxAgeHours = (float) ($settings['fx_max_age_hours'] ?? 6);
if ($this->fxMaxAgeHours <= 0) {
$this->fxMaxAgeHours = 6.0;
}
$this->marketDataMinIntervalMinutes = (int) (($settings['alpha_vantage_min_interval_minutes'] ?? null) ?: 60);
if ($this->marketDataMinIntervalMinutes <= 0) {
$this->marketDataMinIntervalMinutes = 60;
@@ -77,7 +82,8 @@ final class HomePage
$currentReport = $this->convertAmount(
$currentNative,
(string) ($position['latest_currency'] ?: ($position['quote_currency'] ?? $this->defaultReportCurrency)),
$this->defaultReportCurrency
$this->defaultReportCurrency,
(string) ($position['latest_source'] ?? '')
);
$position['current_total_report'] = $currentReport;
if ($position['purchase_total_report'] !== null && $currentReport !== null) {
@@ -156,6 +162,16 @@ final class HomePage
}
if ($bulkCandidates !== []) {
$quoteCurrencies = array_values(array_unique(array_filter(array_map(
fn (array $row): string => $this->normalizeCurrency((string) ($row['quote_currency'] ?? '')),
$bulkCandidates
))));
$fxResult = \module_fn('boersenchecker', 'fx_prepare_fetch', $this->defaultReportCurrency, $quoteCurrencies, $this->fxMaxAgeHours);
if (empty($fxResult['ok'])) {
throw new RuntimeException((string) ($fxResult['message'] ?? 'FX-Aktualisierung fehlgeschlagen.'));
}
$fxFetchId = is_numeric($fxResult['fetch_id'] ?? null) ? (int) $fxResult['fetch_id'] : null;
$bulkResult = \module_fn('boersenchecker', 'alpha_vantage_fetch_quotes', $bulkCandidates);
$quotes = is_array($bulkResult['quotes'] ?? null) ? $bulkResult['quotes'] : [];
foreach ($bulkCandidates as $row) {
@@ -176,7 +192,7 @@ final class HomePage
(float) $quote['price'],
strtoupper(trim((string) ($quote['currency'] ?? $row['quote_currency'] ?? $this->defaultReportCurrency))) ?: $this->defaultReportCurrency,
(string) ($quote['fetched_at'] ?? gmdate('Y-m-d H:i:s')),
(string) ($quote['source'] ?? 'alphavantage:global_quote')
(string) \module_fn('boersenchecker', 'fx_source_with_fetch_id', (string) ($quote['source'] ?? 'alphavantage:global_quote'), $fxFetchId)
);
if (!empty($storeResult['inserted'])) {
$updated++;
@@ -319,7 +335,7 @@ final class HomePage
];
}
private function convertAmount(?float $amount, string $from, string $to): ?float
private function convertAmount(?float $amount, string $from, string $to, ?string $quoteSource = null): ?float
{
if ($amount === null) {
return null;
@@ -331,13 +347,9 @@ final class HomePage
return $amount;
}
$fxService = \module_fn('boersenchecker', 'fx_service');
if (!$fxService || !method_exists($fxService, 'convert')) {
return null;
}
try {
$value = $fxService->convert($amount, $from, $to);
$fetchId = (int) (\module_fn('boersenchecker', 'fx_extract_fetch_id', (string) $quoteSource) ?? 0);
$value = \module_fn('boersenchecker', 'fx_convert_with_fetch', $amount, $from, $to, $fetchId > 0 ? $fetchId : null);
return is_numeric($value) ? (float) $value : null;
} catch (\Throwable) {
return null;

View File

@@ -14,6 +14,7 @@ final class InstrumentPage
private string $positionTable;
private string $quoteTable;
private string $defaultReportCurrency;
private float $fxMaxAgeHours;
private string $searchKeywords = '';
private array $searchResults = [];
private int $selectedInstrumentOverrideId = 0;
@@ -28,6 +29,10 @@ final class InstrumentPage
$settings = \modules()->settings('boersenchecker');
$this->defaultReportCurrency = strtoupper(trim((string) ($settings['report_currency'] ?? 'EUR'))) ?: 'EUR';
$this->fxMaxAgeHours = (float) ($settings['fx_max_age_hours'] ?? 6);
if ($this->fxMaxAgeHours <= 0) {
$this->fxMaxAgeHours = 6.0;
}
$table = static fn (string $name): string => \module_fn('boersenchecker', 'table', $name);
$this->instrumentTable = $table('instruments');
$this->positionTable = $table('positions');
@@ -223,6 +228,12 @@ final class InstrumentPage
if (empty($apiResult['ok'])) {
throw new RuntimeException((string) ($apiResult['message'] ?? 'API-Abruf fehlgeschlagen.'));
}
$quoteCurrency = strtoupper(trim((string) ($instrument['quote_currency'] ?? $this->defaultReportCurrency))) ?: $this->defaultReportCurrency;
$fxResult = \module_fn('boersenchecker', 'fx_prepare_fetch', $this->defaultReportCurrency, [$quoteCurrency], $this->fxMaxAgeHours);
if (empty($fxResult['ok'])) {
throw new RuntimeException((string) ($fxResult['message'] ?? 'FX-Aktualisierung fehlgeschlagen.'));
}
$fxFetchId = is_numeric($fxResult['fetch_id'] ?? null) ? (int) $fxResult['fetch_id'] : null;
$latestApiQuote = $this->latestApiQuoteForInstrument($instrumentId);
if ($this->isApiSnapshotStale($latestApiQuote, (string) ($apiResult['fetched_at'] ?? ''))) {
$displayTime = (string) \module_fn(
@@ -239,9 +250,9 @@ final class InstrumentPage
'store_market_quote',
$instrumentId,
(float) $apiResult['price'],
strtoupper(trim((string) ($instrument['quote_currency'] ?? $this->defaultReportCurrency))) ?: $this->defaultReportCurrency,
$quoteCurrency,
(string) $apiResult['fetched_at'],
(string) $apiResult['source']
(string) \module_fn('boersenchecker', 'fx_source_with_fetch_id', (string) $apiResult['source'], $fxFetchId)
);
return !empty($storeResult['inserted'])