diff --git a/modules/erp/lager/service.php b/modules/erp/lager/service.php
index 9eb2f00..10f4426 100644
--- a/modules/erp/lager/service.php
+++ b/modules/erp/lager/service.php
@@ -73,9 +73,19 @@ function get_lager_product_overview(PDO $pdo): array
p.sku,
p.uom,
COALESCE(SUM(v.qty_net), 0) AS available_qty,
+ sl.lot_number,
+ sl.supplier_lot_number,
sl.expiry_date,
sl.sellout_date,
- sl.sellout_forecast_status
+ sl.sellout_forecast_status,
+ (
+ SELECT virtual_lot.lot_number
+ FROM stock_lot virtual_lot
+ WHERE virtual_lot.product_id = p.id
+ AND virtual_lot.status = 'virtual_shortage'
+ ORDER BY virtual_lot.id
+ LIMIT 1
+ ) AS virtual_lot_number
FROM product p
LEFT JOIN stock_lot sl
ON sl.product_id = p.id
@@ -83,7 +93,8 @@ function get_lager_product_overview(PDO $pdo): array
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, sl.expiry_date, sl.sellout_date, sl.sellout_forecast_status
+ GROUP BY p.id, p.name, p.sku, p.uom, sl.lot_number, sl.supplier_lot_number,
+ sl.expiry_date, sl.sellout_date, sl.sellout_forecast_status
ORDER BY p.name ASC, p.id ASC"
);
@@ -94,9 +105,12 @@ function get_lager_product_overview(PDO $pdo): array
'sku' => (string) $row['sku'],
'uom' => (string) $row['uom'],
'available_qty' => (float) $row['available_qty'],
+ 'lot_number' => $row['lot_number'],
+ 'supplier_lot_number' => $row['supplier_lot_number'],
'expiry_date' => $row['expiry_date'],
'sellout_date' => $row['sellout_date'],
'sellout_forecast_status' => (string) ($row['sellout_forecast_status'] ?? 'no_demand'),
+ 'virtual_lot_number' => $row['virtual_lot_number'],
],
$stmt->fetchAll()
);
diff --git a/modules/shared/auth/ui/home.php b/modules/shared/auth/ui/home.php
index 3a6ce49..02a0194 100644
--- a/modules/shared/auth/ui/home.php
+++ b/modules/shared/auth/ui/home.php
@@ -251,10 +251,20 @@ function auth_render_lager_table(string $title, array $rows, array $columns, str
$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($forecastText) . '"'
+ . ' data-lager-drawer-forecast-warning="' . ($forecastWarning ? 'true' : 'false') . '"'
+ . ' data-lager-drawer-forecast-mhd-warning="' . ($forecastMhdWarning ? 'true' : 'false') . '"'
+ . ' data-lager-drawer-lot-number="' . auth_escape_html((string) ($row['lot_number'] ?? '')) . '"'
+ . ' data-lager-drawer-supplier-lot-number="' . auth_escape_html((string) ($row['supplier_lot_number'] ?? '')) . '"'
+ . ' data-lager-drawer-expiry-date="' . auth_escape_html((string) ($row['expiry_date'] ?? '')) . '"'
+ . ' data-lager-drawer-virtual-lot-number="' . auth_escape_html((string) ($row['virtual_lot_number'] ?? '')) . '"';
+ }
$html[] = '
';
- $html[] = '
';
- $html[] = '
';
+ $html[] = '
';
+ $html[] = '
';
if ($tableKey === 'products') {
$forecastClass = $forecastWarning
? ' sg-strong lager-forecast-warning'
@@ -278,7 +288,24 @@ function auth_render_lager_content(array $lagerProducts, array $lagerArtikel): s
. '
'
. '
'
. '
';
}
@@ -625,7 +652,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
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 = false; } drawer.querySelector('[data-lager-drawer-value-label]').textContent = type === 'Produkt' ? 'Anzahl im Lager:' : 'Artikelnummer:'; 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 (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 = false; } drawer.querySelector('[data-lager-drawer-value-label]').textContent = type === 'Produkt' ? 'Anzahl im Lager:' : 'Artikelnummer:'; drawer.querySelector('[data-lager-drawer-value-value]').textContent = value; drawer.querySelectorAll('[data-lager-drawer-topic]').forEach((card) => { card.hidden = type !== 'Produkt'; }); if (type === 'Produkt') { const forecast = trigger.dataset.lagerDrawerForecast || 'Keine Prognose'; const warning = trigger.dataset.lagerDrawerForecastWarning === 'true' || trigger.dataset.lagerDrawerForecastMhdWarning === 'true'; const warningValue = drawer.querySelector('[data-lager-drawer-warning-value]'); if (warningValue) { warningValue.textContent = warning ? forecast : 'Keine akute Warnung.'; } drawer.querySelector('[data-lager-drawer-lot-number]').textContent = trigger.dataset.lagerDrawerLotNumber || 'Nicht hinterlegt'; drawer.querySelector('[data-lager-drawer-supplier-lot-number]').textContent = trigger.dataset.lagerDrawerSupplierLotNumber || 'Nicht hinterlegt'; drawer.querySelector('[data-lager-drawer-expiry-date]').textContent = trigger.dataset.lagerDrawerExpiryDate || 'Nicht hinterlegt'; const virtualLot = trigger.dataset.lagerDrawerVirtualLotNumber || ''; drawer.querySelector('[data-lager-drawer-virtual-lot-value]').textContent = virtualLot ? 'Virtuelle Charge: ' + virtualLot : 'Keine virtuelle Fehlbestandscharge vorhanden.'; } 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(); } });";