124 lines
4.8 KiB
PHP
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);
|
|
}
|
|
}
|