saufen/src/Service/StockAdjustmentService.php
2025-06-08 13:58:51 +02:00

124 lines
4.8 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Service;
use App\Entity\DrinkType;
use App\Enum\SystemSettingKey;
use App\Repository\DrinkTypeRepository;
use App\Repository\InventoryRecordRepository;
use App\Repository\OrderRepository;
use App\Repository\SystemConfigRepository;
use App\ValueObject\StockAdjustmentProposal;
readonly class StockAdjustmentService
{
public function __construct(
private DrinkTypeRepository $drinkTypeRepository,
private InventoryRecordRepository $inventoryRecordRepository,
private OrderRepository $orderRepository,
private SystemConfigRepository $systemConfigRepository,
) {}
/**
* Proposes adjusted stock levels for all drink types
*
* @return array<int, StockAdjustmentProposal> Array of stock adjustment proposals
*/
public function proposeStockAdjustments(): array
{
$drinkTypes = $this->drinkTypeRepository->findAll();
$proposals = [];
foreach ($drinkTypes as $drinkType) {
$proposals[] = $this->proposeStockAdjustmentForDrinkType($drinkType);
}
return $proposals;
}
/**
* Proposes an adjusted stock level for a specific drink type
*/
public function proposeStockAdjustmentForDrinkType(DrinkType $drinkType): StockAdjustmentProposal
{
$currentDesiredStock = $drinkType->getDesiredStock();
$lookbackOrders =
(int) $this->systemConfigRepository->getValue(SystemSettingKey::STOCK_ADJUSTMENT_LOOKBACK_ORDERS);
$increaseAmount = (int) $this->systemConfigRepository->getValue(SystemSettingKey::STOCK_INCREASE_AMOUNT);
$decreaseAmount = (int) $this->systemConfigRepository->getValue(SystemSettingKey::STOCK_DECREASE_AMOUNT);
// Get the last N orders for this drink type
$lastOrders = $this->orderRepository->findLastOrdersForDrinkType($drinkType, $lookbackOrders);
// If there are no orders, return the current desired stock
if ($lastOrders === []) {
return new StockAdjustmentProposal($drinkType, $currentDesiredStock);
}
// Check if stock was 0 in the last order
$lastOrder = $lastOrders[0];
$lastOrderItems = $lastOrder->getOrderItems();
$stockWasZeroInLastOrder = false;
foreach ($lastOrderItems as $orderItem) {
if ($orderItem->getDrinkType()->getId() === $drinkType->getId()) {
// Find the inventory record closest to the order creation date
$inventoryRecords = $this->inventoryRecordRepository->findByDrinkType($drinkType);
foreach ($inventoryRecords as $record) {
if ($record->getTimestamp() <= $lastOrder->getCreatedAt()) {
if ($record->getQuantity() === 0) {
$stockWasZeroInLastOrder = true;
}
break;
}
}
break;
}
}
// If stock was 0 in the last order, increase desired stock
if ($stockWasZeroInLastOrder) {
return new StockAdjustmentProposal($drinkType, $currentDesiredStock + $increaseAmount);
}
// Check if stock was above zero in all lookback orders
$stockWasAboveZeroInAllOrders = true;
$ordersToCheck = min(count($lastOrders), $lookbackOrders);
for ($i = 0; $i < $ordersToCheck; $i++) {
$order = $lastOrders[$i];
$orderItems = $order->getOrderItems();
foreach ($orderItems as $orderItem) {
if ($orderItem->getDrinkType()->getId() === $drinkType->getId()) {
// Find the inventory record closest to the order creation date
$inventoryRecords = $this->inventoryRecordRepository->findByDrinkType($drinkType);
foreach ($inventoryRecords as $record) {
if ($record->getTimestamp() <= $order->getCreatedAt()) {
if ($record->getQuantity() === 0) {
$stockWasAboveZeroInAllOrders = false;
}
break;
}
}
break;
}
}
if (!$stockWasAboveZeroInAllOrders) {
break;
}
}
// If stock was above zero in all lookback orders, decrease desired stock
if ($stockWasAboveZeroInAllOrders && $ordersToCheck === $lookbackOrders) {
$proposedStock = max(0, $currentDesiredStock - $decreaseAmount);
return new StockAdjustmentProposal($drinkType, $proposedStock);
}
// Otherwise, keep the current desired stock
return new StockAdjustmentProposal($drinkType, $currentDesiredStock);
}
}