Anchor next lot forecast after current lot
This commit is contained in:
1 parent
959586ecc9
commit
3ae9951b13
2 files changed
+23
-7
No files matched your search
@@ -23,7 +23,7 @@ Direkt ausgeführter Lager-Prozess mit zwei Triggern: synchron nach jeder erfolg
|
|||||||
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. Das Leer-Datum der aktuellen Charge und – sofern vorhanden – der nächsten Charge getrennt aus dem jeweiligen physischen Bestand und derselben gewichteten Nachfrage berechnen.
|
7. Das Leer-Datum der aktuellen Charge aus ihrem physischen Bestand und der gewichteten Nachfrage berechnen. Das Leer-Datum der nächsten Charge wird mit derselben Nachfrage ab dem Leer-Datum der aktuellen Charge weitergerechnet; bei bereits leerer aktueller Charge beginnt es am aktuellen Datum.
|
||||||
8. Jede erfolgreiche Berechnung je Charge in `stock_lot_forecast_history` speichern und den jeweiligen Snapshot in `stock_lot` aktualisieren.
|
8. Jede erfolgreiche Berechnung je Charge in `stock_lot_forecast_history` speichern und den jeweiligen 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.
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,12 @@ function lager_forecast_load_daily_sales(PDO $pdo, DateTimeImmutable $asOfDate,
|
|||||||
return $sales;
|
return $sales;
|
||||||
}
|
}
|
||||||
|
|
||||||
function lager_forecast_calculate(DateTimeImmutable $asOfDate, float $physicalQty, array $dailySales): array
|
function lager_forecast_calculate(
|
||||||
|
DateTimeImmutable $asOfDate,
|
||||||
|
float $physicalQty,
|
||||||
|
array $dailySales,
|
||||||
|
?DateTimeImmutable $forecastStartDate = null
|
||||||
|
): array
|
||||||
{
|
{
|
||||||
$lookbackDays = 365;
|
$lookbackDays = 365;
|
||||||
$halfLifeDays = 60;
|
$halfLifeDays = 60;
|
||||||
@@ -176,8 +181,9 @@ function lager_forecast_calculate(DateTimeImmutable $asOfDate, float $physicalQt
|
|||||||
? 'high'
|
? 'high'
|
||||||
: ($salesDays >= 10 ? 'medium' : 'low');
|
: ($salesDays >= 10 ? 'medium' : 'low');
|
||||||
$daysUntilDepleted = (int) ceil($physicalQty / $dailyDemand);
|
$daysUntilDepleted = (int) ceil($physicalQty / $dailyDemand);
|
||||||
|
$forecastStartDate ??= $asOfDate;
|
||||||
return [
|
return [
|
||||||
'forecast_date' => $asOfDate->modify('+' . $daysUntilDepleted . ' days')->format('Y-m-d'),
|
'forecast_date' => $forecastStartDate->modify('+' . $daysUntilDepleted . ' days')->format('Y-m-d'),
|
||||||
'forecast_status' => 'forecast', 'confidence' => $confidence, 'daily_demand' => $dailyDemand,
|
'forecast_status' => 'forecast', 'confidence' => $confidence, 'daily_demand' => $dailyDemand,
|
||||||
'sales_units' => $salesUnits, 'sales_days' => $salesDays, 'observed_span_days' => $observedSpanDays,
|
'sales_units' => $salesUnits, 'sales_days' => $salesDays, 'observed_span_days' => $observedSpanDays,
|
||||||
'lookback_days' => $lookbackDays, 'half_life_days' => $halfLifeDays,
|
'lookback_days' => $lookbackDays, 'half_life_days' => $halfLifeDays,
|
||||||
@@ -255,12 +261,17 @@ function refresh_lager_stock_lot_forecast_for_product(PDO $pdo, int $productId):
|
|||||||
$dailySales[$productId] ?? []
|
$dailySales[$productId] ?? []
|
||||||
);
|
);
|
||||||
lager_forecast_persist($pdo, $lot, $forecast, $asOfDate);
|
lager_forecast_persist($pdo, $lot, $forecast, $asOfDate);
|
||||||
if ($lot['next_lot_id'] !== null) {
|
if ($lot['next_lot_id'] !== null && $forecast['forecast_date'] !== null) {
|
||||||
lager_forecast_persist($pdo, [
|
lager_forecast_persist($pdo, [
|
||||||
'lot_id' => $lot['next_lot_id'],
|
'lot_id' => $lot['next_lot_id'],
|
||||||
'product_id' => $productId,
|
'product_id' => $productId,
|
||||||
'physical_qty' => $lot['next_physical_qty'],
|
'physical_qty' => $lot['next_physical_qty'],
|
||||||
], lager_forecast_calculate($asOfDate, (float) $lot['next_physical_qty'], $dailySales[$productId] ?? []), $asOfDate);
|
], lager_forecast_calculate(
|
||||||
|
$asOfDate,
|
||||||
|
(float) $lot['next_physical_qty'],
|
||||||
|
$dailySales[$productId] ?? [],
|
||||||
|
new DateTimeImmutable($forecast['forecast_date'])
|
||||||
|
), $asOfDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,12 +298,17 @@ function run_lager_stock_lot_forecast(PDO $pdo): array
|
|||||||
try {
|
try {
|
||||||
$forecast = lager_forecast_calculate($asOfDate, (float) $lot['physical_qty'], $dailySales[$productId] ?? []);
|
$forecast = lager_forecast_calculate($asOfDate, (float) $lot['physical_qty'], $dailySales[$productId] ?? []);
|
||||||
lager_forecast_persist($pdo, $lot, $forecast, $asOfDate);
|
lager_forecast_persist($pdo, $lot, $forecast, $asOfDate);
|
||||||
if ($lot['next_lot_id'] !== null) {
|
if ($lot['next_lot_id'] !== null && $forecast['forecast_date'] !== null) {
|
||||||
lager_forecast_persist($pdo, [
|
lager_forecast_persist($pdo, [
|
||||||
'lot_id' => $lot['next_lot_id'],
|
'lot_id' => $lot['next_lot_id'],
|
||||||
'product_id' => $productId,
|
'product_id' => $productId,
|
||||||
'physical_qty' => $lot['next_physical_qty'],
|
'physical_qty' => $lot['next_physical_qty'],
|
||||||
], lager_forecast_calculate($asOfDate, (float) $lot['next_physical_qty'], $dailySales[$productId] ?? []), $asOfDate);
|
], lager_forecast_calculate(
|
||||||
|
$asOfDate,
|
||||||
|
(float) $lot['next_physical_qty'],
|
||||||
|
$dailySales[$productId] ?? [],
|
||||||
|
new DateTimeImmutable($forecast['forecast_date'])
|
||||||
|
), $asOfDate);
|
||||||
}
|
}
|
||||||
$result['processed_products']++;
|
$result['processed_products']++;
|
||||||
} catch (Throwable $error) {
|
} catch (Throwable $error) {
|
||||||
|
|||||||
Reference in new issue
Block a user