Ergaenze offene Chargen und Fehlbestands-Hinweis
This commit is contained in:
1 parent
0401a7ae2a
commit
43cb4b9885
3 files changed
+173
-4
No files matched your search
@@ -65,6 +65,33 @@ try {
|
|||||||
json_response(200, ['ok' => true, 'lot' => $updatedLot, 'balance' => get_stock_lot_balance($pdo, (int) ($data['lotId'] ?? 0))]);
|
json_response(200, ['ok' => true, 'lot' => $updatedLot, 'balance' => get_stock_lot_balance($pdo, (int) ($data['lotId'] ?? 0))]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($action === 'prepare_lot') {
|
||||||
|
$productId = (int) ($data['productId'] ?? 0);
|
||||||
|
$lotNumber = trim((string) ($data['lotNumber'] ?? ''));
|
||||||
|
if ($lotNumber === '') {
|
||||||
|
throw new InvalidArgumentException('Naurua-Chargennummer fehlt');
|
||||||
|
}
|
||||||
|
$lotId = get_or_create_open_lot($pdo, $productId);
|
||||||
|
$manufacturerPartyId = $data['manufacturerPartyId'] ?? null;
|
||||||
|
$manufacturerPartyId = $manufacturerPartyId === null || $manufacturerPartyId === '' ? null : (int) $manufacturerPartyId;
|
||||||
|
$receiptQty = isset($data['receiptQty']) && $data['receiptQty'] !== '' ? (float) $data['receiptQty'] : 0.0;
|
||||||
|
$preparedLot = update_stock_lot_traceability(
|
||||||
|
$pdo,
|
||||||
|
$lotId,
|
||||||
|
$manufacturerPartyId,
|
||||||
|
isset($data['manufacturerLotNumber']) ? (string) $data['manufacturerLotNumber'] : null,
|
||||||
|
isset($data['mfgDate']) ? (string) $data['mfgDate'] : null,
|
||||||
|
isset($data['expiryDate']) ? (string) $data['expiryDate'] : null,
|
||||||
|
isset($data['purchaseDate']) ? (string) $data['purchaseDate'] : null,
|
||||||
|
(string) ($currentUser['username'] ?? $currentUser['email'] ?? 'unknown'),
|
||||||
|
$receiptQty,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
$lotNumber
|
||||||
|
);
|
||||||
|
json_response(200, ['ok' => true, 'lot' => $preparedLot, 'balance' => get_stock_lot_balance($pdo, $lotId)]);
|
||||||
|
}
|
||||||
|
|
||||||
json_response(422, ['ok' => false, 'error' => 'Unsupported action']);
|
json_response(422, ['ok' => false, 'error' => 'Unsupported action']);
|
||||||
} catch (JsonException) {
|
} catch (JsonException) {
|
||||||
json_response(400, ['ok' => false, 'error' => 'Invalid JSON payload']);
|
json_response(400, ['ok' => false, 'error' => 'Invalid JSON payload']);
|
||||||
|
|||||||
@@ -87,6 +87,14 @@ function get_lager_product_overview(PDO $pdo): array
|
|||||||
sl.purchase_date,
|
sl.purchase_date,
|
||||||
sl.sellout_date,
|
sl.sellout_date,
|
||||||
sl.sellout_forecast_status,
|
sl.sellout_forecast_status,
|
||||||
|
COALESCE((
|
||||||
|
SELECT SUM(a.qty)
|
||||||
|
FROM sales_order_line_lot_allocation a
|
||||||
|
INNER JOIN stock_lot virtual_lot ON virtual_lot.id = a.lot_id
|
||||||
|
WHERE virtual_lot.product_id = p.id
|
||||||
|
AND virtual_lot.status = 'virtual_shortage'
|
||||||
|
AND a.allocation_status = 'reserved'
|
||||||
|
), 0) AS virtual_shortage_qty,
|
||||||
(
|
(
|
||||||
SELECT virtual_lot.lot_number
|
SELECT virtual_lot.lot_number
|
||||||
FROM stock_lot virtual_lot
|
FROM stock_lot virtual_lot
|
||||||
@@ -127,6 +135,7 @@ function get_lager_product_overview(PDO $pdo): array
|
|||||||
'purchase_date' => $row['purchase_date'],
|
'purchase_date' => $row['purchase_date'],
|
||||||
'sellout_date' => $row['sellout_date'],
|
'sellout_date' => $row['sellout_date'],
|
||||||
'sellout_forecast_status' => (string) ($row['sellout_forecast_status'] ?? 'no_demand'),
|
'sellout_forecast_status' => (string) ($row['sellout_forecast_status'] ?? 'no_demand'),
|
||||||
|
'virtual_shortage_qty' => (float) ($row['virtual_shortage_qty'] ?? 0),
|
||||||
'virtual_lot_number' => $row['virtual_lot_number'],
|
'virtual_lot_number' => $row['virtual_lot_number'],
|
||||||
],
|
],
|
||||||
$stmt->fetchAll()
|
$stmt->fetchAll()
|
||||||
@@ -176,8 +185,44 @@ function get_lager_product_overview(PDO $pdo): array
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$openStmt = $pdo->query(
|
||||||
|
"SELECT
|
||||||
|
sl.product_id,
|
||||||
|
sl.id AS lot_id,
|
||||||
|
sl.lot_number,
|
||||||
|
sl.manufacturer_party_id,
|
||||||
|
sl.manufacturer_lot_number,
|
||||||
|
mp.name AS manufacturer_name,
|
||||||
|
sl.mfg_date,
|
||||||
|
sl.expiry_date,
|
||||||
|
sl.purchase_date,
|
||||||
|
COALESCE(v.qty_in, 0) AS qty_in,
|
||||||
|
COALESCE(v.qty_net, 0) AS qty_net
|
||||||
|
FROM stock_lot sl
|
||||||
|
LEFT JOIN party mp ON mp.id = sl.manufacturer_party_id
|
||||||
|
LEFT JOIN v_stock_lot_balance v ON v.stock_lot_id = sl.id
|
||||||
|
WHERE sl.status = 'open'
|
||||||
|
ORDER BY sl.product_id, sl.id"
|
||||||
|
);
|
||||||
|
$openLotsByProduct = [];
|
||||||
|
foreach ($openStmt->fetchAll() as $openLot) {
|
||||||
|
$openLotsByProduct[(int) $openLot['product_id']][] = [
|
||||||
|
'lot_id' => (int) $openLot['lot_id'],
|
||||||
|
'lot_number' => $openLot['lot_number'],
|
||||||
|
'manufacturer_party_id' => $openLot['manufacturer_party_id'] === null ? null : (int) $openLot['manufacturer_party_id'],
|
||||||
|
'manufacturer_lot_number' => $openLot['manufacturer_lot_number'],
|
||||||
|
'manufacturer_name' => $openLot['manufacturer_name'],
|
||||||
|
'mfg_date' => $openLot['mfg_date'],
|
||||||
|
'expiry_date' => $openLot['expiry_date'],
|
||||||
|
'purchase_date' => $openLot['purchase_date'],
|
||||||
|
'qty_in' => (float) $openLot['qty_in'],
|
||||||
|
'qty_net' => (float) $openLot['qty_net'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($products as &$product) {
|
foreach ($products as &$product) {
|
||||||
$product['historical_lots'] = $historicalLotsByProduct[$product['id']] ?? [];
|
$product['historical_lots'] = $historicalLotsByProduct[$product['id']] ?? [];
|
||||||
|
$product['open_lots'] = $openLotsByProduct[$product['id']] ?? [];
|
||||||
}
|
}
|
||||||
unset($product);
|
unset($product);
|
||||||
|
|
||||||
@@ -195,7 +240,8 @@ function update_stock_lot_traceability(
|
|||||||
string $changedBy,
|
string $changedBy,
|
||||||
float $receiptQty = 0.0,
|
float $receiptQty = 0.0,
|
||||||
float $adjustmentQty = 0.0,
|
float $adjustmentQty = 0.0,
|
||||||
float $outQty = 0.0
|
float $outQty = 0.0,
|
||||||
|
?string $lotNumber = null
|
||||||
): array {
|
): array {
|
||||||
if ($lotId <= 0) {
|
if ($lotId <= 0) {
|
||||||
throw new InvalidArgumentException('Chargen-ID fehlt');
|
throw new InvalidArgumentException('Chargen-ID fehlt');
|
||||||
@@ -205,6 +251,10 @@ function update_stock_lot_traceability(
|
|||||||
if ($manufacturerLotNumber === '') {
|
if ($manufacturerLotNumber === '') {
|
||||||
$manufacturerLotNumber = null;
|
$manufacturerLotNumber = null;
|
||||||
}
|
}
|
||||||
|
$lotNumber = $lotNumber !== null ? trim($lotNumber) : null;
|
||||||
|
if ($lotNumber === '') {
|
||||||
|
throw new InvalidArgumentException('Naurua-Chargennummer fehlt');
|
||||||
|
}
|
||||||
|
|
||||||
if (!is_finite($receiptQty) || !is_finite($adjustmentQty) || !is_finite($outQty)) {
|
if (!is_finite($receiptQty) || !is_finite($adjustmentQty) || !is_finite($outQty)) {
|
||||||
throw new InvalidArgumentException('Ungueltige Bewegungsmenge');
|
throw new InvalidArgumentException('Ungueltige Bewegungsmenge');
|
||||||
@@ -256,6 +306,9 @@ function update_stock_lot_traceability(
|
|||||||
if ((string) $before['status'] === 'virtual_shortage') {
|
if ((string) $before['status'] === 'virtual_shortage') {
|
||||||
throw new InvalidArgumentException('Virtuelle Fehlbestandschargen koennen nicht gepflegt werden');
|
throw new InvalidArgumentException('Virtuelle Fehlbestandschargen koennen nicht gepflegt werden');
|
||||||
}
|
}
|
||||||
|
if ($lotNumber !== null && (string) $before['status'] !== 'open') {
|
||||||
|
throw new InvalidArgumentException('Nur offene Chargen koennen neu angelegt werden');
|
||||||
|
}
|
||||||
|
|
||||||
if ($manufacturerPartyId !== null) {
|
if ($manufacturerPartyId !== null) {
|
||||||
$manufacturerStmt = $pdo->prepare(
|
$manufacturerStmt = $pdo->prepare(
|
||||||
@@ -277,13 +330,14 @@ function update_stock_lot_traceability(
|
|||||||
if ($manufacturerPartyId !== null && $manufacturerLotNumber === null && (string) $before['status'] !== 'open') {
|
if ($manufacturerPartyId !== null && $manufacturerLotNumber === null && (string) $before['status'] !== 'open') {
|
||||||
throw new InvalidArgumentException('Hersteller-Chargennummer fehlt');
|
throw new InvalidArgumentException('Hersteller-Chargennummer fehlt');
|
||||||
}
|
}
|
||||||
if (($receiptQty > 0 || $adjustmentQty !== 0.0 || $outQty > 0) && (string) $before['status'] !== 'current') {
|
if (($adjustmentQty !== 0.0 || $outQty > 0) && (string) $before['status'] !== 'current') {
|
||||||
throw new InvalidArgumentException('Lagerbewegungen koennen nur auf der aktiven Charge gebucht werden');
|
throw new InvalidArgumentException('Korrekturen und Abgaenge koennen nur auf der aktiven Charge gebucht werden');
|
||||||
}
|
}
|
||||||
|
|
||||||
$updateStmt = $pdo->prepare(
|
$updateStmt = $pdo->prepare(
|
||||||
"UPDATE stock_lot
|
"UPDATE stock_lot
|
||||||
SET manufacturer_party_id = :manufacturer_party_id,
|
SET lot_number = COALESCE(:lot_number, lot_number),
|
||||||
|
manufacturer_party_id = :manufacturer_party_id,
|
||||||
manufacturer_lot_number = :manufacturer_lot_number,
|
manufacturer_lot_number = :manufacturer_lot_number,
|
||||||
mfg_date = :mfg_date,
|
mfg_date = :mfg_date,
|
||||||
expiry_date = :expiry_date,
|
expiry_date = :expiry_date,
|
||||||
@@ -292,6 +346,7 @@ function update_stock_lot_traceability(
|
|||||||
WHERE id = :id"
|
WHERE id = :id"
|
||||||
);
|
);
|
||||||
$updateStmt->execute([
|
$updateStmt->execute([
|
||||||
|
':lot_number' => $lotNumber,
|
||||||
':manufacturer_party_id' => $manufacturerPartyId,
|
':manufacturer_party_id' => $manufacturerPartyId,
|
||||||
':manufacturer_lot_number' => $manufacturerLotNumber,
|
':manufacturer_lot_number' => $manufacturerLotNumber,
|
||||||
':mfg_date' => $dates['mfg_date'],
|
':mfg_date' => $dates['mfg_date'],
|
||||||
@@ -301,6 +356,9 @@ function update_stock_lot_traceability(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$after = $before;
|
$after = $before;
|
||||||
|
if ($lotNumber !== null) {
|
||||||
|
$after['lot_number'] = $lotNumber;
|
||||||
|
}
|
||||||
$after['manufacturer_party_id'] = $manufacturerPartyId;
|
$after['manufacturer_party_id'] = $manufacturerPartyId;
|
||||||
$after['manufacturer_lot_number'] = $manufacturerLotNumber;
|
$after['manufacturer_lot_number'] = $manufacturerLotNumber;
|
||||||
$after['mfg_date'] = $dates['mfg_date'];
|
$after['mfg_date'] = $dates['mfg_date'];
|
||||||
@@ -681,6 +739,37 @@ function get_or_create_virtual_shortage_lot(PDO $pdo, int $productId): int
|
|||||||
return (int) $newLotId;
|
return (int) $newLotId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function get_or_create_open_lot(PDO $pdo, int $productId): int
|
||||||
|
{
|
||||||
|
$select = $pdo->prepare(
|
||||||
|
"SELECT id
|
||||||
|
FROM stock_lot
|
||||||
|
WHERE product_id = :product_id
|
||||||
|
AND status = 'open'
|
||||||
|
ORDER BY id
|
||||||
|
LIMIT 1
|
||||||
|
FOR UPDATE"
|
||||||
|
);
|
||||||
|
$select->execute([':product_id' => $productId]);
|
||||||
|
$lotId = $select->fetchColumn();
|
||||||
|
if ($lotId !== false) {
|
||||||
|
return (int) $lotId;
|
||||||
|
}
|
||||||
|
|
||||||
|
$insert = $pdo->prepare(
|
||||||
|
"INSERT INTO stock_lot (product_id, status, created_at, updated_at)
|
||||||
|
VALUES (:product_id, 'open', NOW(), NOW())
|
||||||
|
RETURNING id"
|
||||||
|
);
|
||||||
|
$insert->execute([':product_id' => $productId]);
|
||||||
|
$newLotId = $insert->fetchColumn();
|
||||||
|
if ($newLotId === false) {
|
||||||
|
throw new RuntimeException('Offene Charge konnte nicht angelegt werden');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) $newLotId;
|
||||||
|
}
|
||||||
|
|
||||||
function replace_virtual_lot_allocation(
|
function replace_virtual_lot_allocation(
|
||||||
PDO $pdo,
|
PDO $pdo,
|
||||||
int $allocationId,
|
int $allocationId,
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ function auth_render_lager_table(string $title, array $rows, array $columns, str
|
|||||||
. ' data-lager-drawer-forecast-mhd-warning="' . ($forecastMhdWarning ? 'true' : 'false') . '"'
|
. ' data-lager-drawer-forecast-mhd-warning="' . ($forecastMhdWarning ? 'true' : 'false') . '"'
|
||||||
. ' data-lager-drawer-current-qty="' . auth_escape_html(auth_format_lager_quantity((float) ($row['available_qty'] ?? 0))) . '"'
|
. ' data-lager-drawer-current-qty="' . auth_escape_html(auth_format_lager_quantity((float) ($row['available_qty'] ?? 0))) . '"'
|
||||||
. ' data-lager-drawer-qty-in="' . auth_escape_html(auth_format_lager_quantity((float) ($row['qty_in'] ?? 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-id="' . auth_escape_html((string) ($row['lot_id'] ?? '')) . '"'
|
||||||
. ' data-lager-drawer-lot-number="' . auth_escape_html((string) ($row['lot_number'] ?? '')) . '"'
|
. ' 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-party-id="' . auth_escape_html((string) ($row['manufacturer_party_id'] ?? '')) . '"'
|
||||||
@@ -254,6 +255,14 @@ function auth_render_lager_table(string $title, array $rows, array $columns, str
|
|||||||
. ' data-lager-drawer-expiry-date="' . auth_escape_html((string) ($row['expiry_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-purchase-date="' . auth_escape_html((string) ($row['purchase_date'] ?? '')) . '"'
|
||||||
. ' data-lager-drawer-virtual-lot-number="' . auth_escape_html((string) ($row['virtual_lot_number'] ?? '')) . '"';
|
. ' 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[] = '<div class="' . $rowClass . '" role="row" data-component-part="large-table-row" data-lager-row="true" data-lager-id="' . $id . '">';
|
$html[] = '<div class="' . $rowClass . '" role="row" data-component-part="large-table-row" data-lager-row="true" data-lager-id="' . $id . '">';
|
||||||
@@ -317,6 +326,31 @@ function auth_render_lager_historical_lots(array $lagerProducts): string
|
|||||||
return implode('', $html);
|
return implode('', $html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function auth_render_lager_open_lots(array $lagerProducts): string
|
||||||
|
{
|
||||||
|
$html = [];
|
||||||
|
foreach ($lagerProducts as $product) {
|
||||||
|
$productId = (int) ($product['id'] ?? 0);
|
||||||
|
$html[] = '<div data-lager-open-product="' . $productId . '" hidden>';
|
||||||
|
foreach (($product['open_lots'] ?? []) as $lot) {
|
||||||
|
$html[] = '<article class="sg-card sg-card--content-card" data-component="content-card" aria-label="Offene Charge">'
|
||||||
|
. '<div class="sg-card-segment sg-card-segment--header sg-card-segment--darkgreen" data-component-part="card-header"><div class="sg-strong sg-text-on-dark">Offene Charge</div></div>'
|
||||||
|
. '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body" data-lager-open-card="' . (int) ($lot['lot_id'] ?? 0) . '"><p class="sg-body"><strong>Naurua-Chargennummer:</strong> <span data-lager-open-card-lot-number>' . auth_escape_html((string) ($lot['lot_number'] ?? 'Nicht hinterlegt')) . '</span><br>'
|
||||||
|
. '<strong>Status:</strong> Vorbereitet<br>'
|
||||||
|
. '<strong>Bestand:</strong> <span data-lager-open-card-qty-net>' . auth_format_lager_quantity((float) ($lot['qty_net'] ?? 0)) . '</span><br>'
|
||||||
|
. '<strong>Eingangsmenge:</strong> <span data-lager-open-card-qty-in>' . auth_format_lager_quantity((float) ($lot['qty_in'] ?? 0)) . '</span><br>'
|
||||||
|
. '<strong>Hersteller:</strong> <span data-lager-open-card-manufacturer>' . auth_escape_html((string) ($lot['manufacturer_name'] ?? 'Nicht hinterlegt')) . '</span><br>'
|
||||||
|
. '<strong>Hersteller-Chargennummer:</strong> <span data-lager-open-card-manufacturer-lot>' . auth_escape_html((string) ($lot['manufacturer_lot_number'] ?? 'Nicht hinterlegt')) . '</span><br>'
|
||||||
|
. '<strong>Herstellungsdatum:</strong> <span data-lager-open-card-mfg-date>' . auth_format_lager_date($lot['mfg_date'] ?? null) . '</span><br>'
|
||||||
|
. '<strong>MHD:</strong> <span data-lager-open-card-expiry-date>' . auth_format_lager_date($lot['expiry_date'] ?? null) . '</span><br>'
|
||||||
|
. '<strong>Einkaufsdatum:</strong> <span data-lager-open-card-purchase-date>' . auth_format_lager_date($lot['purchase_date'] ?? null) . '</span></p></div></article>';
|
||||||
|
}
|
||||||
|
$html[] = '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode('', $html);
|
||||||
|
}
|
||||||
|
|
||||||
function auth_render_lager_content(array $lagerProducts, array $lagerArtikel, array $lagerManufacturers, string $csrfToken): string
|
function auth_render_lager_content(array $lagerProducts, array $lagerArtikel, array $lagerManufacturers, string $csrfToken): string
|
||||||
{
|
{
|
||||||
$manufacturerOptions = '<option value="">Hersteller auswählen</option>';
|
$manufacturerOptions = '<option value="">Hersteller auswählen</option>';
|
||||||
@@ -341,6 +375,21 @@ function auth_render_lager_content(array $lagerProducts, array $lagerArtikel, ar
|
|||||||
. '<div class="sg-card-segment sg-card-segment--header sg-card-segment--darkgreen" data-component-part="card-header" data-lager-drawer-warning-header><div class="sg-strong sg-text-on-dark" data-lager-drawer-warning-title>Keine Warnung</div></div>'
|
. '<div class="sg-card-segment sg-card-segment--header sg-card-segment--darkgreen" data-component-part="card-header" data-lager-drawer-warning-header><div class="sg-strong sg-text-on-dark" data-lager-drawer-warning-title>Keine Warnung</div></div>'
|
||||||
. '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body"><p class="sg-body" data-lager-drawer-warning-value></p></div>'
|
. '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body"><p class="sg-body" data-lager-drawer-warning-value></p></div>'
|
||||||
. '</article>'
|
. '</article>'
|
||||||
|
. '<div class="sg-inline-controls-row" data-lager-drawer-topic="new-lot-action"><button class="sg-interaction-element sg-hyperlink" type="button" data-lager-new-lot-open>Neue Charge anlegen</button></div>'
|
||||||
|
. '<article class="sg-card sg-card--content-card" data-lager-drawer-topic="new-lot-form" aria-label="Neue Charge anlegen" hidden>'
|
||||||
|
. '<div class="sg-card-segment sg-card-segment--body sg-card-segment--gray"><div class="sg-form-sections-card-wrapper" data-pattern="form-sections" aria-label="Neue Charge anlegen"><form class="sg-form-sections-card" data-lager-new-lot-form-element><div class="sg-form-sections-card__body" data-pattern-part="form-body"><h2 class="sg-strong sg-form-sections-card__title">Neue Charge anlegen</h2>'
|
||||||
|
. '<div class="sg-notifications-pattern" data-lager-new-lot-notification-wrapper hidden><article class="sg-card sg-notifications-pattern__card" data-pattern="notification-small" data-component="notification-card"><div class="sg-card-segment sg-card-segment--header sg-card-segment--signal-yellow sg-notifications-pattern__text-segment sg-notifications-pattern__text-segment--small" data-component-part="card-header"><p class="sg-body" data-lager-new-lot-notification></p></div></article></div>'
|
||||||
|
. '<section class="sg-form-sections-card__chapter" aria-labelledby="lager-new-charge-data-title"><h2 id="lager-new-charge-data-title" class="sg-strong sg-form-sections-card__chapter-title">Chargendaten</h2>'
|
||||||
|
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Naurua-Chargennummer</span><input class="sg-interaction-element sg-input-single-line" type="text" data-lager-new-lot-number required></label></div>'
|
||||||
|
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Hersteller</span><select class="sg-interaction-element sg-input-single-line" data-lager-new-manufacturer>' . $manufacturerOptions . '</select></label></div>'
|
||||||
|
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Hersteller-Chargennummer</span><input class="sg-interaction-element sg-input-single-line" type="text" data-lager-new-manufacturer-lot></label></div>'
|
||||||
|
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Herstellungsdatum</span><input class="sg-interaction-element sg-input-single-line" type="date" data-lager-new-mfg-date></label></div>'
|
||||||
|
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">MHD</span><input class="sg-interaction-element sg-input-single-line" type="date" data-lager-new-expiry-date></label></div>'
|
||||||
|
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Einkaufsdatum</span><input class="sg-interaction-element sg-input-single-line" type="date" data-lager-new-purchase-date></label></div>'
|
||||||
|
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Eingangsmenge</span><input class="sg-interaction-element sg-input-single-line" type="number" min="0" step="1" inputmode="numeric" data-lager-new-receipt-qty placeholder="Menge"></label></div>'
|
||||||
|
. '</section><footer class="sg-form-sections-card__actions-segment" data-pattern-part="form-actions-segment"><div class="sg-form-sections-card__actions" data-pattern-part="form-actions"><button class="sg-interaction-element sg-button sg-button--active sg-form-sections-card__action" type="button" data-lager-new-lot-close>Abbrechen</button><button class="sg-interaction-element sg-button sg-button--process sg-form-sections-card__action" type="submit">Speichern</button></div></footer><p class="sg-body" data-lager-new-lot-status aria-live="polite"></p></div></form></div></div>'
|
||||||
|
. '</article>'
|
||||||
|
. '<div data-lager-drawer-topic="open-lots" aria-label="Offene Chargen">' . auth_render_lager_open_lots($lagerProducts) . '</div>'
|
||||||
. '<article class="sg-card sg-card--content-card" data-component="content-card" data-lager-drawer-topic="lot" aria-label="Aktive Charge">'
|
. '<article class="sg-card sg-card--content-card" data-component="content-card" data-lager-drawer-topic="lot" aria-label="Aktive Charge">'
|
||||||
. '<div class="sg-card-segment sg-card-segment--header sg-card-segment--darkgreen sg-object-card__header" data-component-part="card-header"><div class="sg-strong sg-text-on-dark">Aktive Charge</div><button class="sg-interaction-element sg-button sg-button--active" type="button" data-lager-edit-open>Bearbeiten</button></div>'
|
. '<div class="sg-card-segment sg-card-segment--header sg-card-segment--darkgreen sg-object-card__header" data-component-part="card-header"><div class="sg-strong sg-text-on-dark">Aktive Charge</div><button class="sg-interaction-element sg-button sg-button--active" type="button" data-lager-edit-open>Bearbeiten</button></div>'
|
||||||
. '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body" data-lager-read-view><p class="sg-body"><strong>Naurua-Chargennummer:</strong> <span data-lager-drawer-lot-number></span><br><strong>Aktueller Bestand:</strong> <span data-lager-drawer-current-qty></span><br><strong>Eingangsmenge:</strong> <span data-lager-drawer-qty-in></span><br><strong>Hersteller:</strong> <span data-lager-drawer-manufacturer-name></span><br><strong>Hersteller-Chargennummer:</strong> <span data-lager-drawer-manufacturer-lot-number></span><br><strong>Herstellungsdatum:</strong> <span data-lager-drawer-mfg-date></span><br><strong>MHD:</strong> <span data-lager-drawer-expiry-date></span><br><strong>Einkaufsdatum:</strong> <span data-lager-drawer-purchase-date></span></p></div>'
|
. '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body" data-lager-read-view><p class="sg-body"><strong>Naurua-Chargennummer:</strong> <span data-lager-drawer-lot-number></span><br><strong>Aktueller Bestand:</strong> <span data-lager-drawer-current-qty></span><br><strong>Eingangsmenge:</strong> <span data-lager-drawer-qty-in></span><br><strong>Hersteller:</strong> <span data-lager-drawer-manufacturer-name></span><br><strong>Hersteller-Chargennummer:</strong> <span data-lager-drawer-manufacturer-lot-number></span><br><strong>Herstellungsdatum:</strong> <span data-lager-drawer-mfg-date></span><br><strong>MHD:</strong> <span data-lager-drawer-expiry-date></span><br><strong>Einkaufsdatum:</strong> <span data-lager-drawer-purchase-date></span></p></div>'
|
||||||
@@ -760,6 +809,10 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
|
|||||||
echo " const syncLagerReadView = () => { const drawer = getDrawer(); if (!drawer) { return; } const manufacturer = drawer.querySelector('[data-lager-manufacturer]'); const values = { '[data-lager-drawer-manufacturer-name]': manufacturer?.selectedOptions?.[0]?.textContent || 'Nicht hinterlegt', '[data-lager-drawer-manufacturer-lot-number]': drawer.querySelector('[data-lager-manufacturer-lot]')?.value || 'Nicht hinterlegt', '[data-lager-drawer-mfg-date]': drawer.querySelector('[data-lager-mfg-date]')?.value || 'Nicht hinterlegt', '[data-lager-drawer-expiry-date]': drawer.querySelector('[data-lager-expiry-date]')?.value || 'Nicht hinterlegt', '[data-lager-drawer-purchase-date]': drawer.querySelector('[data-lager-purchase-date]')?.value || 'Nicht hinterlegt' }; Object.entries(values).forEach(([selector, value]) => { const target = drawer.querySelector(selector); if (target) { target.textContent = value; } }); const trigger = contentRoot.querySelector('[data-lager-drawer-open][aria-expanded=\"true\"]'); if (trigger) { trigger.dataset.lagerDrawerManufacturerName = values['[data-lager-drawer-manufacturer-name]']; trigger.dataset.lagerDrawerManufacturerLotNumber = values['[data-lager-drawer-manufacturer-lot-number]']; trigger.dataset.lagerDrawerMfgDate = values['[data-lager-drawer-mfg-date]']; trigger.dataset.lagerDrawerExpiryDate = values['[data-lager-drawer-expiry-date]']; trigger.dataset.lagerDrawerPurchaseDate = values['[data-lager-drawer-purchase-date]']; } };";
|
echo " const syncLagerReadView = () => { const drawer = getDrawer(); if (!drawer) { return; } const manufacturer = drawer.querySelector('[data-lager-manufacturer]'); const values = { '[data-lager-drawer-manufacturer-name]': manufacturer?.selectedOptions?.[0]?.textContent || 'Nicht hinterlegt', '[data-lager-drawer-manufacturer-lot-number]': drawer.querySelector('[data-lager-manufacturer-lot]')?.value || 'Nicht hinterlegt', '[data-lager-drawer-mfg-date]': drawer.querySelector('[data-lager-mfg-date]')?.value || 'Nicht hinterlegt', '[data-lager-drawer-expiry-date]': drawer.querySelector('[data-lager-expiry-date]')?.value || 'Nicht hinterlegt', '[data-lager-drawer-purchase-date]': drawer.querySelector('[data-lager-purchase-date]')?.value || 'Nicht hinterlegt' }; Object.entries(values).forEach(([selector, value]) => { const target = drawer.querySelector(selector); if (target) { target.textContent = value; } }); const trigger = contentRoot.querySelector('[data-lager-drawer-open][aria-expanded=\"true\"]'); if (trigger) { trigger.dataset.lagerDrawerManufacturerName = values['[data-lager-drawer-manufacturer-name]']; trigger.dataset.lagerDrawerManufacturerLotNumber = values['[data-lager-drawer-manufacturer-lot-number]']; trigger.dataset.lagerDrawerMfgDate = values['[data-lager-drawer-mfg-date]']; trigger.dataset.lagerDrawerExpiryDate = values['[data-lager-drawer-expiry-date]']; trigger.dataset.lagerDrawerPurchaseDate = values['[data-lager-drawer-purchase-date]']; } };";
|
||||||
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 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 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_in ?? 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 " contentRoot.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; }); 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]', trigger.dataset.lagerOpenLotNumber); setValue('[data-lager-new-manufacturer]', trigger.dataset.lagerOpenManufacturerPartyId); setValue('[data-lager-new-manufacturer-lot]', trigger.dataset.lagerOpenManufacturerLotNumber); setValue('[data-lager-new-mfg-date]', trigger.dataset.lagerOpenMfgDate); setValue('[data-lager-new-expiry-date]', trigger.dataset.lagerOpenExpiryDate); setValue('[data-lager-new-purchase-date]', trigger.dataset.lagerOpenPurchaseDate); if (drawer) { drawer.dataset.lagerOpenLotId = trigger.dataset.lagerOpenLotId || ''; drawer.dataset.lagerProductId = productId; } });";
|
||||||
|
echo " contentRoot.addEventListener('click', (event) => { const openButton = event.target.closest('[data-lager-new-lot-open]'); if (openButton) { const drawer = getDrawer(); 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 = false; } if (action) { action.hidden = true; } return; } if (event.target.closest('[data-lager-new-lot-close]')) { const drawer = getDrawer(); 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; } } });";
|
||||||
|
echo " contentRoot.addEventListener('submit', async (event) => { const newLotForm = event.target.closest('[data-lager-new-lot-form-element]'); if (!newLotForm) { return; } event.preventDefault(); const drawer = getDrawer(); 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: drawer?.dataset.lagerOpenLotId || '', 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 || '' }); syncLagerOpenLotCard(result.lot, result.balance); 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 " contentRoot.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 " contentRoot.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('click', (event) => { if (event.target.closest('[data-lager-detail-back]')) { event.preventDefault(); closeLagerMobileDetail(); } });";
|
||||||
echo " document.addEventListener('keydown', (event) => { if (event.key === 'Escape') { closeDrawer(); closeLagerMobileDetail(); } });";
|
echo " document.addEventListener('keydown', (event) => { if (event.key === 'Escape') { closeDrawer(); closeLagerMobileDetail(); } });";
|
||||||
|
|||||||
Reference in new issue
Block a user