testitest
This commit is contained in:
parent
43ca79f650
commit
66c4c1fe4f
30 changed files with 4443 additions and 184 deletions
|
@ -4,7 +4,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Enum\StockState;
|
||||
use App\Service\InventoryService;
|
||||
use App\ValueObject\DrinkStock;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
@ -20,8 +22,14 @@ final class Index extends AbstractController
|
|||
{
|
||||
$drinkStocks = $this->inventoryService->getAllDrinkTypesWithStockLevels();
|
||||
|
||||
$low = array_filter(
|
||||
$drinkStocks,
|
||||
fn (DrinkStock $stock): bool => $stock->stock === StockState::LOW || $stock->stock === StockState::CRITICAL,
|
||||
);
|
||||
|
||||
return $this->render('index.html.twig', [
|
||||
'drinkStocks' => $drinkStocks,
|
||||
'low' => $low
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,11 +21,16 @@ class Order
|
|||
private null|int $id = null;
|
||||
|
||||
#[ORM\Column(type: 'datetime_immutable')]
|
||||
private readonly DateTimeImmutable $createdAt;
|
||||
private DateTimeImmutable $createdAt;
|
||||
|
||||
#[ORM\Column(type: 'datetime_immutable')]
|
||||
private DateTimeImmutable $updatedAt;
|
||||
|
||||
public function setUpdatedAt(DateTimeImmutable $updatedAt): void
|
||||
{
|
||||
$this->updatedAt = $updatedAt;
|
||||
}
|
||||
|
||||
#[ORM\OneToMany(mappedBy: 'order', targetEntity: OrderItem::class, cascade: ['persist', 'remove'])]
|
||||
private Collection $orderItems;
|
||||
|
||||
|
@ -96,6 +101,7 @@ class Order
|
|||
return $this;
|
||||
}
|
||||
|
||||
|
||||
private function updateTimestamp(): void
|
||||
{
|
||||
$this->updatedAt = new DateTimeImmutable();
|
||||
|
|
|
@ -32,7 +32,7 @@ class OrderItem
|
|||
|
||||
#[ORM\ManyToOne(targetEntity: Order::class, inversedBy: 'orderItems')]
|
||||
#[ORM\JoinColumn(name: 'order_id', referencedColumnName: 'id', nullable: false)]
|
||||
private Order $order;
|
||||
private null|Order $order;
|
||||
|
||||
public function __construct(
|
||||
) {
|
||||
|
@ -53,7 +53,7 @@ class OrderItem
|
|||
public function setOrder(null|Order $order): self
|
||||
{
|
||||
// Remove from old order if exists
|
||||
if ($this->order instanceof Order && $this->order !== $order) {
|
||||
if (isset($this->order) && $this->order instanceof Order && $this->order !== $order) {
|
||||
$this->order->removeOrderItem($this);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,12 +13,12 @@ use Doctrine\ORM\Mapping as ORM;
|
|||
#[ORM\Table(name: 'system_config')]
|
||||
class SystemConfig
|
||||
{
|
||||
public const DEFAULT_STOCK_ADJUSTMENT_LOOKBACK_ORDERS = '3';
|
||||
public const DEFAULT_DEFAULT_DESIRED_STOCK = '2';
|
||||
public const DEFAULT_SYSTEM_NAME = 'Zaufen';
|
||||
public const DEFAULT_STOCK_INCREASE_AMOUNT = '1';
|
||||
public const DEFAULT_STOCK_DECREASE_AMOUNT = '1';
|
||||
public const DEFAULT_STOCK_LOW_MULTIPLIER = '0.3';
|
||||
public const string DEFAULT_STOCK_ADJUSTMENT_LOOKBACK_ORDERS = '3';
|
||||
public const string DEFAULT_DEFAULT_DESIRED_STOCK = '2';
|
||||
public const string DEFAULT_SYSTEM_NAME = 'Zaufen';
|
||||
public const string DEFAULT_STOCK_INCREASE_AMOUNT = '1';
|
||||
public const string DEFAULT_STOCK_DECREASE_AMOUNT = '1';
|
||||
public const string DEFAULT_STOCK_LOW_MULTIPLIER = '0.3';
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
|
|
|
@ -21,10 +21,12 @@ class SystemConfigRepository extends AbstractRepository
|
|||
public function findByKey(SystemSettingKey $key): SystemConfig
|
||||
{
|
||||
$config = $this->findOneBy([
|
||||
'key' => $key->value,
|
||||
'key' => $key,
|
||||
]);
|
||||
if (!($config instanceof SystemConfig)) {
|
||||
$config = new SystemConfig($key, SystemSettingKey::getDefaultValue($key));
|
||||
$config = new SystemConfig();
|
||||
$config->setKey($key);
|
||||
$config->setValue($key->getDefaultValue($key));
|
||||
$this->save($config);
|
||||
}
|
||||
return $config;
|
||||
|
@ -43,7 +45,9 @@ class SystemConfigRepository extends AbstractRepository
|
|||
if ($config instanceof SystemConfig) {
|
||||
$config->setValue($value);
|
||||
} else {
|
||||
$config = new SystemConfig($key, $value);
|
||||
$config = new SystemConfig();
|
||||
$config->setKey($key);
|
||||
$config->setValue($value);
|
||||
}
|
||||
$this->save($config);
|
||||
}
|
||||
|
|
|
@ -46,7 +46,9 @@ readonly class ConfigurationService
|
|||
throw new InvalidArgumentException("A configuration with the key '{$key->value}' already exists");
|
||||
}
|
||||
|
||||
$config = new SystemConfig($key, $value);
|
||||
$config = new SystemConfig();
|
||||
$config->setKey($key);
|
||||
$config->setValue($value);
|
||||
$this->systemConfigRepository->save($config);
|
||||
|
||||
return $config;
|
||||
|
|
|
@ -75,10 +75,13 @@ readonly class DrinkTypeService
|
|||
|
||||
// If no desired stock is provided, use the default from configuration
|
||||
if ($desiredStock === null) {
|
||||
$desiredStock = (int) $this->configService->getConfigByKey(SystemSettingKey::DEFAULT_DESIRED_STOCK);
|
||||
$desiredStock = (int) $this->configService->getConfigByKey(SystemSettingKey::DEFAULT_DESIRED_STOCK)->getValue();
|
||||
}
|
||||
|
||||
$drinkType = new DrinkType($name, $description, $desiredStock);
|
||||
$drinkType = new DrinkType();
|
||||
$drinkType->setName($name);
|
||||
$drinkType->setDescription($description);
|
||||
$drinkType->setDesiredStock($desiredStock);
|
||||
$this->drinkTypeRepository->save($drinkType);
|
||||
|
||||
return $drinkType;
|
||||
|
|
|
@ -51,7 +51,10 @@ readonly class InventoryService
|
|||
{
|
||||
$record = $this->inventoryRecordRepository->findLatestByDrinkType($drinkType);
|
||||
if (!($record instanceof InventoryRecord)) {
|
||||
return new InventoryRecord($drinkType, 0);
|
||||
$record = new InventoryRecord();
|
||||
$record->setDrinkType($drinkType);
|
||||
$record->setQuantity(0);
|
||||
$this->inventoryRecordRepository->save($record);
|
||||
}
|
||||
return $record;
|
||||
}
|
||||
|
@ -83,7 +86,10 @@ readonly class InventoryService
|
|||
int $quantity,
|
||||
DateTimeImmutable $timestamp = new DateTimeImmutable(),
|
||||
): InventoryRecord {
|
||||
$inventoryRecord = new InventoryRecord($drinkType, $quantity, $timestamp);
|
||||
$inventoryRecord = new InventoryRecord();
|
||||
$inventoryRecord->setDrinkType($drinkType);
|
||||
$inventoryRecord->setQuantity($quantity);
|
||||
$inventoryRecord->setTimestamp($timestamp);
|
||||
|
||||
$this->inventoryRecordRepository->save($inventoryRecord);
|
||||
|
||||
|
@ -91,7 +97,7 @@ readonly class InventoryService
|
|||
}
|
||||
|
||||
/**
|
||||
* @return InventoryRecord[]
|
||||
* @return DrinkStock[]
|
||||
*/
|
||||
public function getAllDrinkTypesWithStockLevels(bool $includeZeroDesiredStock = false): array
|
||||
{
|
||||
|
|
|
@ -124,7 +124,10 @@ readonly class OrderService
|
|||
throw new InvalidArgumentException("Invalid drink type ID: {$item['drinkTypeId']}");
|
||||
}
|
||||
|
||||
$orderItem = new OrderItem($drinkType, $item['quantity'], $order);
|
||||
$orderItem = new OrderItem();
|
||||
$orderItem->setDrinkType($drinkType);
|
||||
$orderItem->setQuantity($item['quantity']);
|
||||
$orderItem->setOrder($order);
|
||||
$this->orderItemRepository->save($orderItem);
|
||||
}
|
||||
|
||||
|
@ -142,10 +145,10 @@ readonly class OrderService
|
|||
$orderItems = [];
|
||||
|
||||
foreach ($lowStockItems as $item) {
|
||||
if ($item['currentStock'] < $item['desiredStock']) {
|
||||
if ($item->record->getQuantity() < $item->record->getDrinkType()->getDesiredStock()) {
|
||||
$orderItems[] = [
|
||||
'drinkTypeId' => $item['drinkType']->getId(),
|
||||
'quantity' => $item['desiredStock'] - $item['currentStock'],
|
||||
'drinkTypeId' => $item->record->getDrinkType()->getId(),
|
||||
'quantity' => $item->record->getDrinkType()->getDesiredStock() - $item->record->getQuantity(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +181,7 @@ readonly class OrderService
|
|||
*/
|
||||
public function addOrderItem(Order $order, DrinkType $drinkType, int $quantity): OrderItem
|
||||
{
|
||||
if (!in_array($order->getStatus(), [OrderStatus::STATUS_NEW, OrderStatus::STATUS_IN_WORK], true)) {
|
||||
if (!in_array($order->getStatus(), [OrderStatus::NEW, OrderStatus::IN_WORK], true)) {
|
||||
throw new InvalidArgumentException(
|
||||
"Cannot add items to an order with status '{$order->getStatus()->value}'",
|
||||
);
|
||||
|
@ -194,7 +197,10 @@ readonly class OrderService
|
|||
return $existingItem;
|
||||
}
|
||||
// Create a new item
|
||||
$orderItem = new OrderItem($drinkType, $quantity, $order);
|
||||
$orderItem = new OrderItem();
|
||||
$orderItem->setQuantity($quantity);
|
||||
$orderItem->setOrder($order);
|
||||
$orderItem->setDrinkType($drinkType);
|
||||
$this->orderItemRepository->save($orderItem);
|
||||
return $orderItem;
|
||||
}
|
||||
|
@ -209,7 +215,7 @@ readonly class OrderService
|
|||
*/
|
||||
public function removeOrderItem(Order $order, OrderItem $orderItem): void
|
||||
{
|
||||
if (!in_array($order->getStatus(), [OrderStatus::STATUS_NEW, OrderStatus::STATUS_IN_WORK], true)) {
|
||||
if (!in_array($order->getStatus(), [OrderStatus::NEW, OrderStatus::IN_WORK], true)) {
|
||||
throw new InvalidArgumentException(
|
||||
"Cannot remove items from an order with status '{$order->getStatus()->value}'",
|
||||
);
|
||||
|
|
15
src/ValueObject/StockAdjustmentProposal.php
Normal file
15
src/ValueObject/StockAdjustmentProposal.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\ValueObject;
|
||||
|
||||
use App\Entity\DrinkType;
|
||||
|
||||
final readonly class StockAdjustmentProposal
|
||||
{
|
||||
public function __construct(
|
||||
public DrinkType $drinkType,
|
||||
public int $proposedStock,
|
||||
) {}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue