improv
This commit is contained in:
parent
66c4c1fe4f
commit
2c2e34b71e
42 changed files with 910 additions and 939 deletions
|
@ -6,7 +6,7 @@ namespace App\Controller;
|
|||
|
||||
use App\Entity\DrinkType;
|
||||
use App\Form\DrinkTypeForm;
|
||||
use App\Repository\DrinkTypeRepository;
|
||||
use App\Repository\PropertyChangeLogRepository;
|
||||
use App\Service\InventoryService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
|
@ -21,7 +21,7 @@ final class DrinkTypeController extends AbstractController
|
|||
public function index(InventoryService $inventoryService): Response
|
||||
{
|
||||
return $this->render('drink_type/index.html.twig', [
|
||||
'drink_stocks' => $inventoryService->getAllDrinkTypesWithStockLevels(true)
|
||||
'drink_stocks' => $inventoryService->getAllDrinkTypesWithStockLevels(true),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -46,10 +46,26 @@ final class DrinkTypeController extends AbstractController
|
|||
}
|
||||
|
||||
#[Route(path: '/{id}', name: 'app_drink_type_show', methods: ['GET'])]
|
||||
public function show(DrinkType $drinkType): Response
|
||||
public function show(DrinkType $drinkType, PropertyChangeLogRepository $propertyChangeLogRepository): Response
|
||||
{
|
||||
// Get orders that contain this drink type
|
||||
$orderItems = $drinkType->getOrderItems();
|
||||
|
||||
// Get inventory history for this drink type
|
||||
$inventoryRecords = $drinkType->getInventoryRecords();
|
||||
|
||||
// Get desired stock history from PropertyChangeLog
|
||||
$desiredStockHistory = $propertyChangeLogRepository->findBy([
|
||||
'entityClass' => DrinkType::class,
|
||||
'propertyName' => 'desiredStock',
|
||||
'entityId' => $drinkType->getId()
|
||||
], ['changeDate' => 'DESC']);
|
||||
|
||||
return $this->render('drink_type/show.html.twig', [
|
||||
'drink_type' => $drinkType,
|
||||
'order_items' => $orderItems,
|
||||
'inventory_records' => $inventoryRecords,
|
||||
'desired_stock_history' => $desiredStockHistory,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,12 +24,12 @@ final class Index extends AbstractController
|
|||
|
||||
$low = array_filter(
|
||||
$drinkStocks,
|
||||
fn (DrinkStock $stock): bool => $stock->stock === StockState::LOW || $stock->stock === StockState::CRITICAL,
|
||||
fn(DrinkStock $stock): bool => $stock->stock === StockState::LOW || $stock->stock === StockState::CRITICAL,
|
||||
);
|
||||
|
||||
return $this->render('index.html.twig', [
|
||||
'drinkStocks' => $drinkStocks,
|
||||
'low' => $low
|
||||
'low' => $low,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\DrinkType;
|
||||
use App\Entity\InventoryRecord;
|
||||
use App\Form\InventoryRecordForm;
|
||||
use App\Repository\InventoryRecordRepository;
|
||||
use App\Service\InventoryService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
@ -22,10 +26,11 @@ final class InventoryRecordController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
#[Route('/new', name: 'app_inventory_record_new', methods: ['GET', 'POST'])]
|
||||
public function new(Request $request, EntityManagerInterface $entityManager): Response
|
||||
#[Route('/new/{drinkType}', name: 'app_inventory_record_new', methods: ['GET', 'POST'])]
|
||||
public function new(Request $request, EntityManagerInterface $entityManager, DrinkType $drinkType): Response
|
||||
{
|
||||
$inventoryRecord = new InventoryRecord();
|
||||
$inventoryRecord->setDrinkType($drinkType);
|
||||
$form = $this->createForm(InventoryRecordForm::class, $inventoryRecord);
|
||||
$form->handleRequest($request);
|
||||
|
||||
|
@ -33,7 +38,22 @@ final class InventoryRecordController extends AbstractController
|
|||
$entityManager->persist($inventoryRecord);
|
||||
$entityManager->flush();
|
||||
|
||||
return $this->redirectToRoute('app_inventory_record_index', [], Response::HTTP_SEE_OTHER);
|
||||
// If it's an HTMX request, return a redirect that HTMX will follow
|
||||
if ($request->headers->has('HX-Request')) {
|
||||
$response = new Response('', Response::HTTP_SEE_OTHER);
|
||||
$response->headers->set('HX-Redirect', $this->generateUrl('app_index'));
|
||||
return $response;
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('app_index', [], Response::HTTP_SEE_OTHER);
|
||||
}
|
||||
|
||||
// Check if it's an HTMX request
|
||||
if ($request->headers->has('HX-Request')) {
|
||||
return $this->render('inventory_record/_modal_form.html.twig', [
|
||||
'inventory_record' => $inventoryRecord,
|
||||
'form' => $form,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->render('inventory_record/new.html.twig', [
|
||||
|
@ -42,6 +62,26 @@ final class InventoryRecordController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
#[Route('/modal/{drinkType}', name: 'app_inventory_record_modal', methods: ['GET'])]
|
||||
public function modal(DrinkType $drinkType, InventoryService $inventoryService): Response
|
||||
{
|
||||
$inventoryRecord = new InventoryRecord();
|
||||
$inventoryRecord->setDrinkType($drinkType);
|
||||
$inventoryRecord->setQuantity(
|
||||
$inventoryService->getLatestInventoryRecord($drinkType)->getQuantity() ?? 0
|
||||
);
|
||||
$form = $this->createForm(InventoryRecordForm::class, $inventoryRecord, [
|
||||
'action' => $this->generateUrl('app_inventory_record_new', [
|
||||
'drinkType' => $drinkType->getId(),
|
||||
]),
|
||||
]);
|
||||
|
||||
return $this->render('inventory_record/_modal_form.html.twig', [
|
||||
'inventory_record' => $inventoryRecord,
|
||||
'form' => $form,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/{id}', name: 'app_inventory_record_show', methods: ['GET'])]
|
||||
public function show(InventoryRecord $inventoryRecord): Response
|
||||
{
|
||||
|
@ -71,7 +111,7 @@ final class InventoryRecordController extends AbstractController
|
|||
#[Route('/{id}', name: 'app_inventory_record_delete', methods: ['POST'])]
|
||||
public function delete(Request $request, InventoryRecord $inventoryRecord, EntityManagerInterface $entityManager): Response
|
||||
{
|
||||
if ($this->isCsrfTokenValid('delete'.$inventoryRecord->getId(), $request->getPayload()->getString('_token'))) {
|
||||
if ($this->isCsrfTokenValid('delete' . $inventoryRecord->getId(), $request->getPayload()->getString('_token'))) {
|
||||
$entityManager->remove($inventoryRecord);
|
||||
$entityManager->flush();
|
||||
}
|
||||
|
|
83
src/Controller/OrderController.php
Normal file
83
src/Controller/OrderController.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Order;
|
||||
use App\Form\OrderForm;
|
||||
use App\Repository\OrderRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
#[Route('/order')]
|
||||
final class OrderController extends AbstractController
|
||||
{
|
||||
#[Route(name: 'app_order_index', methods: ['GET'])]
|
||||
public function index(OrderRepository $orderRepository): Response
|
||||
{
|
||||
return $this->render('order/index.html.twig', [
|
||||
'orders' => $orderRepository->findAll(),
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/new', name: 'app_order_new', methods: ['GET', 'POST'])]
|
||||
public function new(Request $request, EntityManagerInterface $entityManager): Response
|
||||
{
|
||||
$order = new Order();
|
||||
$form = $this->createForm(OrderForm::class, $order);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$entityManager->persist($order);
|
||||
$entityManager->flush();
|
||||
|
||||
return $this->redirectToRoute('app_order_index', [], Response::HTTP_SEE_OTHER);
|
||||
}
|
||||
|
||||
return $this->render('order/new.html.twig', [
|
||||
'order' => $order,
|
||||
'form' => $form,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/{id}', name: 'app_order_show', methods: ['GET'])]
|
||||
public function show(Order $order): Response
|
||||
{
|
||||
return $this->render('order/show.html.twig', [
|
||||
'order' => $order,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/{id}/edit', name: 'app_order_edit', methods: ['GET', 'POST'])]
|
||||
public function edit(Request $request, Order $order, EntityManagerInterface $entityManager): Response
|
||||
{
|
||||
$form = $this->createForm(OrderForm::class, $order);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$entityManager->flush();
|
||||
|
||||
return $this->redirectToRoute('app_order_index', [], Response::HTTP_SEE_OTHER);
|
||||
}
|
||||
|
||||
return $this->render('order/edit.html.twig', [
|
||||
'order' => $order,
|
||||
'form' => $form,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/{id}', name: 'app_order_delete', methods: ['POST'])]
|
||||
public function delete(Request $request, Order $order, EntityManagerInterface $entityManager): Response
|
||||
{
|
||||
if ($this->isCsrfTokenValid('delete' . $order->getId(), $request->getPayload()->getString('_token'))) {
|
||||
$entityManager->remove($order);
|
||||
$entityManager->flush();
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('app_order_index', [], Response::HTTP_SEE_OTHER);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ use DateTimeImmutable;
|
|||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use App\Entity\PropertyChangeLog;
|
||||
|
||||
#[ORM\Entity(repositoryClass: DrinkTypeRepository::class)]
|
||||
#[ORM\Table(name: 'drink_type')]
|
||||
|
|
|
@ -21,7 +21,7 @@ class Order
|
|||
private null|int $id = null;
|
||||
|
||||
#[ORM\Column(type: 'datetime_immutable')]
|
||||
private DateTimeImmutable $createdAt;
|
||||
private DateTimeImmutable $createdAt;
|
||||
|
||||
#[ORM\Column(type: 'datetime_immutable')]
|
||||
private DateTimeImmutable $updatedAt;
|
||||
|
|
|
@ -32,7 +32,7 @@ class OrderItem
|
|||
|
||||
#[ORM\ManyToOne(targetEntity: Order::class, inversedBy: 'orderItems')]
|
||||
#[ORM\JoinColumn(name: 'order_id', referencedColumnName: 'id', nullable: false)]
|
||||
private null|Order $order;
|
||||
private null|Order $order = null;
|
||||
|
||||
public function __construct(
|
||||
) {
|
||||
|
@ -53,7 +53,7 @@ class OrderItem
|
|||
public function setOrder(null|Order $order): self
|
||||
{
|
||||
// Remove from old order if exists
|
||||
if (isset($this->order) && $this->order instanceof Order && $this->order !== $order) {
|
||||
if ($this->order instanceof Order && $this->order instanceof Order && $this->order !== $order) {
|
||||
$this->order->removeOrderItem($this);
|
||||
}
|
||||
|
||||
|
|
92
src/Entity/PropertyChangeLog.php
Normal file
92
src/Entity/PropertyChangeLog.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\PropertyChangeLogRepository;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\GeneratedValue;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: PropertyChangeLogRepository::class)]
|
||||
#[ORM\Table(name: 'property_change_log')]
|
||||
final class PropertyChangeLog
|
||||
{
|
||||
#[Id]
|
||||
#[GeneratedValue]
|
||||
#[Column(type: 'integer')]
|
||||
private int|null $id = null;
|
||||
#[Column(type: 'string', length: 255)]
|
||||
private string $propertyName;
|
||||
#[Column(type: 'string', length: 255)]
|
||||
private string $entityClass;
|
||||
#[Column(type: 'integer', nullable: true)]
|
||||
private ?int $entityId = null;
|
||||
#[Column(type: 'string', length: 255)]
|
||||
private string $newValue;
|
||||
#[Column(type: 'datetime_immutable')]
|
||||
private DateTimeImmutable $changeDate;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->changeDate = new DateTimeImmutable();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getPropertyName(): string
|
||||
{
|
||||
return $this->propertyName;
|
||||
}
|
||||
|
||||
public function setPropertyName(string $propertyName): void
|
||||
{
|
||||
$this->propertyName = $propertyName;
|
||||
}
|
||||
|
||||
public function getEntityClass(): string
|
||||
{
|
||||
return $this->entityClass;
|
||||
}
|
||||
|
||||
public function setEntityClass(string $entityClass): void
|
||||
{
|
||||
$this->entityClass = $entityClass;
|
||||
}
|
||||
|
||||
public function getEntityId(): ?int
|
||||
{
|
||||
return $this->entityId;
|
||||
}
|
||||
|
||||
public function setEntityId(?int $entityId): void
|
||||
{
|
||||
$this->entityId = $entityId;
|
||||
}
|
||||
|
||||
public function getNewValue(): string
|
||||
{
|
||||
return $this->newValue;
|
||||
}
|
||||
|
||||
public function setNewValue(string $newValue): void
|
||||
{
|
||||
$this->newValue = $newValue;
|
||||
}
|
||||
|
||||
public function getChangeDate(): DateTimeImmutable
|
||||
{
|
||||
return $this->changeDate;
|
||||
}
|
||||
|
||||
public function setChangeDate(DateTimeImmutable $changeDate): void
|
||||
{
|
||||
$this->changeDate = $changeDate;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ namespace App\Form;
|
|||
|
||||
use App\Entity\DrinkType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
|
@ -13,7 +14,7 @@ class DrinkTypeForm extends AbstractType
|
|||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add('name')->add('description')->add('desiredStock');
|
||||
$builder->add('name')->add('description')->add('desiredStock', NumberType::class);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\DrinkType;
|
||||
use App\Entity\InventoryRecord;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
|
@ -14,19 +17,14 @@ class InventoryRecordForm extends AbstractType
|
|||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('quantity')
|
||||
->add('timestamp', null, [
|
||||
'widget' => 'single_text',
|
||||
])
|
||||
->add('createdAt', null, [
|
||||
'widget' => 'single_text',
|
||||
])
|
||||
->add('updatedAt', null, [
|
||||
'widget' => 'single_text',
|
||||
])
|
||||
->add('quantity', NumberType::class)
|
||||
->add('drinkType', EntityType::class, [
|
||||
'class' => DrinkType::class,
|
||||
'choice_label' => 'id',
|
||||
'attr' => [
|
||||
'style' => 'display: none;',
|
||||
],
|
||||
'label' => false,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
|
27
src/Form/OrderForm.php
Normal file
27
src/Form/OrderForm.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\Order;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class OrderForm extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('status')
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => Order::class,
|
||||
]);
|
||||
}
|
||||
}
|
19
src/Repository/PropertyChangeLogRepository.php
Normal file
19
src/Repository/PropertyChangeLogRepository.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\PropertyChangeLog;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractRepository<PropertyChangeLog>
|
||||
*/
|
||||
final class PropertyChangeLogRepository extends AbstractRepository
|
||||
{
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
parent::__construct($entityManager, $entityManager->getClassMetadata(PropertyChangeLog::class));
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue