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

This commit is contained in:
2026-04-22 01:31:18 +02:00
parent a1bab34bd3
commit 91dc84d027
16 changed files with 1697 additions and 86 deletions

View File

@@ -10,6 +10,7 @@ final class DashboardPage
{
private PDO $pdo;
private array $user;
private bool $isAdmin;
private string $ownerSub;
private array $moduleSettings;
private string $defaultReportCurrency;
@@ -21,8 +22,10 @@ final class DashboardPage
private string $instrumentTable;
private string $positionTable;
private string $quoteTable;
private InstrumentRegistry $instrumentRegistry;
private string $symbolSearchKeywords = '';
private array $symbolSearchResults = [];
private array $availableOwners = [];
public function __construct()
{
@@ -30,7 +33,15 @@ final class DashboardPage
\module_fn('boersenchecker', 'ensure_schema');
$this->user = \auth_user() ?? [];
$this->isAdmin = \auth_is_admin();
$this->ownerSub = trim((string) ($this->user['sub'] ?? 'local'));
$this->availableOwners = $this->buildAvailableOwners();
if ($this->isAdmin) {
$requestedOwner = trim((string) ($_GET['owner_sub'] ?? $_POST['owner_sub'] ?? ''));
if ($requestedOwner !== '' && isset($this->availableOwners[$requestedOwner])) {
$this->ownerSub = $requestedOwner;
}
}
$this->moduleSettings = \modules()->settings('boersenchecker');
$this->defaultReportCurrency = $this->normalizeCurrency((string) ($this->moduleSettings['report_currency'] ?? 'EUR'));
$this->fxMaxAgeHours = (float) ($this->moduleSettings['fx_max_age_hours'] ?? 6);
@@ -51,6 +62,12 @@ final class DashboardPage
$this->instrumentTable = $table('instruments');
$this->positionTable = $table('positions');
$this->quoteTable = $table('quotes');
$this->instrumentRegistry = new InstrumentRegistry(
$this->pdo,
$this->instrumentTable,
$this->positionTable,
$this->quoteTable,
);
}
public function handle(): array
@@ -78,6 +95,9 @@ final class DashboardPage
return [
'notice' => $notice,
'error' => $error,
'isAdmin' => $this->isAdmin,
'ownerSub' => $this->ownerSub,
'availableOwners' => array_values($this->availableOwners),
'defaultReportCurrency' => $this->defaultReportCurrency,
'fxMaxAgeHours' => $this->fxMaxAgeHours,
'alphaMinIntervalMinutes' => $this->alphaMinIntervalMinutes,
@@ -696,81 +716,7 @@ final class DashboardPage
private function upsertInstrument(array $payload): int
{
$instrumentId = (int) ($payload['id'] ?? 0);
$driver = strtolower((string) $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME));
$data = [
'isin' => trim((string) ($payload['isin'] ?? '')) ?: null,
'wkn' => trim((string) ($payload['wkn'] ?? '')) ?: null,
'symbol' => trim((string) ($payload['symbol'] ?? '')) ?: null,
'name' => trim((string) ($payload['name'] ?? '')),
'quote_currency' => $this->normalizeCurrency((string) ($payload['quote_currency'] ?? 'EUR')),
'market' => trim((string) ($payload['market'] ?? '')) ?: null,
];
if ($data['name'] === '') {
throw new RuntimeException('Bitte mindestens einen Aktiennamen angeben.');
}
if ($instrumentId <= 0) {
$instrumentId = $this->findInstrumentId($payload) ?? 0;
}
if ($instrumentId > 0) {
$stmt = $this->pdo->prepare(
'UPDATE ' . $this->instrumentTable . '
SET isin = :isin, wkn = :wkn, symbol = :symbol, name = :name, quote_currency = :quote_currency, market = :market, updated_at = CURRENT_TIMESTAMP
WHERE id = :id'
);
$stmt->execute($data + ['id' => $instrumentId]);
return $instrumentId;
}
if ($driver === 'pgsql') {
$stmt = $this->pdo->prepare(
'INSERT INTO ' . $this->instrumentTable . ' (isin, wkn, symbol, name, quote_currency, market)
VALUES (:isin, :wkn, :symbol, :name, :quote_currency, :market)
RETURNING id'
);
$stmt->execute($data);
return (int) $stmt->fetchColumn();
}
$stmt = $this->pdo->prepare(
'INSERT INTO ' . $this->instrumentTable . ' (isin, wkn, symbol, name, quote_currency, market)
VALUES (:isin, :wkn, :symbol, :name, :quote_currency, :market)'
);
$stmt->execute($data);
return (int) $this->pdo->lastInsertId();
}
private function findInstrumentId(array $payload): ?int
{
$isin = trim((string) ($payload['isin'] ?? ''));
$symbol = trim((string) ($payload['symbol'] ?? ''));
$name = trim((string) ($payload['name'] ?? ''));
if ($isin !== '') {
$stmt = $this->pdo->prepare('SELECT id FROM ' . $this->instrumentTable . ' WHERE isin = :isin LIMIT 1');
$stmt->execute(['isin' => $isin]);
$id = $stmt->fetchColumn();
if ($id !== false) {
return (int) $id;
}
}
if ($symbol !== '' && $name !== '') {
$stmt = $this->pdo->prepare('SELECT id FROM ' . $this->instrumentTable . ' WHERE symbol = :symbol AND name = :name LIMIT 1');
$stmt->execute([
'symbol' => $symbol,
'name' => $name,
]);
$id = $stmt->fetchColumn();
if ($id !== false) {
return (int) $id;
}
}
return null;
return $this->instrumentRegistry->save($payload);
}
private function storeQuote(int $instrumentId, float $price, string $currency, string $quotedAt, string $source): void
@@ -838,4 +784,33 @@ final class DashboardPage
return number_format($value, $scale, ',', '.');
}
private function buildAvailableOwners(): array
{
$owners = [];
$currentSub = trim((string) ($this->user['sub'] ?? 'local'));
$owners[$currentSub] = [
'sub' => $currentSub,
'label' => trim((string) ($this->user['name'] ?? $this->user['email'] ?? $currentSub)) ?: $currentSub,
];
if (!$this->isAdmin) {
return $owners;
}
foreach (\modules()->knownAuthUsers() as $knownUser) {
$sub = trim((string) ($knownUser['sub'] ?? ''));
if ($sub === '') {
continue;
}
$label = trim((string) ($knownUser['name'] ?? $knownUser['email'] ?? $knownUser['username'] ?? $sub));
$owners[$sub] = [
'sub' => $sub,
'label' => $label !== '' ? $label : $sub,
];
}
uasort($owners, static fn (array $left, array $right): int => strcmp((string) $left['label'], (string) $right['label']));
return $owners;
}
}