From e656841de299d4bb7dd2f793dc40226423ba367d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Gla=CC=88ser?= Date: Sun, 6 Sep 2026 17:33:23 +0200 Subject: [PATCH] Beruecksichtige offene Chargenmengen im Forecast --- .../main/erp.lager.stock_lot_forecast_refresh.md | 8 ++++---- modules/erp/lager/forecast.php | 10 ++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/modules/erp/lager/processes/main/erp.lager.stock_lot_forecast_refresh.md b/docs/modules/erp/lager/processes/main/erp.lager.stock_lot_forecast_refresh.md index b99ad0e..fd0f1eb 100644 --- a/docs/modules/erp/lager/processes/main/erp.lager.stock_lot_forecast_refresh.md +++ b/docs/modules/erp/lager/processes/main/erp.lager.stock_lot_forecast_refresh.md @@ -13,17 +13,17 @@ Direkt ausgeführter Lager-Prozess mit zwei Triggern: synchron nach jeder erfolg ## 3. Input - optional eine einzelne `product_id` bei synchronem Verkaufstrigger -- aktive Produkte, aktuelle Chargen, physischer Chargenbestand und gültige Verkaufsallokationen aus dem eigenen Datenbestand +- aktive Produkte, aktuelle Chargen, eingetragene Mengen der offenen Folgecharge, physischer Chargenbestand und gültige Verkaufsallokationen aus dem eigenen Datenbestand ## 4. Exakter Prozessablauf 1. Bei einem Verkaufstrigger die betroffenen Produkt-IDs aus der erfolgreichen Allokation übernehmen; beim Cron-Trigger alle aktiven Produkte bestimmen. -2. Aktive Produkte und ihre aktuelle Charge aus `product`, `stock_lot` und `v_stock_lot_balance` laden. +2. Aktive Produkte, ihre aktuelle Charge und den Nettobestand einer bereits angelegten offenen Folgecharge aus `product`, `stock_lot` und `v_stock_lot_balance` laden. Die Menge der offenen Folgecharge wird nur berücksichtigt, wenn sie positiv ist. 3. Verkaufsallokationen der letzten 365 Tage aus `sales_order_line_lot_allocation`, `sales_order_line` und `sales_order` laden. 4. `allocated`- und `reserved`-Mengen nach Bestell- beziehungsweise OTC-Verkaufsdatum je Produkt und Kalendertag aggregieren. 5. Stornierte Auftrags- und Positionsmengen aus der Nachfrage herausrechnen. 6. Den gewichteten Tagesabsatz mit einer Halbwertszeit von 60 Tagen berechnen. -7. Für jede aktuelle Charge das Leer-Datum aus physischem Bestand und gewichteter Nachfrage berechnen. +7. Für jede aktuelle Charge das Leer-Datum aus ihrem physischen Bestand zuzüglich der eingetragenen Menge der offenen Folgecharge und der gewichteten Nachfrage berechnen. 8. Jede erfolgreiche Berechnung in `stock_lot_forecast_history` speichern und den aktuellen Snapshot in `stock_lot` aktualisieren. 9. Im Cron Fehler einzelner Produkte sammeln und mit den übrigen Produkten fortfahren. @@ -37,7 +37,7 @@ Ein normalisiertes JSON-Ergebnis mit Gesamtzahl, erfolgreich verarbeiteten Produ ## 7. Erfolgskriterien -- Jede aktuelle Charge erhält täglich einen aktuellen Prognosesnapshot. +- Jede aktuelle Charge erhält täglich einen aktuellen Prognosesnapshot, der eine eingetragene Menge der offenen Folgecharge berücksichtigt. - Verkäufe ohne Bestand über virtuelle `reserved`-Allokationen beeinflussen die Nachfrage. - Die vollständige Berechnungshistorie bleibt erhalten. - Ein einzelner Produktfehler verhindert keine weiteren Berechnungen. diff --git a/modules/erp/lager/forecast.php b/modules/erp/lager/forecast.php index d19326f..911ca79 100644 --- a/modules/erp/lager/forecast.php +++ b/modules/erp/lager/forecast.php @@ -35,10 +35,13 @@ function lager_forecast_load_current_lots(PDO $pdo): array { $stmt = $pdo->query( "SELECT p.id AS product_id, p.sku, sl.id AS lot_id, - COALESCE(v.qty_net, 0) AS physical_qty + COALESCE(v.qty_net, 0) + GREATEST(COALESCE(open_v.qty_net, 0), 0) AS physical_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 + LEFT JOIN stock_lot open_lot + ON open_lot.product_id = p.id AND open_lot.status = 'open' + LEFT JOIN v_stock_lot_balance open_v ON open_v.stock_lot_id = open_lot.id WHERE p.status = 'active' ORDER BY p.id" ); @@ -49,10 +52,13 @@ function lager_forecast_load_current_lot(PDO $pdo, int $productId): ?array { $stmt = $pdo->prepare( "SELECT p.id AS product_id, p.sku, sl.id AS lot_id, - COALESCE(v.qty_net, 0) AS physical_qty + COALESCE(v.qty_net, 0) + GREATEST(COALESCE(open_v.qty_net, 0), 0) AS physical_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 + LEFT JOIN stock_lot open_lot + ON open_lot.product_id = p.id AND open_lot.status = 'open' + LEFT JOIN v_stock_lot_balance open_v ON open_v.stock_lot_id = open_lot.id WHERE p.status = 'active' AND p.id = :product_id LIMIT 1" );