$value !== null && $value !== '' ); $path = auth_current_path(); $queryString = http_build_query($query, '', '&', PHP_QUERY_RFC3986); return $queryString === '' ? $path : $path . '?' . $queryString; } function auth_format_bestellung_date(?string $value): string { if ($value === null || $value === '') { return ''; } try { return (new DateTimeImmutable($value))->format('d.m.Y H:i'); } catch (Throwable) { return $value; } } function auth_format_bestellung_total(?float $value): string { if ($value === null) { return ''; } return 'CHF ' . number_format($value, 2, '.', '\''); } function auth_format_market_date(?string $earliest, ?string $latest): string { if ($earliest === null || $earliest === '') { return ''; } $start = (new DateTimeImmutable($earliest))->format('d.m.Y'); if ($latest === null || $latest === '' || $latest === $earliest) { return $start; } return $start . '–' . (new DateTimeImmutable($latest))->format('d.m.Y'); } function auth_market_status_label(string $status): string { return [ 'open' => 'Offen', 'waitlist' => 'Warteliste', 'registered' => 'Angemeldet', 'confirmation_pending' => 'Bestätigung ausstehend', 'confirmed' => 'Bestätigt', 'cancelled' => 'Abgesagt', 'completed' => 'Abgeschlossen', ][$status] ?? $status; } function auth_market_status_signal_class(string $status): string { return [ 'waitlist' => 'market-status--red', 'registered' => 'market-status--yellow', 'confirmation_pending' => 'market-status--yellow', 'confirmed' => 'market-status--green', ][$status] ?? ''; } function auth_render_markets_large_table(array $marketTable): string { $rows = $marketTable['rows'] ?? $marketTable; $search = (string) ($marketTable['search'] ?? ''); $sortColumn = (string) ($marketTable['sort_column'] ?? 'earliest_date'); $sortDirection = strtoupper((string) ($marketTable['sort_direction'] ?? 'ASC')); $limit = (int) ($marketTable['limit'] ?? 20); $pageSize = (int) ($marketTable['page_size'] ?? 20); $hasMore = (bool) ($marketTable['has_more'] ?? (count($rows) >= $limit)); $sortColumns = [ 'market_name' => 'Markt', 'earliest_date' => 'Markttag(e)', 'place' => 'Ort', 'registration_deadline' => 'Anmeldefrist', 'status' => 'Status', ]; $html = []; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
Marktdurchführungen
'; $html[] = ''; $html[] = ''; $html[] = ''; $html[] = ''; $html[] = '
'; $html[] = '
'; $headerIndex = 0; foreach ($sortColumns as $column => $label) { $isActive = $column === $sortColumn; $headerDirection = $isActive ? strtolower($sortDirection) : 'none'; $iconDirection = $isActive ? strtolower($sortDirection) : 'ascending'; $targetDirection = $isActive ? ($sortDirection === 'ASC' ? 'DESC' : 'ASC') : ($column === 'earliest_date' ? 'ASC' : 'ASC'); $ariaLabel = $isActive ? $label . ' ' . ($sortDirection === 'ASC' ? 'aufsteigend sortiert' : 'absteigend sortiert') : $label . ' sortieren'; $html[] = '
'; $headerIndex++; } $html[] = '
Verkaufte Produkte
'; $html[] = '
'; foreach ($rows as $index => $row) { $rowClass = 'sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row'; if ($index % 2 === 1) { $rowClass .= ' sg-large-table__row--striped-light'; } $html[] = '
'; $html[] = '
' . auth_escape_html((string) ($row['market_name'] ?? '')) . '
'; $html[] = ''; $html[] = '
' . auth_escape_html((string) ($row['place'] ?? '')) . '
'; $registrationDeadline = (string) ($row['registration_deadline'] ?? ''); $registrationDeadlineLabel = ''; if ($registrationDeadline !== '') { try { $registrationDeadlineLabel = (new DateTimeImmutable($registrationDeadline))->format('d.m.Y'); } catch (Throwable) { $registrationDeadlineLabel = $registrationDeadline; } } $html[] = '
' . auth_escape_html($registrationDeadlineLabel) . '
'; $status = (string) ($row['status'] ?? ''); $statusSignalClass = trim(auth_market_status_signal_class($status)); $statusValue = '' . auth_escape_html(auth_market_status_label($status)) . ''; $html[] = '
' . $statusValue . '
'; $soldItemCount = (int) round((float) ($row['sold_item_count'] ?? 0)); $soldGrossRevenue = number_format((float) ($row['sold_gross_revenue'] ?? 0), 0, '.', "'"); $html[] = '
' . $soldItemCount . '/' . $soldGrossRevenue . 'CHF
'; $html[] = '
'; } if ($hasMore) { $html[] = ''; } $html[] = '
'; return implode('', $html); } function auth_render_markets_content(array $marketTable): string { return '
' . auth_render_markets_large_table($marketTable) . '' . '
'; } function auth_render_market_definitions_content(array $marketTable): string { $rows = $marketTable['rows'] ?? $marketTable; $categories = $marketTable['categories'] ?? []; $organizers = $marketTable['organizers'] ?? []; $html = []; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
Märkte
'; $html[] = '
'; foreach (['Markt', 'Kategorie', 'Veranstalter', 'Website', 'Durchführungen', 'Status'] as $label) { $html[] = '
' . auth_escape_html($label) . '
'; } $html[] = '
'; foreach ($rows as $index => $row) { $rowClass = 'sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row'; if ($index % 2 === 1) { $rowClass .= ' sg-large-table__row--striped-light'; } $website = (string) ($row['website_url'] ?? ''); $websiteValue = $website === '' ? 'Nicht hinterlegt' : $website; $status = ($row['archived_at'] ?? null) === null ? 'Aktiv' : 'Archiviert'; $marketId = (int) ($row['id'] ?? 0); $marketName = (string) ($row['name'] ?? ''); $organizerName = (string) ($row['organizer_name'] ?? ''); $html[] = '
'; $html[] = ''; $html[] = '
' . auth_escape_html((string) ($row['category_name'] ?? $row['category_code'] ?? '')) . '
'; $html[] = '
' . auth_escape_html($organizerName) . '
'; $html[] = '
' . auth_escape_html($websiteValue) . '
'; $html[] = '
' . auth_escape_html((string) ($row['occurrence_count'] ?? 0)) . '
'; $html[] = '
' . auth_escape_html($status) . '
'; $html[] = '
'; } $html[] = '
'; $html[] = ''; $html[] = '
'; return implode('', $html); } function auth_render_bestellungen_large_table(array $bestellungenTable): string { $rows = $bestellungenTable['rows'] ?? []; $search = (string) ($bestellungenTable['search'] ?? ''); $sortColumn = (string) ($bestellungenTable['sort_column'] ?? 'order_date'); $sortDirection = strtoupper((string) ($bestellungenTable['sort_direction'] ?? 'DESC')); $limit = (int) ($bestellungenTable['limit'] ?? 20); $pageSize = (int) ($bestellungenTable['page_size'] ?? 20); $hasMore = (bool) ($bestellungenTable['has_more'] ?? false); $nextLimit = (int) ($bestellungenTable['next_limit'] ?? $limit); $sortColumns = [ 'order_date' => 'Bestelldatum', 'external_ref' => 'Bestellnummer', 'name' => 'Name', 'total_amount' => 'Gesamtsumme', ]; $html = []; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
Bestellungen
'; $html[] = ''; $html[] = ''; $html[] = ''; $html[] = ''; $html[] = ''; $html[] = ''; $html[] = '
'; $html[] = '
'; $headerIndex = 0; foreach ($sortColumns as $column => $label) { $isActive = $column === $sortColumn; $headerDirection = $isActive ? strtolower($sortDirection) : 'none'; $iconDirection = $isActive ? strtolower($sortDirection) : 'ascending'; $ariaLabel = $isActive ? $label . ' ' . ($sortDirection === 'ASC' ? 'aufsteigend sortiert' : 'absteigend sortiert') : $label . ' sortieren'; $targetDirection = $isActive ? ($sortDirection === 'ASC' ? 'DESC' : 'ASC') : ($column === 'order_date' ? 'DESC' : 'ASC'); $amountHeaderClass = $column === 'total_amount' ? ' sg-large-table__cell--amount' : ''; $html[] = '
'; $html[] = ''; $html[] = '
'; $headerIndex++; } $html[] = '
'; foreach ($rows as $index => $row) { $isStriped = $index % 2 === 1; $rowClass = 'sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row'; if ($isStriped) { $rowClass .= ' sg-large-table__row--striped-light'; } $orderNumber = (string) ($row['external_ref'] ?? ''); $orderDate = auth_format_bestellung_date((string) ($row['order_date'] ?? '')); $totalAmount = auth_format_bestellung_total(isset($row['total_amount']) ? (float) $row['total_amount'] : null); $firstName = (string) ($row['first_name'] ?? ''); $lastName = (string) ($row['last_name'] ?? ''); $name = trim($firstName . ' ' . $lastName); $street = (string) ($row['street'] ?? ''); $houseNumber = (string) ($row['house_number'] ?? ''); $zip = (string) ($row['zip'] ?? ''); $city = (string) ($row['city'] ?? ''); $country = (string) ($row['country_name'] ?? ''); $html[] = '
'; $html[] = '
' . auth_escape_html($orderDate) . '
'; $html[] = '
'; $html[] = ''; $html[] = '
'; $html[] = '
'; $html[] = ''; $html[] = '
'; $html[] = '
' . auth_escape_html($totalAmount) . '
'; $html[] = '
'; } if ($hasMore) { $loadMoreUrl = auth_build_path_with_query([ 'bestellungen_search' => $search !== '' ? $search : null, 'bestellungen_sort' => $sortColumn, 'bestellungen_dir' => $sortDirection, 'bestellungen_limit' => $nextLimit, ]); $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = 'Mehr laden'; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
'; $html[] = '
'; } $html[] = '
'; $html[] = '
'; $html[] = ''; $html[] = ''; return implode('', $html); } function auth_render_bestellungen_content(array $bestellungenTable): string { return '
' . '
' . auth_render_bestellungen_large_table($bestellungenTable) . '
'; } function auth_render_lager_table(string $title, array $rows, array $columns, string $tableKey): string { $renderForecast = static function (?string $date, string $status, ?string $expiryDate, bool $available): array { if (!$available) { return ['text' => '-', 'warning' => false, 'mhdWarning' => false]; } if ($date === null || $date === '') { return ['text' => 'Keine Prognose', 'warning' => false, 'mhdWarning' => false]; } $dateObject = new DateTimeImmutable($date); $text = $dateObject->format('d.m.Y'); if ($status === 'depleted') { return ['text' => $text . ' (BESTAND ENDE)', 'warning' => true, 'mhdWarning' => false]; } $warning = false; $mhdWarning = false; if ($expiryDate !== null && $expiryDate !== '') { $expiryObject = new DateTimeImmutable($expiryDate); $warning = $dateObject >= $expiryObject->modify('-3 months') && $dateObject <= $expiryObject; $mhdWarning = $dateObject > $expiryObject; if ($warning || $mhdWarning) { $text .= ' (MHD: ' . $expiryObject->format('d.m.Y') . ')'; } } return ['text' => $text, 'warning' => $warning, 'mhdWarning' => $mhdWarning]; }; $html = []; $html[] = '
'; $html[] = '
' . auth_escape_html($title) . '
'; $html[] = '
'; foreach ($columns as $label) { $html[] = '
' . auth_escape_html($label) . '
'; } $html[] = '
'; foreach ($rows as $index => $row) { $rowClass = 'sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row'; if ($index % 2 === 1) { $rowClass .= ' sg-large-table__row--striped-light'; } $id = (int) ($row['id'] ?? 0); $name = (string) ($row['name'] ?? $row['display_name'] ?? ''); $value = $tableKey === 'products' ? (string) max(0, (int) round((float) ($row['available_qty'] ?? 0))) : (string) ($row['item_code'] ?? ''); $currentForecast = ['text' => '', 'warning' => false, 'mhdWarning' => false]; $nextForecast = ['text' => '-', 'warning' => false, 'mhdWarning' => false]; if ($tableKey === 'products') { $currentForecast = $renderForecast( $row['sellout_date'] ?? null, (string) ($row['sellout_forecast_status'] ?? 'no_demand'), $row['expiry_date'] ?? null, true ); $nextForecast = $renderForecast( $row['next_sellout_date'] ?? null, (string) ($row['next_sellout_forecast_status'] ?? 'no_demand'), $row['next_expiry_date'] ?? null, $row['next_lot_id'] !== null ); } $nameData = ' data-lager-drawer-name="' . auth_escape_html($name) . '"'; $valueData = ' data-lager-drawer-value="' . auth_escape_html($value) . '"'; $typeData = ' data-lager-drawer-type="' . auth_escape_html($tableKey === 'products' ? 'Produkt' : 'Artikel') . '"'; $detailData = ''; if ($tableKey === 'products') { $detailData = ' data-lager-drawer-forecast="' . auth_escape_html($currentForecast['text']) . '"' . ' data-lager-drawer-forecast-status="' . auth_escape_html((string) ($row['sellout_forecast_status'] ?? 'no_demand')) . '"' . ' data-lager-drawer-forecast-warning="' . ($currentForecast['warning'] ? 'true' : 'false') . '"' . ' data-lager-drawer-forecast-mhd-warning="' . ($currentForecast['mhdWarning'] ? 'true' : 'false') . '"' . ' data-lager-drawer-current-qty="' . auth_escape_html(auth_format_lager_quantity((float) ($row['current_qty'] ?? 0))) . '"' . ' data-lager-drawer-qty-in="' . auth_escape_html(auth_format_lager_quantity((float) ($row['qty_in'] ?? 0))) . '"' . ' data-lager-drawer-virtual-shortage-qty="' . auth_escape_html(auth_format_lager_quantity((float) ($row['virtual_shortage_qty'] ?? 0))) . '"' . ' data-lager-drawer-lot-id="' . auth_escape_html((string) ($row['lot_id'] ?? '')) . '"' . ' data-lager-drawer-lot-number="' . auth_escape_html((string) ($row['lot_number'] ?? '')) . '"' . ' data-lager-drawer-manufacturer-party-id="' . auth_escape_html((string) ($row['manufacturer_party_id'] ?? '')) . '"' . ' data-lager-drawer-manufacturer-name="' . auth_escape_html((string) ($row['manufacturer_name'] ?? '')) . '"' . ' data-lager-drawer-manufacturer-lot-number="' . auth_escape_html((string) ($row['manufacturer_lot_number'] ?? '')) . '"' . ' data-lager-drawer-mfg-date="' . auth_escape_html((string) ($row['mfg_date'] ?? '')) . '"' . ' data-lager-drawer-expiry-date="' . auth_escape_html((string) ($row['expiry_date'] ?? '')) . '"' . ' data-lager-drawer-purchase-date="' . auth_escape_html((string) ($row['purchase_date'] ?? '')) . '"' . ' data-lager-drawer-virtual-lot-number="' . auth_escape_html((string) ($row['virtual_lot_number'] ?? '')) . '"'; $openLot = $row['open_lots'][0] ?? []; $detailData .= ' data-lager-open-lot-id="' . auth_escape_html((string) ($openLot['lot_id'] ?? '')) . '"' . ' data-lager-open-lot-number="' . auth_escape_html((string) ($openLot['lot_number'] ?? '')) . '"' . ' data-lager-open-manufacturer-party-id="' . auth_escape_html((string) ($openLot['manufacturer_party_id'] ?? '')) . '"' . ' data-lager-open-manufacturer-lot-number="' . auth_escape_html((string) ($openLot['manufacturer_lot_number'] ?? '')) . '"' . ' data-lager-open-mfg-date="' . auth_escape_html((string) ($openLot['mfg_date'] ?? '')) . '"' . ' data-lager-open-expiry-date="' . auth_escape_html((string) ($openLot['expiry_date'] ?? '')) . '"' . ' data-lager-open-purchase-date="' . auth_escape_html((string) ($openLot['purchase_date'] ?? '')) . '"'; } $html[] = '
'; $html[] = ''; $valueCell = $tableKey === 'products' ? '
' . auth_escape_html($value) . '
' : ''; $html[] = $valueCell; if ($tableKey === 'products') { $forecastClass = $currentForecast['warning'] ? ' sg-strong lager-forecast-warning' : ($currentForecast['mhdWarning'] ? ' sg-strong lager-forecast-mhd-warning' : ''); $nextForecastClass = $nextForecast['warning'] ? ' sg-strong lager-forecast-warning' : ($nextForecast['mhdWarning'] ? ' sg-strong lager-forecast-mhd-warning' : ''); $html[] = '
' . auth_escape_html($currentForecast['text']) . '
'; $html[] = '
' . auth_escape_html((string) max(0, (int) round((float) ($row['next_qty'] ?? 0)))) . '
'; $html[] = '
' . auth_escape_html($nextForecast['text']) . '
'; } $html[] = '
'; } $html[] = '
'; return implode('', $html); } function auth_format_lager_date(?string $value): string { if ($value === null || $value === '') { return 'Nicht hinterlegt'; } return (new DateTimeImmutable($value))->format(str_contains($value, ':') ? 'd.m.Y H:i' : 'd.m.Y'); } function auth_format_lager_quantity(float $value): string { return fmod($value, 1.0) === 0.0 ? number_format($value, 0, ',', '.') : number_format($value, 2, ',', '.'); } function auth_render_lager_historical_lots(array $lagerProducts): string { $html = []; foreach ($lagerProducts as $product) { $productId = (int) ($product['id'] ?? 0); $html[] = ''; } return implode('', $html); } function auth_render_lager_open_lots(array $lagerProducts, string $manufacturerOptions): string { $html = []; foreach ($lagerProducts as $product) { $productId = (int) ($product['id'] ?? 0); $html[] = ''; } return implode('', $html); } function auth_render_lager_content(array $lagerProducts, array $lagerArtikel, array $lagerManufacturers, string $csrfToken, string $sectionTitle = 'Lager'): string { $manufacturerOptions = ''; foreach ($lagerManufacturers as $manufacturer) { $manufacturerOptions .= ''; } $tables = $sectionTitle === 'Lager' ? auth_render_lager_table('Produkte', $lagerProducts, ['Produktname', 'Lager aktuell', 'Verkauf bis aktuell', 'Lager neu', 'Verkauf bis neu'], 'products') : auth_render_lager_table('Artikel', $lagerArtikel, ['Artikel-Name', 'Artikelnummer'], 'articles'); $openLotCardTemplate = auth_render_lager_open_lots([[ 'id' => 0, 'open_lots' => [[ 'lot_id' => 0, 'lot_number' => null, 'manufacturer_party_id' => null, 'manufacturer_name' => null, 'manufacturer_lot_number' => null, 'mfg_date' => null, 'expiry_date' => null, 'purchase_date' => null, 'qty_net' => 0, 'qty_in' => 0, 'next_active' => false, ]], ]], $manufacturerOptions); return '
' . '
' . $tables . '
' . '' . '' . '
'; } function render_auth_home_page(array $user, array $otcProducts = [], array $otcMarketOptions = [], array $bestellungenTable = [], array $marketOccurrencesTable = [], array $lagerProducts = [], array $lagerArtikel = [], array $lagerManufacturers = [], array $marketDefinitionsTable = []): void { $otcProductRows = ''; foreach ($otcProducts as $product) { $productId = (int) ($product['id'] ?? 0); $productName = auth_escape_html((string) ($product['name'] ?? '')); $productSku = auth_escape_html((string) ($product['sku'] ?? '')); $availableQty = (int) max(0, (int) round((float) ($product['available_qty'] ?? 0))); $hasSellableMapping = (bool) ($product['has_sellable_mapping'] ?? false); $inputId = 'otc-product-' . $productId; $mappingNote = $hasSellableMapping ? '' : ' (Artikel-Mapping fehlt)'; $disabledAttribute = $hasSellableMapping ? '' : ' disabled aria-disabled="true"'; $otcProductRows .= '
'; $otcProductRows .= ''; $otcProductRows .= '
'; } $otcMarketOccurrenceOptions = []; $otcMarketDayOptions = ''; foreach ($otcMarketOptions as $option) { $occurrenceId = (int) ($option['occurrence_id'] ?? 0); $marketDefinitionId = (int) ($option['market_definition_id'] ?? 0); $dayId = (int) ($option['day_id'] ?? 0); if ($occurrenceId <= 0 || $marketDefinitionId <= 0 || $dayId <= 0) { continue; } $marketName = (string) ($option['market_name'] ?? ''); $otcMarketOccurrenceOptions[$marketDefinitionId] = $marketName; $day = (string) ($option['date'] ?? ''); $dayLabel = $day === '' ? '' : (new DateTimeImmutable($day))->format('d.m.Y'); $otcMarketDayOptions .= ''; } $otcMarketOccurrenceOptionHtml = ''; foreach ($otcMarketOccurrenceOptions as $marketDefinitionId => $marketName) { $otcMarketOccurrenceOptionHtml .= ''; } if ($otcProductRows === '') { $otcProductRows = '

Keine aktiven OTC-Produkte erfasst.

'; } $moduleNavigation = json_encode( [ 'Übersicht' => [], 'ERP' => ['Bestellungen', 'Lager', 'Produkte', 'Artikel', 'Kontakte'], 'Märkte' => ['Marktdurchführungen', 'Märkte'], 'Buchhaltung' => [], 'Kundenberatung' => [], ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); $bestellungenContent = auth_render_bestellungen_content($bestellungenTable); $marketsContent = auth_render_markets_content($marketOccurrencesTable); $marketDefinitionsContent = auth_render_market_definitions_content($marketDefinitionsTable); $lagerContent = auth_render_lager_content($lagerProducts, $lagerArtikel, $lagerManufacturers, auth_csrf_token()); $moduleContentCards = json_encode( [ 'Bestellungen' => $bestellungenContent, 'Marktdurchführungen' => $marketsContent, 'Märkte' => $marketDefinitionsContent, 'Lager' => $lagerContent, 'Artikel' => auth_render_lager_content([], $lagerArtikel, $lagerManufacturers, auth_csrf_token(), 'Artikel'), ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); $moduleHeadings = json_encode( [ 'Übersicht' => 'Willkommen bei Naurua Übersicht', 'ERP' => 'Willkommen bei Naurua ERP', 'Märkte' => 'Willkommen bei Naurua Märkte', 'Buchhaltung' => 'Willkommen bei Naurua Buchhaltung', 'Kundenberatung' => 'Willkommen bei Naurua Kundenberatung', ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); echo ''; echo ''; echo ''; echo ''; echo ''; echo 'Naurua ERP'; echo ''; echo ''; echo ''; echo ''; echo '
'; echo '
'; echo '
'; echo '
'; echo '

Naurua ERP

'; echo '
'; echo ''; echo '
'; echo 'Startseite'; echo 'Abmelden'; echo '
'; echo '
'; echo ''; echo '
'; echo '
'; echo '
'; echo '
'; echo '

Willkommen bei Naurua ERP

'; echo '
'; echo '
'; echo '
'; echo ''; echo ''; echo '
'; echo '
'; echo '
'; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; }