From f8f011f96c7769e298cb1f93c00e54ab14d58f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Gla=CC=88ser?= Date: Tue, 8 Sep 2026 15:08:27 +0200 Subject: [PATCH] Keep lot drawer open after creation --- modules/erp/lager/api/lot.php | 1 + modules/erp/lager/service.php | 12 ++++++++++++ modules/shared/auth/ui/home.php | 23 +++++++++++++++++++---- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/modules/erp/lager/api/lot.php b/modules/erp/lager/api/lot.php index b91446f..b955489 100644 --- a/modules/erp/lager/api/lot.php +++ b/modules/erp/lager/api/lot.php @@ -122,6 +122,7 @@ try { $lotNumber === '' ); sync_open_lot_activation_selection($pdo, $productId); + $preparedLot['next_active'] = is_open_lot_selected_for_activation($pdo, $lotId); refresh_lager_stock_lot_forecast_for_product($pdo, $productId); $forecast = get_lager_product_forecast($pdo, $productId); json_response(200, ['ok' => true, 'productId' => $productId, 'lot' => $preparedLot, 'balance' => get_stock_lot_balance($pdo, $lotId), 'availableQty' => get_lager_product_available_qty($pdo, $productId), 'forecast' => $forecast]); diff --git a/modules/erp/lager/service.php b/modules/erp/lager/service.php index 2c48c98..853c363 100644 --- a/modules/erp/lager/service.php +++ b/modules/erp/lager/service.php @@ -490,6 +490,18 @@ function is_qualified_open_lot(PDO $pdo, int $lotId): bool return (bool) $stmt->fetchColumn(); } +function is_open_lot_selected_for_activation(PDO $pdo, int $lotId): bool +{ + $stmt = $pdo->prepare( + "SELECT next_active + FROM stock_lot + WHERE id = :lot_id + AND status = 'open'" + ); + $stmt->execute([':lot_id' => $lotId]); + return (bool) $stmt->fetchColumn(); +} + function sync_open_lot_activation_selection(PDO $pdo, int $productId): void { $lockStmt = $pdo->prepare( diff --git a/modules/shared/auth/ui/home.php b/modules/shared/auth/ui/home.php index 222b822..8163751 100644 --- a/modules/shared/auth/ui/home.php +++ b/modules/shared/auth/ui/home.php @@ -384,6 +384,22 @@ function auth_render_lager_content(array $lagerProducts, array $lagerArtikel, ar $tables = $sectionTitle === 'Lager' ? auth_render_lager_table('Produkte', $lagerProducts, ['Produktname', 'Anzahl im Lager', 'Verbraucht bis'], '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 '
' . '
' @@ -416,6 +432,7 @@ function auth_render_lager_content(array $lagerProducts, array $lagerArtikel, ar . '

' . '' . '' + . '' . '
' . auth_render_lager_open_lots($lagerProducts, $manufacturerOptions) . '
' . '
' . '
Aktive Charge
' @@ -727,8 +744,6 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best echo " });"; echo " });"; echo "});"; - echo "const restoreLagerDrawerProductId = window.sessionStorage.getItem('lager-drawer-restore-product-id');"; - echo "if (restoreLagerDrawerProductId) { const erpTab = Array.from(document.querySelectorAll('.sg-portal-header__tabs .sg-tab-button')).find((button) => button.textContent.trim() === 'ERP'); erpTab?.click(); const lagerTab = Array.from(document.querySelectorAll('#left-navigation-menu .sg-tab-button')).find((button) => button.textContent.trim() === 'Lager'); lagerTab?.click(); }"; echo "document.querySelectorAll('.sg-sandwich-menu-wrap').forEach((wrap) => {"; echo " const button = wrap.querySelector('.sg-sandwich-button');"; echo " button.addEventListener('click', (event) => {"; @@ -842,6 +857,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best echo " const syncLagerBalanceView = (balance) => { const drawer = getDrawer(); if (!drawer || !balance) { return; } const currentQty = drawer.querySelector('[data-lager-drawer-current-qty]'); const qtyIn = drawer.querySelector('[data-lager-drawer-qty-in]'); if (currentQty) { currentQty.textContent = String(balance.qty_net ?? 0); } if (qtyIn) { qtyIn.textContent = String(balance.qty_in ?? 0); } const trigger = contentRoot.querySelector('[data-lager-drawer-open][aria-expanded=\"true\"]'); if (trigger) { trigger.dataset.lagerDrawerCurrentQty = String(balance.qty_net ?? 0); trigger.dataset.lagerDrawerQtyIn = String(balance.qty_in ?? 0); } };"; echo " const clearLagerMovementInputs = () => { const drawer = getDrawer(); if (!drawer) { return; } drawer.querySelectorAll('[data-lager-receipt-qty], [data-lager-adjustment-qty], [data-lager-out-qty]').forEach((input) => { input.value = ''; }); };"; echo " const syncLagerOpenLotCard = (lot, balance) => { const drawer = getDrawer(); const card = drawer?.querySelector('[data-lager-open-card=\"' + (lot?.id || '') + '\"]'); if (!card) { return; } const values = { '[data-lager-open-card-lot-number]': lot?.lot_number || 'Nicht hinterlegt', '[data-lager-open-card-qty-net]': String(balance?.qty_net ?? 0), '[data-lager-open-card-qty-in]': String(balance?.qty_net ?? 0), '[data-lager-open-card-manufacturer]': drawer.querySelector('[data-lager-new-manufacturer]')?.selectedOptions?.[0]?.textContent || 'Nicht hinterlegt', '[data-lager-open-card-manufacturer-lot]': lot?.manufacturer_lot_number || 'Nicht hinterlegt', '[data-lager-open-card-mfg-date]': lot?.mfg_date || 'Nicht hinterlegt', '[data-lager-open-card-expiry-date]': lot?.expiry_date || 'Nicht hinterlegt', '[data-lager-open-card-purchase-date]': lot?.purchase_date || 'Nicht hinterlegt' }; Object.entries(values).forEach(([selector, value]) => { const target = card.querySelector(selector); if (target) { target.textContent = value; } }); };"; + echo " const appendLagerOpenLotCard = (lot, balance) => { const drawer = getDrawer(); const product = drawer?.querySelector('[data-lager-open-product=\"' + (drawer?.dataset.lagerProductId || '') + '\"]'); const templateProduct = drawer?.querySelector('[data-lager-open-lot-template]')?.content.querySelector('[data-lager-open-product]'); const card = templateProduct?.querySelector('[data-lager-open-card-wrapper]')?.cloneNode(true); if (!product || !card || !lot?.id) { return null; } const lotId = String(lot.id); card.dataset.lagerOpenCardWrapper = lotId; const readView = card.querySelector('[data-lager-open-card]'); if (readView) { readView.dataset.lagerOpenCard = lotId; } const editTitle = card.querySelector('[id^=\"lager-open-charge-data-\"]'); if (editTitle) { editTitle.id = 'lager-open-charge-data-' + lotId; editTitle.closest('section')?.setAttribute('aria-labelledby', editTitle.id); } const confirmationTitle = card.querySelector('[id^=\"lager-open-confirmation-title-\"]'); if (confirmationTitle) { confirmationTitle.id = 'lager-open-confirmation-title-' + lotId; confirmationTitle.closest('[data-lager-open-confirmation-card]')?.setAttribute('aria-labelledby', confirmationTitle.id); } const deleteTitle = card.querySelector('[id^=\"lager-open-delete-title-\"]'); if (deleteTitle) { deleteTitle.id = 'lager-open-delete-title-' + lotId; deleteTitle.closest('[data-lager-open-delete-confirmation]')?.setAttribute('aria-labelledby', deleteTitle.id); } const setValue = (selector, value) => { const input = card.querySelector(selector); if (input) { input.value = value ?? ''; } }; setValue('[data-lager-open-lot-number]', lot.lot_number); setValue('[data-lager-open-manufacturer]', lot.manufacturer_party_id); setValue('[data-lager-open-manufacturer-lot]', lot.manufacturer_lot_number); setValue('[data-lager-open-mfg-date]', lot.mfg_date); setValue('[data-lager-open-expiry-date]', lot.expiry_date); setValue('[data-lager-open-purchase-date]', lot.purchase_date); setValue('[data-lager-open-target-qty]', balance?.qty_net ?? 0); const qualified = Boolean(lot.lot_number && lot.manufacturer_party_id && lot.manufacturer_lot_number && lot.mfg_date && lot.expiry_date && lot.purchase_date && Number(balance?.qty_net || 0) > 0); const selected = lot.next_active === true; card.classList.toggle('sg-lager-open-lot--next-active', selected); const selectedLabel = card.querySelector('[data-lager-open-card-next-active]'); if (selectedLabel) { selectedLabel.textContent = selected ? 'Als nächste aktive Charge ausgewählt' : 'Nicht ausgewählt'; } const selectButton = card.querySelector('[data-lager-open-select]'); if (selectButton) { selectButton.disabled = !qualified; selectButton.setAttribute('aria-disabled', String(!qualified)); } product.append(card); syncLagerOpenLotCard(lot, balance); return card; };"; echo " const resetLagerNewLotConfirmation = () => { const drawer = getDrawer(); const confirmation = drawer?.querySelector('[data-lager-new-confirmation-card]'); const list = drawer?.querySelector('[data-lager-new-confirmation-empty-fields]'); const form = drawer?.querySelector('[data-lager-new-lot-form-element]'); if (confirmation) { confirmation.hidden = true; } if (list) { list.replaceChildren(); } if (form) { form.dataset.confirmationAccepted = 'false'; } if (drawer) { drawer.dataset.dialogOpen = 'false'; } };"; echo " const resetLagerOpenLotConfirmations = () => { const drawer = getDrawer(); drawer?.querySelectorAll('[data-lager-open-card-wrapper]').forEach((card) => { const confirmation = card.querySelector('[data-lager-open-confirmation-card]'); const list = card.querySelector('[data-lager-open-confirmation-empty-fields]'); const form = card.querySelector('[data-lager-open-edit-form-element]'); if (confirmation) { confirmation.hidden = true; } if (list) { list.replaceChildren(); } if (form) { form.dataset.confirmationAccepted = 'false'; } card.dataset.dialogOpen = 'false'; }); };"; echo " document.addEventListener('click', (event) => { const trigger = event.target.closest('[data-lager-drawer-open]'); if (!trigger || trigger.dataset.lagerDrawerType !== 'Produkt' || window.matchMedia('(max-width: 767px)').matches) { return; } const drawer = getDrawer(); const productId = trigger.closest('[data-lager-row]')?.dataset.lagerId || ''; const openProduct = drawer?.querySelector('[data-lager-open-product=\"' + productId + '\"]'); drawer?.querySelectorAll('[data-lager-open-product]').forEach((product) => { product.hidden = product !== openProduct; product.querySelectorAll('[data-lager-open-edit-form]').forEach((form) => { form.hidden = true; }); product.querySelectorAll('[data-lager-open-card]').forEach((readView) => { readView.hidden = false; }); }); resetLagerNewLotConfirmation(); resetLagerOpenLotConfirmations(); const newForm = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-form\"]'); const newAction = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-action\"]'); if (newForm) { newForm.hidden = true; } if (newAction) { newAction.hidden = false; } const notificationWrapper = drawer?.querySelector('[data-lager-new-lot-notification-wrapper]'); const notification = drawer?.querySelector('[data-lager-new-lot-notification]'); const shortageQty = trigger.dataset.lagerDrawerVirtualShortageQty || '0'; const hasShortage = shortageQty !== '0' && shortageQty !== '0,00'; if (notificationWrapper) { notificationWrapper.hidden = !hasShortage; } if (notification && hasShortage) { notification.textContent = 'Für dieses Produkt bestehen virtuelle Fehlbestände in Höhe von ' + shortageQty + '. Die Eingangsmenge wird nach Aktivierung der Charge zur Nachallokation verwendet.'; } const setValue = (selector, value) => { const field = drawer?.querySelector(selector); if (field) { field.value = value || ''; } }; setValue('[data-lager-new-lot-number]', ''); setValue('[data-lager-new-manufacturer]', ''); setValue('[data-lager-new-manufacturer-lot]', ''); setValue('[data-lager-new-mfg-date]', ''); setValue('[data-lager-new-expiry-date]', ''); setValue('[data-lager-new-purchase-date]', ''); setValue('[data-lager-new-receipt-qty]', ''); if (drawer) { drawer.dataset.lagerOpenLotId = ''; drawer.dataset.lagerProductId = productId; } });"; @@ -853,7 +869,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best echo " document.addEventListener('click', (event) => { const cancel = event.target.closest('[data-lager-open-confirmation-cancel]'); if (cancel) { const card = cancel.closest('[data-lager-open-card-wrapper]'); const confirmation = card?.querySelector('[data-lager-open-confirmation-card]'); if (confirmation) { confirmation.hidden = true; } if (card) { card.dataset.dialogOpen = 'false'; } const form = card?.querySelector('[data-lager-open-edit-form-element]'); if (form) { form.dataset.confirmationAccepted = 'false'; } return; } const confirm = event.target.closest('[data-lager-open-confirmation-submit]'); if (confirm) { const card = confirm.closest('[data-lager-open-card-wrapper]'); const confirmation = card?.querySelector('[data-lager-open-confirmation-card]'); const form = card?.querySelector('[data-lager-open-edit-form-element]'); if (confirmation) { confirmation.hidden = true; } if (card) { card.dataset.dialogOpen = 'false'; } if (form) { form.dataset.confirmationAccepted = 'true'; form.requestSubmit(); } } });"; echo " document.addEventListener('submit', async (event) => { const openLotForm = event.target.closest('[data-lager-open-edit-form-element]'); if (!openLotForm) { return; } event.preventDefault(); const drawer = getDrawer(); const card = openLotForm.closest('[data-lager-open-card-wrapper]'); const editSection = card?.querySelector('[data-lager-open-edit-form]'); const fields = [{ label: 'Naurua-Chargennummer', input: openLotForm.querySelector('[data-lager-open-lot-number]') }, { label: 'Hersteller', input: openLotForm.querySelector('[data-lager-open-manufacturer]') }, { label: 'Hersteller-Chargennummer', input: openLotForm.querySelector('[data-lager-open-manufacturer-lot]') }, { label: 'Herstellungsdatum', input: openLotForm.querySelector('[data-lager-open-mfg-date]') }, { label: 'MHD', input: openLotForm.querySelector('[data-lager-open-expiry-date]') }, { label: 'Einkaufsdatum', input: openLotForm.querySelector('[data-lager-open-purchase-date]') }, { label: 'Menge', input: openLotForm.querySelector('[data-lager-open-target-qty]') }]; const emptyFields = fields.filter((field) => (field.input?.value || '').trim() === ''); if (emptyFields.length > 0 && openLotForm.dataset.confirmationAccepted !== 'true') { const confirmation = card?.querySelector('[data-lager-open-confirmation-card]'); const emptyFieldsList = card?.querySelector('[data-lager-open-confirmation-empty-fields]'); if (emptyFieldsList) { emptyFieldsList.replaceChildren(...emptyFields.map((field) => { const item = document.createElement('div'); item.className = 'sg-body'; item.textContent = field.label; return item; })); } if (confirmation) { confirmation.hidden = false; } if (card) { card.dataset.dialogOpen = 'true'; } return; } openLotForm.dataset.confirmationAccepted = 'false'; const status = openLotForm.querySelector('[data-lager-open-edit-status]'); if (status) { status.textContent = 'Speichern läuft …'; } try { const targetQtyInput = openLotForm.querySelector('[data-lager-open-target-qty]'); const result = await postLagerChange({ action: 'prepare_lot', productId: drawer?.dataset.lagerProductId || '', lotId: card?.dataset.lagerOpenCardWrapper || '', lotNumber: openLotForm.querySelector('[data-lager-open-lot-number]')?.value || '', manufacturerPartyId: openLotForm.querySelector('[data-lager-open-manufacturer]')?.value || null, manufacturerLotNumber: openLotForm.querySelector('[data-lager-open-manufacturer-lot]')?.value || '', mfgDate: openLotForm.querySelector('[data-lager-open-mfg-date]')?.value || '', expiryDate: openLotForm.querySelector('[data-lager-open-expiry-date]')?.value || '', purchaseDate: openLotForm.querySelector('[data-lager-open-purchase-date]')?.value || '', targetQty: targetQtyInput?.value.trim() === '' ? '0' : targetQtyInput?.value || '' }); syncLagerOpenLotCard(result.lot, result.balance); if (editSection) { editSection.hidden = true; } card?.querySelector('[data-lager-open-card]')?.removeAttribute('hidden'); if (status) { status.textContent = ''; } } catch (error) { if (status) { status.textContent = error instanceof Error ? error.message : 'Speichern fehlgeschlagen'; } } });"; echo " document.addEventListener('click', (event) => { const cancel = event.target.closest('[data-lager-new-confirmation-cancel]'); if (cancel) { const drawer = getDrawer(); const confirmation = drawer?.querySelector('[data-lager-new-confirmation-card]'); if (confirmation) { confirmation.hidden = true; } if (drawer) { drawer.dataset.dialogOpen = 'false'; } const form = drawer?.querySelector('[data-lager-new-lot-form-element]'); if (form) { form.dataset.confirmationAccepted = 'false'; } return; } const confirm = event.target.closest('[data-lager-new-confirmation-submit]'); if (confirm) { const drawer = getDrawer(); const confirmation = drawer?.querySelector('[data-lager-new-confirmation-card]'); const form = drawer?.querySelector('[data-lager-new-lot-form-element]'); if (confirmation) { confirmation.hidden = true; } if (drawer) { drawer.dataset.dialogOpen = 'false'; } if (form) { form.dataset.confirmationAccepted = 'true'; form.requestSubmit(); } } });"; - echo " document.addEventListener('submit', async (event) => { const newLotForm = event.target.closest('[data-lager-new-lot-form-element]'); if (!newLotForm) { return; } event.preventDefault(); const drawer = getDrawer(); const fields = [{ label: 'Naurua-Chargennummer', input: drawer?.querySelector('[data-lager-new-lot-number]') }, { label: 'Hersteller', input: drawer?.querySelector('[data-lager-new-manufacturer]') }, { label: 'Hersteller-Chargennummer', input: drawer?.querySelector('[data-lager-new-manufacturer-lot]') }, { label: 'Herstellungsdatum', input: drawer?.querySelector('[data-lager-new-mfg-date]') }, { label: 'MHD', input: drawer?.querySelector('[data-lager-new-expiry-date]') }, { label: 'Einkaufsdatum', input: drawer?.querySelector('[data-lager-new-purchase-date]') }, { label: 'Eingangsmenge', input: drawer?.querySelector('[data-lager-new-receipt-qty]') }]; const emptyFields = fields.filter((field) => (field.input?.value || '').trim() === ''); if (emptyFields.length > 0 && newLotForm.dataset.confirmationAccepted !== 'true') { const confirmation = drawer?.querySelector('[data-lager-new-confirmation-card]'); const emptyFieldsList = drawer?.querySelector('[data-lager-new-confirmation-empty-fields]'); if (emptyFieldsList) { emptyFieldsList.replaceChildren(...emptyFields.map((field) => { const item = document.createElement('div'); item.className = 'sg-body'; item.textContent = field.label; return item; })); } if (confirmation) { confirmation.hidden = false; } if (drawer) { drawer.dataset.dialogOpen = 'true'; } return; } newLotForm.dataset.confirmationAccepted = 'false'; const status = drawer?.querySelector('[data-lager-new-lot-status]'); if (status) { status.textContent = 'Speichern läuft …'; } try { await postLagerChange({ action: 'prepare_lot', productId: drawer?.dataset.lagerProductId || '', lotId: '', lotNumber: drawer?.querySelector('[data-lager-new-lot-number]')?.value || '', manufacturerPartyId: drawer?.querySelector('[data-lager-new-manufacturer]')?.value || null, manufacturerLotNumber: drawer?.querySelector('[data-lager-new-manufacturer-lot]')?.value || '', mfgDate: drawer?.querySelector('[data-lager-new-mfg-date]')?.value || '', expiryDate: drawer?.querySelector('[data-lager-new-expiry-date]')?.value || '', purchaseDate: drawer?.querySelector('[data-lager-new-purchase-date]')?.value || '', receiptQty: drawer?.querySelector('[data-lager-new-receipt-qty]')?.value || '' }); const productId = drawer?.dataset.lagerProductId || ''; resetLagerNewLotConfirmation(); const form = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-form\"]'); const action = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-action\"]'); if (form) { form.hidden = true; } if (action) { action.hidden = false; } if (productId) { window.sessionStorage.setItem('lager-drawer-restore-product-id', productId); } window.location.reload(); } catch (error) { if (status) { status.textContent = error instanceof Error ? error.message : 'Speichern fehlgeschlagen'; } } });"; + echo " document.addEventListener('submit', async (event) => { const newLotForm = event.target.closest('[data-lager-new-lot-form-element]'); if (!newLotForm) { return; } event.preventDefault(); const drawer = getDrawer(); const fields = [{ label: 'Naurua-Chargennummer', input: drawer?.querySelector('[data-lager-new-lot-number]') }, { label: 'Hersteller', input: drawer?.querySelector('[data-lager-new-manufacturer]') }, { label: 'Hersteller-Chargennummer', input: drawer?.querySelector('[data-lager-new-manufacturer-lot]') }, { label: 'Herstellungsdatum', input: drawer?.querySelector('[data-lager-new-mfg-date]') }, { label: 'MHD', input: drawer?.querySelector('[data-lager-new-expiry-date]') }, { label: 'Einkaufsdatum', input: drawer?.querySelector('[data-lager-new-purchase-date]') }, { label: 'Eingangsmenge', input: drawer?.querySelector('[data-lager-new-receipt-qty]') }]; const emptyFields = fields.filter((field) => (field.input?.value || '').trim() === ''); if (emptyFields.length > 0 && newLotForm.dataset.confirmationAccepted !== 'true') { const confirmation = drawer?.querySelector('[data-lager-new-confirmation-card]'); const emptyFieldsList = drawer?.querySelector('[data-lager-new-confirmation-empty-fields]'); if (emptyFieldsList) { emptyFieldsList.replaceChildren(...emptyFields.map((field) => { const item = document.createElement('div'); item.className = 'sg-body'; item.textContent = field.label; return item; })); } if (confirmation) { confirmation.hidden = false; } if (drawer) { drawer.dataset.dialogOpen = 'true'; } return; } newLotForm.dataset.confirmationAccepted = 'false'; const status = drawer?.querySelector('[data-lager-new-lot-status]'); if (status) { status.textContent = 'Speichern läuft …'; } try { const result = await postLagerChange({ action: 'prepare_lot', productId: drawer?.dataset.lagerProductId || '', lotId: '', lotNumber: drawer?.querySelector('[data-lager-new-lot-number]')?.value || '', manufacturerPartyId: drawer?.querySelector('[data-lager-new-manufacturer]')?.value || null, manufacturerLotNumber: drawer?.querySelector('[data-lager-new-manufacturer-lot]')?.value || '', mfgDate: drawer?.querySelector('[data-lager-new-mfg-date]')?.value || '', expiryDate: drawer?.querySelector('[data-lager-new-expiry-date]')?.value || '', purchaseDate: drawer?.querySelector('[data-lager-new-purchase-date]')?.value || '', receiptQty: drawer?.querySelector('[data-lager-new-receipt-qty]')?.value || '' }); appendLagerOpenLotCard(result.lot, result.balance); resetLagerNewLotConfirmation(); const form = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-form\"]'); const action = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-action\"]'); if (form) { form.hidden = true; } if (action) { action.hidden = false; } if (status) { status.textContent = ''; } } catch (error) { if (status) { status.textContent = error instanceof Error ? error.message : 'Speichern fehlgeschlagen'; } } });"; echo " document.addEventListener('submit', async (event) => { const editForm = event.target.closest('[data-lager-edit-form-element]'); if (!editForm) { return; } event.preventDefault(); const drawer = getDrawer(); const status = drawer?.querySelector('[data-lager-edit-status]'); if (status) { status.textContent = 'Speichern läuft …'; } try { const result = await postLagerChange({ action: 'update_lot', lotId: drawer?.dataset.lagerActiveLotId || '', manufacturerPartyId: drawer?.querySelector('[data-lager-manufacturer]')?.value || null, manufacturerLotNumber: drawer?.querySelector('[data-lager-manufacturer-lot]')?.value || '', mfgDate: drawer?.querySelector('[data-lager-mfg-date]')?.value || '', expiryDate: drawer?.querySelector('[data-lager-expiry-date]')?.value || '', purchaseDate: drawer?.querySelector('[data-lager-purchase-date]')?.value || '', receiptQty: drawer?.querySelector('[data-lager-receipt-qty]')?.value || '', adjustmentQty: drawer?.querySelector('[data-lager-adjustment-qty]')?.value || '', outQty: drawer?.querySelector('[data-lager-out-qty]')?.value || '' }); syncLagerReadView(); syncLagerBalanceView(result.balance); clearLagerMovementInputs(); setLagerEditMode(false); if (status) { status.textContent = ''; } } catch (error) { if (status) { status.textContent = error instanceof Error ? error.message : 'Speichern fehlgeschlagen'; } } });"; echo " document.addEventListener('click', (event) => { if (event.target.closest('[data-lager-detail-back]')) { event.preventDefault(); closeLagerMobileDetail(); } });"; echo " document.addEventListener('keydown', (event) => { if (event.key === 'Escape') { closeDrawer(); closeLagerMobileDetail(); } });"; @@ -861,7 +877,6 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best echo " const syncLagerDisplayedTotal = () => { const drawer = getDrawer(); const trigger = contentRoot.querySelector('[data-lager-drawer-open][aria-expanded=\"true\"]'); if (!drawer || !trigger || trigger.dataset.lagerDrawerType !== 'Produkt') { return; } const active = Number((drawer.querySelector('[data-lager-drawer-current-qty]')?.textContent || '0').replace(',', '.')) || 0; const productId = trigger.closest('[data-lager-row]')?.dataset.lagerId || ''; const openProduct = drawer.querySelector('[data-lager-open-product=\"' + productId + '\"]'); const open = Array.from(openProduct?.querySelectorAll('[data-lager-open-card-qty-net]') || []).reduce((sum, node) => sum + (Number((node.textContent || '0').replace(',', '.')) || 0), 0); const total = active + open; const formatted = Number.isInteger(total) ? String(total) : total.toFixed(2).replace(/\.?0+$/, ''); const value = drawer.querySelector('[data-lager-drawer-value-value]'); if (value && value.textContent !== formatted) { value.textContent = formatted; } trigger.dataset.lagerDrawerValue = formatted; };"; echo " const lagerTotalObserver = new MutationObserver(syncLagerDisplayedTotal);"; echo " lagerTotalObserver.observe(contentRoot, { childList: true, subtree: true, characterData: true });"; - echo " const restoreProductId = window.sessionStorage.getItem('lager-drawer-restore-product-id'); if (restoreProductId) { window.sessionStorage.removeItem('lager-drawer-restore-product-id'); const restoreTrigger = Array.from(contentRoot.querySelectorAll('[data-lager-drawer-open]')).find((trigger) => trigger.closest('[data-lager-row]')?.dataset.lagerId === restoreProductId); restoreTrigger?.click(); }"; echo "}"; echo "function initBestellungenBindings() {"; echo " const contentRoot = document.querySelector('[data-left-navigation-content-body]');";