Beruecksichtige offene Chargenmengen im Forecast

This commit is contained in:
gitea_admin committed 2026-09-06 17:33:23 +02:00
1 parent 7b25f4da0a
commit e656841de2
2 files changed
+12 -6

No files matched your search

@@ -13,17 +13,17 @@ Direkt ausgeführter Lager-Prozess mit zwei Triggern: synchron nach jeder erfolg
## 3. Input ## 3. Input
- optional eine einzelne `product_id` bei synchronem Verkaufstrigger - 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 ## 4. Exakter Prozessablauf
1. Bei einem Verkaufstrigger die betroffenen Produkt-IDs aus der erfolgreichen Allokation übernehmen; beim Cron-Trigger alle aktiven Produkte bestimmen. 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. 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. 4. `allocated`- und `reserved`-Mengen nach Bestell- beziehungsweise OTC-Verkaufsdatum je Produkt und Kalendertag aggregieren.
5. Stornierte Auftrags- und Positionsmengen aus der Nachfrage herausrechnen. 5. Stornierte Auftrags- und Positionsmengen aus der Nachfrage herausrechnen.
6. Den gewichteten Tagesabsatz mit einer Halbwertszeit von 60 Tagen berechnen. 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. 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. 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 ## 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. - Verkäufe ohne Bestand über virtuelle `reserved`-Allokationen beeinflussen die Nachfrage.
- Die vollständige Berechnungshistorie bleibt erhalten. - Die vollständige Berechnungshistorie bleibt erhalten.
- Ein einzelner Produktfehler verhindert keine weiteren Berechnungen. - Ein einzelner Produktfehler verhindert keine weiteren Berechnungen.
+8 -2
View File
@@ -35,10 +35,13 @@ function lager_forecast_load_current_lots(PDO $pdo): array
{ {
$stmt = $pdo->query( $stmt = $pdo->query(
"SELECT p.id AS product_id, p.sku, sl.id AS lot_id, "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 FROM product p
LEFT JOIN stock_lot sl ON sl.product_id = p.id AND sl.status = 'current' 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 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' WHERE p.status = 'active'
ORDER BY p.id" ORDER BY p.id"
); );
@@ -49,10 +52,13 @@ function lager_forecast_load_current_lot(PDO $pdo, int $productId): ?array
{ {
$stmt = $pdo->prepare( $stmt = $pdo->prepare(
"SELECT p.id AS product_id, p.sku, sl.id AS lot_id, "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 FROM product p
LEFT JOIN stock_lot sl ON sl.product_id = p.id AND sl.status = 'current' 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 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 WHERE p.status = 'active' AND p.id = :product_id
LIMIT 1" LIMIT 1"
); );