Lager-Tab mit Produkt- und Artikel-Tabellen erweitern
This commit is contained in:
1 parent
8ed5dba3d8
commit
3334913915
5 files changed
+146
-2
No files matched your search
@@ -1,6 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
function get_lager_bundle_overview(PDO $pdo): array
|
||||
{
|
||||
$stmt = $pdo->query(
|
||||
"SELECT
|
||||
si.id,
|
||||
si.item_code,
|
||||
si.display_name
|
||||
FROM sellable_item si
|
||||
INNER JOIN sellable_item_component sic
|
||||
ON sic.sellable_item_id = si.id
|
||||
WHERE si.status = 'active'
|
||||
GROUP BY si.id, si.item_code, si.display_name
|
||||
HAVING COUNT(DISTINCT sic.product_id) > 1
|
||||
ORDER BY si.display_name ASC, si.id ASC"
|
||||
);
|
||||
|
||||
return array_map(
|
||||
static fn (array $row): array => [
|
||||
'id' => (int) $row['id'],
|
||||
'item_code' => (string) $row['item_code'],
|
||||
'display_name' => (string) $row['display_name'],
|
||||
],
|
||||
$stmt->fetchAll()
|
||||
);
|
||||
}
|
||||
|
||||
function normalize_title_key(string $value): string
|
||||
{
|
||||
$value = trim(mb_strtolower($value, 'UTF-8'));
|
||||
|
||||
@@ -50,6 +50,38 @@ function get_otc_order_form_products(PDO $pdo): array
|
||||
return $products;
|
||||
}
|
||||
|
||||
function get_lager_product_overview(PDO $pdo): array
|
||||
{
|
||||
$stmt = $pdo->query(
|
||||
"SELECT
|
||||
p.id,
|
||||
p.name,
|
||||
p.sku,
|
||||
p.uom,
|
||||
COALESCE(SUM(v.qty_net), 0) AS available_qty
|
||||
FROM product p
|
||||
LEFT JOIN stock_lot sl
|
||||
ON sl.product_id = p.id
|
||||
AND sl.status = 'current'
|
||||
LEFT JOIN v_stock_lot_balance v
|
||||
ON v.stock_lot_id = sl.id
|
||||
WHERE p.status = 'active'
|
||||
GROUP BY p.id, p.name, p.sku, p.uom
|
||||
ORDER BY p.name ASC, p.id ASC"
|
||||
);
|
||||
|
||||
return array_map(
|
||||
static fn (array $row): array => [
|
||||
'id' => (int) $row['id'],
|
||||
'name' => (string) $row['name'],
|
||||
'sku' => (string) $row['sku'],
|
||||
'uom' => (string) $row['uom'],
|
||||
'available_qty' => (float) $row['available_qty'],
|
||||
],
|
||||
$stmt->fetchAll()
|
||||
);
|
||||
}
|
||||
|
||||
function get_current_lot_balance_for_update(PDO $pdo, int $productId): array
|
||||
{
|
||||
$stmt = $pdo->prepare(
|
||||
|
||||
@@ -201,7 +201,58 @@ function auth_render_bestellungen_content(array $bestellungenTable): string
|
||||
. '</div>';
|
||||
}
|
||||
|
||||
function render_auth_home_page(array $user, array $otcProducts = [], array $bestellungenTable = []): void
|
||||
function auth_render_lager_table(string $title, array $rows, array $columns, string $tableKey): string
|
||||
{
|
||||
$html = [];
|
||||
$html[] = '<article class="sg-card sg-large-table sg-large-table--lager" data-component="large-table" data-lager-table="true" data-lager-table-key="' . auth_escape_html($tableKey) . '" role="table" aria-label="' . auth_escape_html($title) . '">';
|
||||
$html[] = '<div class="sg-card-segment sg-card-segment--header sg-card-segment--darkblue sg-large-table__title-segment" data-component-part="large-table-header"><div class="sg-strong">' . auth_escape_html($title) . '</div></div>';
|
||||
$html[] = '<div class="sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row sg-large-table__row--header" role="row" data-component-part="large-table-header-row">';
|
||||
foreach ($columns as $label) {
|
||||
$html[] = '<div class="sg-large-table__cell sg-large-table__cell--header" role="columnheader"><span class="sg-large-table__sort-label">' . auth_escape_html($label) . '</span></div>';
|
||||
}
|
||||
$html[] = '</div>';
|
||||
|
||||
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 = (string) max(0, (int) round((float) ($row['available_qty'] ?? 0)));
|
||||
$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') . '"';
|
||||
|
||||
$html[] = '<div class="' . $rowClass . '" role="row" data-component-part="large-table-row" data-lager-row="true" data-lager-id="' . $id . '">';
|
||||
$html[] = '<div class="sg-large-table__cell" role="cell"><a class="sg-hyperlink" href="#" aria-haspopup="dialog" aria-expanded="false" data-lager-drawer-open="true"' . $nameData . $valueData . $typeData . '>' . auth_escape_html($name) . '</a></div>';
|
||||
if ($tableKey === 'products') {
|
||||
$html[] = '<div class="sg-large-table__cell" role="cell"><a class="sg-hyperlink" href="#" aria-haspopup="dialog" aria-expanded="false" data-lager-drawer-open="true"' . $nameData . $valueData . $typeData . '>' . auth_escape_html($value) . '</a></div>';
|
||||
}
|
||||
$html[] = '</div>';
|
||||
}
|
||||
|
||||
$html[] = '</article>';
|
||||
return implode('', $html);
|
||||
}
|
||||
|
||||
function auth_render_lager_content(array $lagerProducts, array $lagerBundles): string
|
||||
{
|
||||
return '<div data-lager-content="true">'
|
||||
. '<section class="sg-card-list-page" aria-label="Lager">'
|
||||
. auth_render_lager_table('Produkte', $lagerProducts, ['Produktname', 'Anzahl im Lager'], 'products')
|
||||
. auth_render_lager_table('Artikel', $lagerBundles, ['Artikel-Name'], 'bundles')
|
||||
. '</section>'
|
||||
. '<div class="sg-card-list-page-drawer-backdrop" aria-hidden="true" data-lager-drawer-backdrop="true" data-open="false"></div>'
|
||||
. '<aside class="sg-card-list-page-drawer" aria-label="Lager-Details" aria-hidden="true" data-lager-drawer="true" data-open="false">'
|
||||
. '<div class="sg-card-list-page-drawer__header"><h2 class="sg-heading-h2 sg-card-list-page-drawer__title" data-lager-drawer-title>Lager-Details</h2><button class="sg-interaction-element sg-hyperlink" type="button" data-lager-drawer-close>schliessen</button></div>'
|
||||
. '<div class="sg-card-list-page-drawer__content"><article class="sg-card sg-vsf-drawer-card" data-component="card" data-pattern="card" aria-label="Lager-Details"><header class="sg-card-segment sg-card-segment--header sg-card-segment--darkblue sg-vsf-drawer-card__header"><div class="sg-strong">Lager-Details</div></header><div class="sg-card-segment sg-card-segment--body sg-vsf-drawer-card__content"><p class="sg-body"><strong>Name:</strong> <span data-lager-drawer-name-value></span></p><p class="sg-body" data-lager-drawer-value-row><strong data-lager-drawer-value-label>Wert:</strong> <span data-lager-drawer-value-value></span></p></div></article></div>'
|
||||
. '</aside>'
|
||||
. '</div>';
|
||||
}
|
||||
|
||||
function render_auth_home_page(array $user, array $otcProducts = [], array $bestellungenTable = [], array $lagerProducts = [], array $lagerBundles = []): void
|
||||
{
|
||||
$otcProductRows = '';
|
||||
foreach ($otcProducts as $product) {
|
||||
@@ -230,9 +281,11 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
|
||||
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
|
||||
);
|
||||
$bestellungenContent = auth_render_bestellungen_content($bestellungenTable);
|
||||
$lagerContent = auth_render_lager_content($lagerProducts, $lagerBundles);
|
||||
$moduleContentCards = json_encode(
|
||||
[
|
||||
'Bestellungen' => $bestellungenContent,
|
||||
'Lager' => $lagerContent,
|
||||
],
|
||||
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
|
||||
);
|
||||
@@ -417,6 +470,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
|
||||
echo " if (contentTitle) { contentTitle.textContent = activeEntry; }";
|
||||
echo " if (contentBody) { contentBody.innerHTML = portalModuleContentCards[activeEntry] || ''; }";
|
||||
echo " if (activeEntry === 'Bestellungen') { initBestellungenBindings(); }";
|
||||
echo " if (activeEntry === 'Lager') { initLagerBindings(); }";
|
||||
echo " menu.innerHTML = '';";
|
||||
echo " entries.forEach((entry, index) => {";
|
||||
echo " const button = document.createElement('button');";
|
||||
@@ -437,6 +491,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
|
||||
echo " if (contentTitle) { contentTitle.textContent = entryName; }";
|
||||
echo " if (contentBody) { contentBody.innerHTML = portalModuleContentCards[entryName] || ''; }";
|
||||
echo " if (entryName === 'Bestellungen') { initBestellungenBindings(); }";
|
||||
echo " if (entryName === 'Lager') { initLagerBindings(); }";
|
||||
echo " });";
|
||||
echo " });";
|
||||
echo "};";
|
||||
@@ -516,6 +571,26 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
|
||||
echo "})();";
|
||||
echo "let bestellungenTableHandlersInstalled = false;";
|
||||
echo "let bestellungenRealtimeSource = null;";
|
||||
echo "let lagerBindingsInstalled = false;";
|
||||
echo "function initLagerBindings() {";
|
||||
echo " if (lagerBindingsInstalled) { return; }";
|
||||
echo " const contentRoot = document.querySelector('[data-left-navigation-content-body]');";
|
||||
echo " if (!contentRoot || !contentRoot.querySelector('[data-lager-content]')) { return; }";
|
||||
echo " lagerBindingsInstalled = true;";
|
||||
echo " const getDrawer = () => contentRoot.querySelector('[data-lager-drawer]');";
|
||||
echo " const setDrawerState = (open) => {";
|
||||
echo " const drawer = getDrawer(); const backdrop = contentRoot.querySelector('[data-lager-drawer-backdrop]');";
|
||||
echo " if (!drawer || !backdrop) { return; }";
|
||||
echo " drawer.dataset.open = String(open); drawer.setAttribute('aria-hidden', String(!open)); backdrop.dataset.open = String(open); backdrop.setAttribute('aria-hidden', String(!open));";
|
||||
echo " };";
|
||||
echo " const closeDrawer = () => { setDrawerState(false); contentRoot.querySelectorAll('[data-lager-drawer-open]').forEach((trigger) => trigger.setAttribute('aria-expanded', 'false')); };";
|
||||
echo " contentRoot.addEventListener('click', (event) => {";
|
||||
echo " const trigger = event.target.closest('[data-lager-drawer-open]');";
|
||||
echo " if (trigger) { event.preventDefault(); const drawer = getDrawer(); if (!drawer) { return; } const type = trigger.dataset.lagerDrawerType || 'Lager'; const name = trigger.dataset.lagerDrawerName || ''; const value = trigger.dataset.lagerDrawerValue || ''; drawer.querySelector('[data-lager-drawer-title]').textContent = type + ': ' + name; drawer.querySelector('[data-lager-drawer-name-value]').textContent = name; const valueRow = drawer.querySelector('[data-lager-drawer-value-row]'); if (valueRow) { valueRow.hidden = type !== 'Produkt'; } drawer.querySelector('[data-lager-drawer-value-label]').textContent = 'Anzahl im Lager:'; drawer.querySelector('[data-lager-drawer-value-value]').textContent = value; setDrawerState(true); contentRoot.querySelectorAll('[data-lager-drawer-open]').forEach((button) => button.setAttribute('aria-expanded', String(button === trigger))); return; }";
|
||||
echo " if (event.target.closest('[data-lager-drawer-close]') || event.target.closest('[data-lager-drawer-backdrop]')) { closeDrawer(); }";
|
||||
echo " });";
|
||||
echo " document.addEventListener('keydown', (event) => { if (event.key === 'Escape') { closeDrawer(); } });";
|
||||
echo "}";
|
||||
echo "function initBestellungenBindings() {";
|
||||
echo " const contentRoot = document.querySelector('[data-left-navigation-content-body]');";
|
||||
echo " if (!contentRoot) { return; }";
|
||||
|
||||
@@ -157,6 +157,14 @@ body.sg-bestellungen-mobile-detail-open > :not([data-bestellungen-mobile-detail-
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.sg-large-table--lager .sg-large-table__row {
|
||||
grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.sg-large-table--lager .sg-large-table__cell:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
[data-bestellungen-mobile-detail] .sg-navigation-card-layout {
|
||||
margin-top: 0;
|
||||
}
|
||||
+4
-1
@@ -6,6 +6,7 @@ require_once __DIR__ . '/../modules/shared/auth/ui/login.php';
|
||||
require_once __DIR__ . '/../modules/shared/auth/ui/home.php';
|
||||
require_once __DIR__ . '/../modules/erp/bestellungen/service.php';
|
||||
require_once __DIR__ . '/../modules/erp/lager/service.php';
|
||||
require_once __DIR__ . '/../modules/erp/artikel-mapping/service.php';
|
||||
|
||||
$env = expand_env_values(parse_env_file(__DIR__ . '/../.env'));
|
||||
$pdo = connect_database($env);
|
||||
@@ -46,6 +47,8 @@ if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
|
||||
$currentUser = auth_current_user($pdo);
|
||||
if ($currentUser !== null) {
|
||||
$otcProducts = get_otc_order_form_products($pdo);
|
||||
$lagerProducts = get_lager_product_overview($pdo);
|
||||
$lagerBundles = get_lager_bundle_overview($pdo);
|
||||
$bestellungenTable = get_sales_order_overview($pdo, [
|
||||
'search' => (string) ($_GET['bestellungen_search'] ?? ''),
|
||||
'sort_column' => (string) ($_GET['bestellungen_sort'] ?? 'order_date'),
|
||||
@@ -57,7 +60,7 @@ if ($currentUser !== null) {
|
||||
echo auth_render_bestellungen_content($bestellungenTable);
|
||||
exit;
|
||||
}
|
||||
render_auth_home_page($currentUser, $otcProducts, $bestellungenTable);
|
||||
render_auth_home_page($currentUser, $otcProducts, $bestellungenTable, $lagerProducts, $lagerBundles);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user