testitest

This commit is contained in:
lubiana 2025-06-08 21:22:26 +02:00
parent 43ca79f650
commit 66c4c1fe4f
Signed by: lubiana
SSH key fingerprint: SHA256:vW1EA0fRR3Fw+dD/sM0K+x3Il2gSry6YRYHqOeQwrfk
30 changed files with 4443 additions and 184 deletions

View file

@ -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
]);
}
}

View file

@ -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();

View file

@ -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);
}

View file

@ -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]

View file

@ -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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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
{

View file

@ -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}'",
);

View 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,
) {}
}