erwre
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:41:12 +02:00
parent 91dc84d027
commit e83d187a16
7 changed files with 215 additions and 127 deletions

View File

@@ -9,10 +9,7 @@ use RuntimeException;
final class HomePage
{
private PDO $pdo;
private array $user;
private bool $isAdmin;
private string $ownerSub;
private array $availableOwners = [];
private string $portfolioTable;
private string $instrumentTable;
private string $positionTable;
@@ -24,16 +21,8 @@ final class HomePage
{
$this->pdo = \module_fn('boersenchecker', 'pdo');
\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;
}
}
$user = \auth_user() ?? [];
$this->ownerSub = trim((string) ($user['sub'] ?? 'local'));
$settings = \modules()->settings('boersenchecker');
$this->defaultReportCurrency = strtoupper(trim((string) ($settings['report_currency'] ?? 'EUR'))) ?: 'EUR';
@@ -79,6 +68,24 @@ final class HomePage
$position['latest_price'] = is_array($latestQuote) && is_numeric($latestQuote['price'] ?? null) ? (float) $latestQuote['price'] : null;
$position['latest_currency'] = is_array($latestQuote) ? (string) ($latestQuote['currency'] ?? '') : '';
$position['latest_quoted_at'] = is_array($latestQuote) ? (string) ($latestQuote['quoted_at'] ?? '') : '';
$position['current_total_report'] = null;
$position['gain_report'] = null;
$position['gain_percent'] = null;
if ($position['latest_price'] !== null) {
$currentNative = (float) $position['latest_price'] * (float) ($position['quantity'] ?? 0);
$currentReport = $this->convertAmount(
$currentNative,
(string) ($position['latest_currency'] ?: ($position['quote_currency'] ?? $this->defaultReportCurrency)),
$this->defaultReportCurrency
);
$position['current_total_report'] = $currentReport;
if ($position['purchase_total_report'] !== null && $currentReport !== null) {
$gain = $currentReport - (float) $position['purchase_total_report'];
$position['gain_report'] = $gain;
$base = (float) $position['purchase_total_report'];
$position['gain_percent'] = $base > 0 ? ($gain / $base) * 100 : null;
}
}
}
unset($position);
@@ -93,15 +100,14 @@ final class HomePage
return [
'notice' => $notice,
'error' => $error,
'isAdmin' => $this->isAdmin,
'ownerSub' => $this->ownerSub,
'availableOwners' => array_values($this->availableOwners),
'portfolios' => $portfolios,
'selectedPortfolioId' => $selectedPortfolioId,
'positions' => $positions,
'selectedInstrumentId' => $selectedInstrumentId,
'selectedInstrument' => $selectedInstrument,
'chartEndpoint' => '/module/boersenchecker/chart_data?owner_sub=' . urlencode($this->ownerSub),
'summary' => $this->buildSummary($positions),
'defaultReportCurrency' => $this->defaultReportCurrency,
'chartEndpoint' => '/module/boersenchecker/chart_data',
];
}
@@ -184,7 +190,21 @@ final class HomePage
'owner_sub' => $this->ownerSub,
'portfolio_id' => $portfolioId,
]);
return $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($rows as &$row) {
$quantity = (float) ($row['quantity'] ?? 0);
$purchasePrice = (float) ($row['purchase_price'] ?? 0);
$fees = is_numeric($row['fees'] ?? null) ? (float) $row['fees'] : 0.0;
$purchaseTotal = ($quantity * $purchasePrice) + $fees;
$row['purchase_total'] = $purchaseTotal;
$row['purchase_total_report'] = $this->convertAmount(
$purchaseTotal,
(string) ($row['purchase_currency'] ?? $this->defaultReportCurrency),
$this->defaultReportCurrency
);
}
unset($row);
return $rows;
}
private function fetchLatestQuotes(array $instrumentIds): array
@@ -227,26 +247,66 @@ final class HomePage
return is_array($row) ? $row : null;
}
private function buildAvailableOwners(): array
private function buildSummary(array $positions): 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;
$invested = 0.0;
$current = 0.0;
$hasInvested = false;
$hasCurrent = false;
$best = null;
$worst = null;
foreach ($positions as $position) {
if (is_numeric($position['purchase_total_report'] ?? null)) {
$invested += (float) $position['purchase_total_report'];
$hasInvested = true;
}
if (is_numeric($position['current_total_report'] ?? null)) {
$current += (float) $position['current_total_report'];
$hasCurrent = true;
}
if (is_numeric($position['gain_percent'] ?? null)) {
if ($best === null || (float) $position['gain_percent'] > (float) ($best['gain_percent'] ?? 0)) {
$best = $position;
}
if ($worst === null || (float) $position['gain_percent'] < (float) ($worst['gain_percent'] ?? 0)) {
$worst = $position;
}
}
$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;
return [
'positions' => count($positions),
'invested' => $hasInvested ? $invested : null,
'current' => $hasCurrent ? $current : null,
'gain' => ($hasInvested && $hasCurrent) ? $current - $invested : null,
'best' => $best,
'worst' => $worst,
];
}
private function convertAmount(?float $amount, string $from, string $to): ?float
{
if ($amount === null) {
return null;
}
$from = strtoupper(trim($from)) ?: $this->defaultReportCurrency;
$to = strtoupper(trim($to)) ?: $this->defaultReportCurrency;
if ($from === $to) {
return $amount;
}
$fxService = \module_fn('boersenchecker', 'fx_service');
if (!$fxService || !method_exists($fxService, 'convert')) {
return null;
}
try {
$value = $fxService->convert($amount, $from, $to);
return is_numeric($value) ? (float) $value : null;
} catch (\Throwable) {
return null;
}
}
}

View File

@@ -9,10 +9,7 @@ use RuntimeException;
final class InstrumentPage
{
private PDO $pdo;
private array $user;
private bool $isAdmin;
private string $ownerSub;
private array $availableOwners = [];
private string $instrumentTable;
private string $positionTable;
private string $quoteTable;
@@ -26,16 +23,8 @@ final class InstrumentPage
{
$this->pdo = \module_fn('boersenchecker', 'pdo');
\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;
}
}
$user = \auth_user() ?? [];
$this->ownerSub = trim((string) ($user['sub'] ?? 'local'));
$settings = \modules()->settings('boersenchecker');
$this->defaultReportCurrency = strtoupper(trim((string) ($settings['report_currency'] ?? 'EUR'))) ?: 'EUR';
@@ -100,9 +89,6 @@ final class InstrumentPage
return [
'notice' => $notice,
'error' => $error,
'isAdmin' => $this->isAdmin,
'ownerSub' => $this->ownerSub,
'availableOwners' => array_values($this->availableOwners),
'instruments' => $instruments,
'selectedInstrument' => $selectedInstrument,
'selectedInstrumentId' => $selectedInstrumentId,
@@ -261,29 +247,6 @@ final class InstrumentPage
return (string) ($result['message'] ?? 'Suche abgeschlossen.');
}
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;
}
private function assertInstrumentAccessible(int $instrumentId): array
{
if ($instrumentId <= 0) {
@@ -303,7 +266,7 @@ final class InstrumentPage
]);
$instrument = $stmt->fetch(PDO::FETCH_ASSOC);
if (!is_array($instrument)) {
throw new RuntimeException('Aktie ist in diesem Benutzer-Scope nicht verfuegbar.');
throw new RuntimeException('Aktie ist nicht verfuegbar.');
}
return $instrument;