diff --git a/composer.json b/composer.json
index 18a169a..36c97de 100644
--- a/composer.json
+++ b/composer.json
@@ -25,6 +25,9 @@
"require-dev": {
"pestphp/pest": "^3.8",
"rector/rector": "^2.0",
+ "symfony/browser-kit": "^7.3",
+ "symfony/css-selector": "7.3.*",
+ "symfony/dom-crawler": "7.3.*",
"symfony/maker-bundle": "^1.63",
"symfony/stopwatch": "7.3.*",
"symfony/web-profiler-bundle": "7.3.*",
diff --git a/composer.lock b/composer.lock
index e726044..8d9fa95 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "139ab419de1a7d3014e5595dbe864775",
+ "content-hash": "7dbc573cc9b3d6ab3fb532611e469c70",
"packages": [
{
"name": "doctrine/cache",
@@ -5196,6 +5196,73 @@
},
"time": "2025-03-19T14:43:43+00:00"
},
+ {
+ "name": "masterminds/html5",
+ "version": "2.9.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Masterminds/html5-php.git",
+ "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
+ "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Masterminds\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matt Butcher",
+ "email": "technosophos@gmail.com"
+ },
+ {
+ "name": "Matt Farina",
+ "email": "matt@mattfarina.com"
+ },
+ {
+ "name": "Asmir Mustafic",
+ "email": "goetas@gmail.com"
+ }
+ ],
+ "description": "An HTML5 parser and serializer.",
+ "homepage": "http://masterminds.github.io/html5-php",
+ "keywords": [
+ "HTML5",
+ "dom",
+ "html",
+ "parser",
+ "querypath",
+ "serializer",
+ "xml"
+ ],
+ "support": {
+ "issues": "https://github.com/Masterminds/html5-php/issues",
+ "source": "https://github.com/Masterminds/html5-php/tree/2.9.0"
+ },
+ "time": "2024-03-31T07:05:07+00:00"
+ },
{
"name": "myclabs/deep-copy",
"version": "1.13.1",
@@ -7746,6 +7813,206 @@
],
"time": "2024-10-20T05:08:20+00:00"
},
+ {
+ "name": "symfony/browser-kit",
+ "version": "v7.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/browser-kit.git",
+ "reference": "5384291845e74fd7d54f3d925c4a86ce12336593"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/browser-kit/zipball/5384291845e74fd7d54f3d925c4a86ce12336593",
+ "reference": "5384291845e74fd7d54f3d925c4a86ce12336593",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/dom-crawler": "^6.4|^7.0"
+ },
+ "require-dev": {
+ "symfony/css-selector": "^6.4|^7.0",
+ "symfony/http-client": "^6.4|^7.0",
+ "symfony/mime": "^6.4|^7.0",
+ "symfony/process": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\BrowserKit\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/browser-kit/tree/v7.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-03-05T10:15:41+00:00"
+ },
+ {
+ "name": "symfony/css-selector",
+ "version": "v7.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/css-selector.git",
+ "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2",
+ "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\CssSelector\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Jean-François Simon",
+ "email": "jeanfrancois.simon@sensiolabs.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Converts CSS selectors to XPath expressions",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/css-selector/tree/v7.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:21:43+00:00"
+ },
+ {
+ "name": "symfony/dom-crawler",
+ "version": "v7.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/dom-crawler.git",
+ "reference": "0fabbc3d6a9c473b716a93fc8e7a537adb396166"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/0fabbc3d6a9c473b716a93fc8e7a537adb396166",
+ "reference": "0fabbc3d6a9c473b716a93fc8e7a537adb396166",
+ "shasum": ""
+ },
+ "require": {
+ "masterminds/html5": "^2.6",
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "require-dev": {
+ "symfony/css-selector": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\DomCrawler\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases DOM navigation for HTML and XML documents",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/dom-crawler/tree/v7.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-03-05T10:15:41+00:00"
+ },
{
"name": "symfony/maker-bundle",
"version": "v1.63.0",
diff --git a/config/services.php b/config/services.php
index 6168c78..d9df3a6 100644
--- a/config/services.php
+++ b/config/services.php
@@ -1,8 +1,6 @@
findBy([
'entityClass' => DrinkType::class,
'propertyName' => 'desiredStock',
- 'entityId' => $drinkType->getId()
- ], ['changeDate' => 'DESC']);
+ 'entityId' => $drinkType->getId(),
+ ], [
+ 'changeDate' => 'DESC',
+ ]);
return $this->render('drink_type/show.html.twig', [
'drink_type' => $drinkType,
diff --git a/src/Controller/Index.php b/src/Controller/Index.php
index aaabdfd..20b6077 100644
--- a/src/Controller/Index.php
+++ b/src/Controller/Index.php
@@ -4,9 +4,6 @@ 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;
@@ -14,19 +11,8 @@ use Symfony\Component\Routing\Attribute\Route;
#[Route(path: '/', name: 'app_index')]
final class Index extends AbstractController
{
-
public function __invoke(): Response
{
- $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,
- ]);
+ return new Response('
Hello World!
');
}
}
diff --git a/src/Entity/PropertyChangeLog.php b/src/Entity/PropertyChangeLog.php
index 43bff16..f444baf 100644
--- a/src/Entity/PropertyChangeLog.php
+++ b/src/Entity/PropertyChangeLog.php
@@ -6,13 +6,14 @@ namespace App\Entity;
use App\Repository\PropertyChangeLogRepository;
use Doctrine\ORM\Mapping\Column;
+use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use DateTimeImmutable;
use Doctrine\ORM\Mapping\Id;
-use Doctrine\ORM\Mapping as ORM;
+use Doctrine\ORM\Mapping\Table;
-#[ORM\Entity(repositoryClass: PropertyChangeLogRepository::class)]
-#[ORM\Table(name: 'property_change_log')]
+#[Entity(repositoryClass: PropertyChangeLogRepository::class)]
+#[Table(name: 'property_change_log')]
final class PropertyChangeLog
{
#[Id]
diff --git a/src/EventListener/PostPersistUpdateListener.php b/src/EventListener/PostPersistUpdateListener.php
index 43965f8..b94badc 100644
--- a/src/EventListener/PostPersistUpdateListener.php
+++ b/src/EventListener/PostPersistUpdateListener.php
@@ -1,21 +1,20 @@
-log(Events::postPersist, $args);
@@ -25,12 +24,12 @@ final class PostPersistUpdateListener
{
$this->log(Events::postUpdate, $args);
}
- private function log(string $action, LifecycleEventArgs $args): void
- {
- $entity = $args->getObject();
- match($entity::class) {
- DrinkType::class => $this->drinkTypeUpdate->handleLog($action, $entity, $args->getObjectManager()),
- default => null,
- };
- }
+ private function log(string $action, LifecycleEventArgs $args): void
+ {
+ $entity = $args->getObject();
+ match ($entity::class) {
+ DrinkType::class => $this->drinkTypeUpdate->handleLog($action, $entity, $args->getObjectManager()),
+ default => null,
+ };
+ }
}
diff --git a/src/Repository/DrinkTypeRepository.php b/src/Repository/DrinkTypeRepository.php
index f4f2bc4..bd26f83 100644
--- a/src/Repository/DrinkTypeRepository.php
+++ b/src/Repository/DrinkTypeRepository.php
@@ -25,20 +25,20 @@ class DrinkTypeRepository extends AbstractRepository
return parent::findBy(
criteria: [],
orderBy: [
- 'desiredStock' => 'DESC',
+ 'wantedStock' => 'DESC',
],
);
}
/** @return DrinkType[] */
- public function findDesired(): array
+ public function findWanted(): array
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('d')
->from(DrinkType::class, 'd')
- ->where('d.desiredStock > 0')
- ->orderBy('d.desiredStock', 'DESC')
+ ->where('d.wantedStock > 0')
+ ->orderBy('d.wantedStock', 'DESC')
->addOrderBy('d.name', 'ASC');
/** @var array $result */
diff --git a/src/Repository/SystemConfigRepository.php b/src/Repository/SystemConfigRepository.php
index 4b813ed..0875798 100644
--- a/src/Repository/SystemConfigRepository.php
+++ b/src/Repository/SystemConfigRepository.php
@@ -26,7 +26,7 @@ class SystemConfigRepository extends AbstractRepository
if (!($config instanceof SystemConfig)) {
$config = new SystemConfig();
$config->setKey($key);
- $config->setValue($key->defaultValue($key));
+ $config->setValue($key->defaultValue());
$this->save($config);
}
return $config;
diff --git a/src/Service/Config/AppName.php b/src/Service/Config/AppName.php
index 43cc62d..6137828 100644
--- a/src/Service/Config/AppName.php
+++ b/src/Service/Config/AppName.php
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace App\Service\Config;
-use App\Entity\SystemConfig;
use App\Enum\SystemSettingKey;
use App\Service\ConfigurationService;
use Stringable;
diff --git a/src/Service/Config/LowStockMultiplier.php b/src/Service/Config/LowStockMultiplier.php
index 3f85cb7..195adb2 100644
--- a/src/Service/Config/LowStockMultiplier.php
+++ b/src/Service/Config/LowStockMultiplier.php
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace App\Service\Config;
-use App\Entity\SystemConfig;
use App\Enum\SystemSettingKey;
use App\Service\ConfigurationService;
@@ -16,11 +15,6 @@ final readonly class LowStockMultiplier
public function getValue(): float
{
- $value = $this->configService->getConfigValue(
- SystemSettingKey::STOCK_LOW_MULTIPLIER,
- SystemConfig::DEFAULT_STOCK_LOW_MULTIPLIER,
- );
-
- return (float) $value;
+ return (float) $this->configService->get(SystemSettingKey::STOCK_LOW_MULTIPLIER);
}
}
diff --git a/src/Service/ConfigurationService.php b/src/Service/ConfigurationService.php
index 57d1202..296eda8 100644
--- a/src/Service/ConfigurationService.php
+++ b/src/Service/ConfigurationService.php
@@ -4,10 +4,8 @@ declare(strict_types=1);
namespace App\Service;
-use App\Entity\SystemConfig;
use App\Enum\SystemSettingKey;
use App\Repository\SystemConfigRepository;
-use InvalidArgumentException;
readonly class ConfigurationService
{
diff --git a/src/Service/DrinkType/CreateNewOrderItems.php b/src/Service/DrinkType/CreateNewOrderItems.php
new file mode 100644
index 0000000..2c139fb
--- /dev/null
+++ b/src/Service/DrinkType/CreateNewOrderItems.php
@@ -0,0 +1,30 @@
+drinkTypeRepository->findWanted())->forAll(
+ fn (DrinkType $drinkType) => $order
+ ->addOrderItem(
+ new OrderItem()
+ ->setDrinkType($drinkType)
+ ->setQuantity($drinkType->getWantedStock() - $drinkType->getCurrentStock())
+ ),
+ );
+ }
+}
diff --git a/src/Service/DrinkType/DrinkTypeUpdateLog.php b/src/Service/DrinkType/DrinkTypeUpdateLog.php
new file mode 100644
index 0000000..307e20a
--- /dev/null
+++ b/src/Service/DrinkType/DrinkTypeUpdateLog.php
@@ -0,0 +1,63 @@
+ $this->handleCreate($entity, $em),
+ Events::postUpdate => $this->handleUpdate($entity, $em),
+ };
+ }
+
+ private function handleCreate(DrinkType $entity, EntityManagerInterface $em): void
+ {
+ $this->logWanted($entity, $em);
+ $this->logCurrent($entity, $em);
+ }
+
+ private function logWanted(DrinkType $entity, EntityManagerInterface $em): void
+ {
+ $logWanted = new PropertyChangeLog();
+ $logWanted->setNewValue((string) $entity->getWantedStock());
+ $logWanted->setEntityClass(DrinkType::class);
+ $logWanted->setEntityId($entity->getId());
+ $logWanted->setPropertyName('wantedStock');
+
+ $em->persist($logWanted);
+ $em->flush();
+ }
+
+ private function logCurrent(DrinkType $entity, EntityManagerInterface $em): void
+ {
+ $logCurrent = new PropertyChangeLog();
+ $logCurrent->setNewValue((string) $entity->getCurrentStock());
+ $logCurrent->setEntityClass(DrinkType::class);
+ $logCurrent->setEntityId($entity->getId());
+ $logCurrent->setPropertyName('currentStock');
+
+ $em->persist($logCurrent);
+ $em->flush();
+ }
+
+ private function handleUpdate(DrinkType $entity, EntityManagerInterface $em): void
+ {
+ $uow = $em->getUnitOfWork();
+ $changeSet = $uow->getEntityChangeSet($entity);
+ if (isset($changeSet['wantedStock'])) {
+ $this->logWanted($entity, $em);
+ }
+ if (isset($changeSet['currentStock'])) {
+ $this->logCurrent($entity, $em);
+ }
+ }
+}
diff --git a/src/Service/DrinkType/GetStockHistory.php b/src/Service/DrinkType/GetStockHistory.php
new file mode 100644
index 0000000..a9f34c1
--- /dev/null
+++ b/src/Service/DrinkType/GetStockHistory.php
@@ -0,0 +1,32 @@
+getId() === null) {
+ return[];
+ }
+ return $this->propertyChangeLogRepository->findBy([
+ 'entityClass' => DrinkType::class,
+ 'propertyName' => 'currentStock',
+ 'entityId' => $drinkType->getId(),
+ ]);
+ }
+}
diff --git a/src/Service/DrinkType/GetWantedHistory.php b/src/Service/DrinkType/GetWantedHistory.php
new file mode 100644
index 0000000..9e1533b
--- /dev/null
+++ b/src/Service/DrinkType/GetWantedHistory.php
@@ -0,0 +1,27 @@
+getId() === null) {
+ return[];
+ }
+
+ return $this->propertyChangeLogRepository->findBy([
+ 'entityClass' => DrinkType::class,
+ 'propertyName' => 'wantedStock',
+ 'entityId' => $drinkType->getId(),
+ ]);
+ }
+}
diff --git a/src/Service/DrinkTypeUpdate.php b/src/Service/DrinkTypeUpdate.php
deleted file mode 100644
index 8b2b087..0000000
--- a/src/Service/DrinkTypeUpdate.php
+++ /dev/null
@@ -1,61 +0,0 @@
- $this->handleCreate($entity, $em),
- Events::postUpdate => $this->handleUpdate($entity, $em),
- };
- }
-
- private function handleCreate(DrinkType $entity, EntityManagerInterface $em): void
- {
- $this->logWanted($entity, $em);
- $this->logCurrent($entity, $em);
- }
-
- private function logWanted(DrinkType $entity, EntityManagerInterface $em): void
- {
- $logWanted = new PropertyChangeLog();
- $logWanted->setNewValue((string) $entity->getWantedStock());
- $logWanted->setEntityClass(DrinkType::class);
- $logWanted->setEntityId($entity->getId());
- $logWanted->setPropertyName('wantedStock');
-
- $em->persist($logWanted);
- $em->flush();
- }
-
- private function logCurrent(DrinkType $entity, EntityManagerInterface $em): void
- {
- $logCurrent = new PropertyChangeLog();
- $logCurrent->setNewValue((string) $entity->getCurrentStock());
- $logCurrent->setEntityClass(DrinkType::class);
- $logCurrent->setEntityId($entity->getId());
- $logCurrent->setPropertyName('currentStock');
-
- $em->persist($logCurrent);
- $em->flush();
- }
-
- private function handleUpdate(DrinkType $entity, EntityManagerInterface $em): void
- {
- $uow = $em->getUnitOfWork();
- $changeSet = $uow->getEntityChangeSet($entity);
- if (isset($changeSet['wantedStock'])) {
- $this->logWanted($entity, $em);;
- }
- if (isset($changeSet['currentStock'])) {
- $this->logCurrent($entity,$em);
- }
- }
-}
diff --git a/src/Service/OrderService.php b/src/Service/OrderService.php
index 733e905..1bb1f64 100644
--- a/src/Service/OrderService.php
+++ b/src/Service/OrderService.php
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace App\Service;
-use App\Entity\DrinkType;
use App\Entity\Order;
use App\Entity\OrderItem;
use App\Enum\OrderStatus;
@@ -132,111 +131,4 @@ readonly class OrderService
return $order;
}
-
- /**
- * Create an order based on current stock levels
- *
- * @return Order
- */
- public function createOrderFromStockLevels(): Order
- {
- $lowStockItems = $this->inventoryService->getAllDrinkTypesWithStockLevels();
- $orderItems = [];
-
- foreach ($lowStockItems as $item) {
- if ($item->record->getQuantity() < $item->record->getDrinkType()->getDesiredStock()) {
- $orderItems[] = [
- 'drinkTypeId' => $item->record->getDrinkType()->getId(),
- 'quantity' => $item->record->getDrinkType()->getDesiredStock() - $item->record->getQuantity(),
- ];
- }
- }
-
- return $this->createOrder($orderItems);
- }
-
- /**
- * Update an order's status
- *
- * @return Order
- * @throws InvalidArgumentException If the status is invalid
- */
- public function updateOrderStatus(Order $order, OrderStatus $status): Order
- {
- $order->setStatus($status);
- $this->orderRepository->save($order);
-
- return $order;
- }
-
- /**
- * Add an item to an order
- *
- * @param Order $order
- * @param DrinkType $drinkType
- * @param int $quantity
- * @return OrderItem
- * @throws InvalidArgumentException If the order is not in 'new' or 'in_work' status
- */
- public function addOrderItem(Order $order, DrinkType $drinkType, int $quantity): OrderItem
- {
- 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}'",
- );
- }
-
- // Check if the order already has an item for this drink type
- $existingItem = $this->orderItemRepository->findByOrderAndDrinkType($order, $drinkType);
-
- if ($existingItem instanceof OrderItem) {
- // Update the existing item
- $existingItem->setQuantity($existingItem->getQuantity() + $quantity);
- $this->orderItemRepository->save($existingItem);
- return $existingItem;
- }
- // Create a new item
- $orderItem = new OrderItem();
- $orderItem->setQuantity($quantity);
- $orderItem->setOrder($order);
- $orderItem->setDrinkType($drinkType);
- $this->orderItemRepository->save($orderItem);
- return $orderItem;
- }
-
- /**
- * Remove an item from an order
- *
- * @param Order $order
- * @param OrderItem $orderItem
- * @return void
- * @throws InvalidArgumentException If the order is not in 'new' or 'in_work' status
- */
- public function removeOrderItem(Order $order, OrderItem $orderItem): void
- {
- 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}'",
- );
- }
-
- $order->removeOrderItem($orderItem);
- $this->orderItemRepository->remove($orderItem);
- }
-
- /**
- * Delete an order
- *
- * @param Order $order
- * @return void
- * @throws InvalidArgumentException If the order is not in 'new' status
- */
- public function deleteOrder(Order $order): void
- {
- if ($order->getStatus() !== OrderStatus::NEW && $order->getStatus() !== OrderStatus::IN_WORK) {
- throw new InvalidArgumentException("Cannot delete an order with status '{$order->getStatus()->value}'");
- }
-
- $this->orderRepository->remove($order);
- }
}
diff --git a/src/ValueObject/DrinkStock.php b/src/ValueObject/DrinkStock.php
deleted file mode 100644
index 3b27e8a..0000000
--- a/src/ValueObject/DrinkStock.php
+++ /dev/null
@@ -1,31 +0,0 @@
-getQuantity() === 0 && $record->getDrinkType()->getDesiredStock() > 0) {
- return new self($record, StockState::CRITICAL);
- }
- if ($record->getQuantity() < ($record->getDrinkType()->getDesiredStock() * $lowStockMultiplier)) {
- return new self($record, StockState::LOW);
- }
- if ($record->getQuantity() > $record->getDrinkType()->getDesiredStock()) {
- return new self($record, StockState::HIGH);
- }
-
- return new self($record, StockState::NORMAL);
- }
-}
diff --git a/tests/Feature/Entity/DrinkTypeTest.php b/tests/Feature/Entity/DrinkTypeTest.php
index 9c3236c..3e8a829 100644
--- a/tests/Feature/Entity/DrinkTypeTest.php
+++ b/tests/Feature/Entity/DrinkTypeTest.php
@@ -1,10 +1,12 @@
setName('test');
$drinkType->setWantedStock(10);
@@ -16,26 +18,34 @@ test('Update Listener', function () {
$em->flush();
$propertyLogRepository = $em->getRepository(PropertyChangeLog::class);
- $logs = $propertyLogRepository->findBy(['entityClass' => DrinkType::class]);
+ $logs = $propertyLogRepository->findBy([
+ 'entityClass' => DrinkType::class,
+ ]);
expect($logs)->toHaveCount(2);
$drinkType->setWantedStock(15);
$em->persist($drinkType);
$em->flush();
- $logs = $propertyLogRepository->findBy(['entityClass' => DrinkType::class]);
+ $logs = $propertyLogRepository->findBy([
+ 'entityClass' => DrinkType::class,
+ ]);
expect($logs)->toHaveCount(3);
$drinkType->setCurrentStock(15);
$em->persist($drinkType);
$em->flush();
- $logs = $propertyLogRepository->findBy(['entityClass' => DrinkType::class]);
+ $logs = $propertyLogRepository->findBy([
+ 'entityClass' => DrinkType::class,
+ ]);
expect($logs)->toHaveCount(4);
$drinkType->setDescription('test');
$em->persist($drinkType);
$em->flush();
- $logs = $propertyLogRepository->findBy(['entityClass' => DrinkType::class]);
+ $logs = $propertyLogRepository->findBy([
+ 'entityClass' => DrinkType::class,
+ ]);
expect($logs)->toHaveCount(4);
});
diff --git a/tests/Feature/Repository/DrinkTypeRepositoryTest.php b/tests/Feature/Repository/DrinkTypeRepositoryTest.php
new file mode 100644
index 0000000..5f8209b
--- /dev/null
+++ b/tests/Feature/Repository/DrinkTypeRepositoryTest.php
@@ -0,0 +1,105 @@
+getContainer()->get(EntityManagerInterface::class);
+ $repository = $this->getContainer()->get(DrinkTypeRepository::class);
+
+ // Clear existing drink types
+ $existingDrinkTypes = $repository->findAll();
+ foreach ($existingDrinkTypes as $drinkType) {
+ $em->remove($drinkType);
+ }
+ $em->flush();
+
+ // Create test drink types with different wantedStock values
+ $drinkType1 = new DrinkType();
+ $drinkType1->setName('Drink Type 1');
+ $drinkType1->setWantedStock(5);
+
+ $drinkType2 = new DrinkType();
+ $drinkType2->setName('Drink Type 2');
+ $drinkType2->setWantedStock(10);
+
+ $drinkType3 = new DrinkType();
+ $drinkType3->setName('Drink Type 3');
+ $drinkType3->setWantedStock(0);
+
+ // Persist the drink types
+ $em->persist($drinkType1);
+ $em->persist($drinkType2);
+ $em->persist($drinkType3);
+ $em->flush();
+
+ // Test findAll method
+ $result = $repository->findAll();
+
+ // Verify the result
+ expect($result)->toHaveCount(3);
+ expect($result[0]->getName())->toBe('Drink Type 2');
+ expect($result[1]->getName())->toBe('Drink Type 1');
+ expect($result[2]->getName())->toBe('Drink Type 3');
+});
+
+test('findWanted returns only drink types with wantedStock > 0 ordered by wantedStock DESC and name ASC', function (): void {
+ $em = $this->getContainer()->get(EntityManagerInterface::class);
+ $repository = $this->getContainer()->get(DrinkTypeRepository::class);
+
+ // Clear existing drink types
+ $existingDrinkTypes = $repository->findAll();
+ foreach ($existingDrinkTypes as $drinkType) {
+ $em->remove($drinkType);
+ }
+ $em->flush();
+
+ // Create test drink types with different wantedStock values
+ $drinkType1 = new DrinkType();
+ $drinkType1->setName('Cola');
+ $drinkType1->setWantedStock(10);
+
+ $drinkType2 = new DrinkType();
+ $drinkType2->setName('Beer');
+ $drinkType2->setWantedStock(10);
+
+ $drinkType3 = new DrinkType();
+ $drinkType3->setName('Water');
+ $drinkType3->setWantedStock(5);
+
+ $drinkType4 = new DrinkType();
+ $drinkType4->setName('Juice');
+ $drinkType4->setWantedStock(0);
+
+ // Persist the drink types
+ $em->persist($drinkType1);
+ $em->persist($drinkType2);
+ $em->persist($drinkType3);
+ $em->persist($drinkType4);
+ $em->flush();
+
+ // Test findWanted method
+ $result = $repository->findWanted();
+
+ // Verify the result
+ expect($result)->toHaveCount(3);
+
+ // First should be Beer (wantedStock 10, name starts with B)
+ expect($result[0]->getName())->toBe('Beer');
+ expect($result[0]->getWantedStock())->toBe(10);
+
+ // Second should be Cola (wantedStock 10, name starts with C)
+ expect($result[1]->getName())->toBe('Cola');
+ expect($result[1]->getWantedStock())->toBe(10);
+
+ // Third should be Water (wantedStock 5)
+ expect($result[2]->getName())->toBe('Water');
+ expect($result[2]->getWantedStock())->toBe(5);
+
+ // Juice should not be in the result (wantedStock 0)
+ $names = array_map(fn($dt) => $dt->getName(), $result);
+ expect($names)->not->toContain('Juice');
+});
diff --git a/tests/Feature/Service/Config/AppNameTest.php b/tests/Feature/Service/Config/AppNameTest.php
index bd4cb89..6fa1cec 100644
--- a/tests/Feature/Service/Config/AppNameTest.php
+++ b/tests/Feature/Service/Config/AppNameTest.php
@@ -1,18 +1,20 @@
getContainer()->get(ConfigurationService::class);
$appName = new AppName($configService);
- expect((string)$appName)->toBe(SystemSettingKey::SYSTEM_NAME->defaultValue());
+ expect((string) $appName)->toBe(SystemSettingKey::SYSTEM_NAME->defaultValue());
$expected = 'Test System Name';
$configService->set(SystemSettingKey::SYSTEM_NAME, $expected);
- expect((string)$appName)->toBe($expected);
+ expect((string) $appName)->toBe($expected);
});
diff --git a/tests/Feature/Service/ConfigurationServiceTest.php b/tests/Feature/Service/ConfigurationServiceTest.php
index f7af002..7b4773c 100644
--- a/tests/Feature/Service/ConfigurationServiceTest.php
+++ b/tests/Feature/Service/ConfigurationServiceTest.php
@@ -1,12 +1,9 @@
setName('Test Drink');
+
+ $getStockHistory = $this->getContainer()->get(GetStockHistory::class);
+ $result = $getStockHistory($drinkType);
+
+ expect($result)->toBeArray();
+ expect($result)->toBeEmpty();
+});
+
+test('it returns stock history for a drink type', function (): void {
+ // Create a drink type
+ $drinkType = new DrinkType();
+ $drinkType->setName('Test Drink');
+ $drinkType->setCurrentStock(10);
+
+ $em = $this->getContainer()->get(EntityManagerInterface::class);
+ $em->persist($drinkType);
+ $em->flush();
+
+ // Change the current stock to create logs
+ $drinkType->setCurrentStock(15);
+ $em->persist($drinkType);
+ $em->flush();
+
+ $drinkType->setCurrentStock(5);
+ $em->persist($drinkType);
+ $em->flush();
+
+ // Get the stock history
+ $getStockHistory = $this->getContainer()->get(GetStockHistory::class);
+ $result = $getStockHistory($drinkType);
+
+ // Verify the result
+ expect($result)->toBeArray();
+ expect($result)->toHaveCount(3);
+
+ // Verify that all logs are for currentStock
+ foreach ($result as $log) {
+ expect($log)->toBeInstanceOf(PropertyChangeLog::class);
+ expect($log->getPropertyName())->toBe('currentStock');
+ expect($log->getEntityClass())->toBe(DrinkType::class);
+ expect($log->getEntityId())->toBe($drinkType->getId());
+ }
+
+ // Verify the values in order (oldest first)
+ expect($result[0]->getNewValue())->toBe('10');
+ expect($result[1]->getNewValue())->toBe('15');
+ expect($result[2]->getNewValue())->toBe('5');
+});
+
+test('it only returns logs for the specified drink type', function (): void {
+ $em = $this->getContainer()->get(EntityManagerInterface::class);
+
+ // Create first drink type
+ $drinkType1 = new DrinkType();
+ $drinkType1->setName('Drink 1');
+ $drinkType1->setCurrentStock(10);
+ $em->persist($drinkType1);
+ $em->flush();
+
+ // Create second drink type
+ $drinkType2 = new DrinkType();
+ $drinkType2->setName('Drink 2');
+ $drinkType2->setCurrentStock(20);
+ $em->persist($drinkType2);
+ $em->flush();
+
+ // Get history for first drink type
+ $getStockHistory = $this->getContainer()->get(GetStockHistory::class);
+ $result = $getStockHistory($drinkType1);
+
+ // Verify we only get logs for the first drink type
+ expect($result)->toHaveCount(1);
+ expect($result[0]->getEntityId())->toBe($drinkType1->getId());
+ expect($result[0]->getNewValue())->toBe('10');
+});
diff --git a/tests/Feature/Service/DrinkType/GetWantedHistoryTest.php b/tests/Feature/Service/DrinkType/GetWantedHistoryTest.php
new file mode 100644
index 0000000..1259ab2
--- /dev/null
+++ b/tests/Feature/Service/DrinkType/GetWantedHistoryTest.php
@@ -0,0 +1,87 @@
+setName('Test Drink');
+
+ $getWantedHistory = $this->getContainer()->get(GetWantedHistory::class);
+ $result = $getWantedHistory($drinkType);
+
+ expect($result)->toBeArray();
+ expect($result)->toBeEmpty();
+});
+
+test('it returns wanted stock history for a drink type', function (): void {
+ // Create a drink type
+ $drinkType = new DrinkType();
+ $drinkType->setName('Test Drink');
+ $drinkType->setWantedStock(10);
+
+ $em = $this->getContainer()->get(EntityManagerInterface::class);
+ $em->persist($drinkType);
+ $em->flush();
+
+ // Change the wanted stock to create logs
+ $drinkType->setWantedStock(15);
+ $em->persist($drinkType);
+ $em->flush();
+
+ $drinkType->setWantedStock(5);
+ $em->persist($drinkType);
+ $em->flush();
+
+ // Get the wanted stock history
+ $getWantedHistory = $this->getContainer()->get(GetWantedHistory::class);
+ $result = $getWantedHistory($drinkType);
+
+ // Verify the result
+ expect($result)->toBeArray();
+ expect($result)->toHaveCount(3);
+
+ // Verify that all logs are for wantedStock
+ foreach ($result as $log) {
+ expect($log)->toBeInstanceOf(PropertyChangeLog::class);
+ expect($log->getPropertyName())->toBe('wantedStock');
+ expect($log->getEntityClass())->toBe(DrinkType::class);
+ expect($log->getEntityId())->toBe($drinkType->getId());
+ }
+
+ // Verify the values in order (oldest first)
+ expect($result[0]->getNewValue())->toBe('10');
+ expect($result[1]->getNewValue())->toBe('15');
+ expect($result[2]->getNewValue())->toBe('5');
+});
+
+test('it only returns logs for the specified drink type', function (): void {
+ $em = $this->getContainer()->get(EntityManagerInterface::class);
+
+ // Create first drink type
+ $drinkType1 = new DrinkType();
+ $drinkType1->setName('Drink 1');
+ $drinkType1->setWantedStock(10);
+ $em->persist($drinkType1);
+ $em->flush();
+
+ // Create second drink type
+ $drinkType2 = new DrinkType();
+ $drinkType2->setName('Drink 2');
+ $drinkType2->setWantedStock(20);
+ $em->persist($drinkType2);
+ $em->flush();
+
+ // Get history for first drink type
+ $getWantedHistory = $this->getContainer()->get(GetWantedHistory::class);
+ $result = $getWantedHistory($drinkType1);
+
+ // Verify we only get logs for the first drink type
+ expect($result)->toHaveCount(1);
+ expect($result[0]->getEntityId())->toBe($drinkType1->getId());
+ expect($result[0]->getNewValue())->toBe('10');
+});
diff --git a/tests/Feature/Web/HelloWorldTest.php b/tests/Feature/Web/HelloWorldTest.php
new file mode 100644
index 0000000..78303d2
--- /dev/null
+++ b/tests/Feature/Web/HelloWorldTest.php
@@ -0,0 +1,18 @@
+ensureKernelShutdown();
+ $client = static::createClient();
+
+ // Request a specific page
+ $crawler = $client->request('GET', '/');
+
+ // Validate a successful response and some content
+ $this->assertResponseIsSuccessful();
+ $this->assertSelectorTextContains('h1', 'Hello World');
+});
diff --git a/tests/TestCase.php b/tests/TestCase.php
index 8966cfd..142058b 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -4,6 +4,6 @@ declare(strict_types=1);
namespace Tests;
-use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
+use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
-abstract class TestCase extends KernelTestCase {}
+abstract class TestCase extends WebTestCase {}
diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php
deleted file mode 100644
index 3dbe6e4..0000000
--- a/tests/Unit/ExampleTest.php
+++ /dev/null
@@ -1,7 +0,0 @@
-toBeTrue();
-});