<?php
namespace App\Controller;
use App\Entity\PurchaseOrder;
use App\Entity\SalesCase;
use App\Entity\Supplier;
use App\Exception\AccessDeniedException;
use App\Exception\CurrencyException;
use App\Form\Type\DocumentNotesType;
use App\Form\Type\LogEntryType;
use App\Form\Type\PurchaseOrderType;
use App\Repository\ProductConfigurationItemRepository;
use App\Service\ProductService;
use App\Service\PurchaseOrderService;
use Exception;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Profiler\Profiler;
use Symfony\Component\Routing\Annotation\Route;
class PurchaseOrderController extends AbstractController
{
/**
* @var ProductConfigurationItemRepository
*/
protected ProductConfigurationItemRepository $productConfigurationItemRepository;
/**
* @var ProductService
*/
protected ProductService $productService;
/**
* @var PurchaseOrderService
*/
protected PurchaseOrderService $purchaseOrderService;
/**
* @param PurchaseOrderService $purchaseOrderService
* @param ProductService $productService
* @param ProductConfigurationItemRepository $productConfigurationItemRepository
*/
public function __construct(
PurchaseOrderService $purchaseOrderService,
ProductService $productService,
ProductConfigurationItemRepository $productConfigurationItemRepository
) {
$this->purchaseOrderService = $purchaseOrderService;
$this->productService = $productService;
$this->productConfigurationItemRepository = $productConfigurationItemRepository;
}
/**
* @Route(
* "/cases/{salesCase}/purchase-orders/create/{supplier}",
* name="app.purchase_order.create",
* requirements={"salesCase" = "\d+", "supplier" = "\d+"}
* )
*
* @param SalesCase $salesCase
* @param Supplier $supplier
*
* @return RedirectResponse
*/
public function createAction(SalesCase $salesCase, Supplier $supplier): RedirectResponse
{
try {
$salesItems = [];
if (null !== $orderConfirmation = $salesCase->getOrderConfirmation()) {
$orderConfirmationSalesItems = $orderConfirmation->getSalesItems();
foreach ($orderConfirmationSalesItems as $item) {
if ($item->getSupplier() === $supplier) {
if ($item->isPciSubItem()) {
$item = clone $item;
$pciPosition = $item->getPciPosition();
foreach ($orderConfirmationSalesItems as $itemTemp) {
if ($itemTemp->getPosition() == $pciPosition) {
$item->setQuantity($itemTemp->getQuantity() * $item->getQuantity());
}
}
}
$salesItems[] = $item;
}
}
}
$purchaseOrder = $this->purchaseOrderService->getNew(
$salesCase,
$supplier,
$salesItems
);
$this->purchaseOrderService->assertInventoryBalanceNotExceeded($purchaseOrder);
$this->purchaseOrderService->create($purchaseOrder);
$this->flashSuccess('entity.success.create', [], 'PurchaseOrder');
if (null === $purchaseOrder->getInvoicingAddress()) {
$this->flashWarning('entity.error.no_inventory_address', [], 'PurchaseOrder');
}
return $this->redirectResponse(
'app.purchase_order.update',
[
'purchaseOrder' => $purchaseOrder->getId(),
]
);
} catch (Exception $e) {
$this->flashError('entity.error.create', [], 'PurchaseOrder');
if ($this->getCurrentUser()->isSuperAdmin()) {
$this->flashError($e->getMessage());
}
}
return $this->redirectResponse(
'app.sales_case.order_confirmation.update',
[
'salesCase' => $salesCase->getId(),
]
);
}
/**
* @Route(
* "/cases/{salesCase}/purchase-orders/create-all",
* name="app.purchase_order.create_all",
* requirements={"salesCase" = "\d+"}
* )
*
* @param SalesCase $salesCase
*
* @return RedirectResponse
*/
public function createAllAction(SalesCase $salesCase): RedirectResponse
{
try {
$purchaseOrders = $this->purchaseOrderService->getNewAll($salesCase);
foreach ($purchaseOrders as $purchaseOrder) {
$this->purchaseOrderService->assertInventoryBalanceNotExceeded($purchaseOrder);
$this->purchaseOrderService->create($purchaseOrder);
}
$this->flashSuccess(
'entity.success.create_all',
[
'%list%' => implode(
', ',
array_map(
function (PurchaseOrder $purchaseOrder) {
return $purchaseOrder->getNumber();
},
$purchaseOrders
)
),
],
'PurchaseOrder'
);
} catch (Exception $e) {
$this->flashError('entity.error.create_all', [], 'PurchaseOrder');
if ($this->getCurrentUser()->isSuperAdmin()) {
$this->flashError($e->getMessage());
}
}
return $this->redirectResponse(
'app.sales_case.order_confirmation.update',
[
'salesCase' => $salesCase->getId(),
]
);
}
/**
* @Route(
* "/purchase-orders/{purchaseOrder}/delete",
* name="app.purchase_order.delete",
* requirements={"purchaseOrder" = "\d+"}
* )
*
* @param PurchaseOrder $purchaseOrder
*
* @return RedirectResponse
*
* @throws AccessDeniedException
*/
public function deleteAction(PurchaseOrder $purchaseOrder): RedirectResponse
{
$this->purchaseOrderService->assertDelete($purchaseOrder);
try {
$this->purchaseOrderService->delete($purchaseOrder);
$this->flashSuccess('entity.success.delete', [], 'PurchaseOrder');
} catch (Exception $e) {
$this->flashError('entity.error.delete', [], 'PurchaseOrder');
if ($this->getCurrentUser()->isSuperAdmin()) {
$this->flashError($e->getMessage());
}
}
return $this->redirectResponse(
'app.sales_case.order_confirmation.update',
[
'salesCase' => $purchaseOrder->getSalesCase()->getId(),
]
);
}
/**
* @Route(
* "/purchase-orders/{purchaseOrder}/pdf",
* name="app.purchase_order.pdf",
* requirements={"purchaseOrder" = "\d+"}
* )
*
* @param PurchaseOrder $purchaseOrder
* @param Profiler|null $profiler
*
* @return Response
*/
public function getPdf(PurchaseOrder $purchaseOrder, ?Profiler $profiler): Response
{
$profiler?->disable();
try {
return new Response(
$this->purchaseOrderService->generatePdf($purchaseOrder),
200,
[
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="' . $purchaseOrder->getPreviewFileName() . '"',
]
);
} catch (Exception $e) {
$this->flashError('Failed to generate the purchase order PDF.');
$this->flashError($e->getMessage());
}
return $this->redirectResponse(
'app.purchase_order.update',
[
'purchaseOrder' => $purchaseOrder->getId(),
]
);
}
/**
* @Route(
* "/price-updates",
* name="app.purchase_order.price_updates"
* )
*
* @return Response
*/
public function priceUpdatesAction(): Response
{
return $this->render(
'Product/priceUpdates.html.twig',
[
'priceUpdates' => $this->purchaseOrderService->getPriceUpdates(),
]
);
}
/**
* @Route(
* "/purchase-orders/{purchaseOrder}/versions/create",
* name="app.rest.purchase_order.version.create",
* requirements={"purchaseOrder" = "\d+"}
* )
*
* @param PurchaseOrder $purchaseOrder
*
* @return JsonResponse
*/
public function restCreateDocumentVersionAction(PurchaseOrder $purchaseOrder): JsonResponse
{
$data = [];
try {
$this->purchaseOrderService->assertUpdate($purchaseOrder);
$documentVersion = $this->purchaseOrderService->generateVersion($purchaseOrder);
$data['row'] = $this->renderView(
'Document/versionRow.html.twig',
[
'version' => $documentVersion,
]
);
} catch (Exception $e) {
$data['error'] = 'Failed to create the document version.';
}
return new JsonResponse($data);
}
/**
* @Route(
* "/purchase-orders/{purchaseOrder}/notes/update",
* name="app.rest.purchase_order.notes.update",
* requirements={"purchaseOrder" = "\d+"}
* )
*
* @param Request $request
* @param PurchaseOrder $purchaseOrder
*
* @return JsonResponse
*/
public function restUpdateNotesAction(Request $request, PurchaseOrder $purchaseOrder): JsonResponse
{
$data = [
'success' => true,
'notes' => null,
];
try {
$this->purchaseOrderService->assertUpdate($purchaseOrder);
$notesForm = $this->createForm(
DocumentNotesType::class,
$purchaseOrder,
[
'action' => $this->generateUrl(
'app.rest.purchase_order.notes.update',
[
'purchaseOrder' => $purchaseOrder->getId(),
]
),
]
);
$notesForm->handleRequest($request);
if ($notesForm->isSubmitted()) {
if ($notesForm->isValid()) {
$this->purchaseOrderService->update($purchaseOrder);
$data['notes'] = $purchaseOrder->getNotes();
}
}
} catch (Exception $e) {
$data['success'] = false;
$data['error'] = 'Failed to update the purchase order notes.';
}
return new JsonResponse($data);
}
/**
* @Route(
* "/purchase-orders/{purchaseOrder}/status/update",
* name="app.rest.purchase_order.status.update",
* requirements={"purchaseOrder" = "\d+"}
* )
*
* @param Request $request
* @param PurchaseOrder $purchaseOrder
*
* @return JsonResponse
*/
public function restUpdateStatusAction(Request $request, PurchaseOrder $purchaseOrder): JsonResponse
{
$data = [
'success' => true,
];
try {
$this->purchaseOrderService->assertUpdate($purchaseOrder);
$status = $request->query->get('status');
$this->purchaseOrderService->updateStatus($purchaseOrder, $status);
$data['status'] = $purchaseOrder->getStatus();
$data['number'] = $purchaseOrder->getNumber();
$data['dropdown'] = $this->renderView(
'Document/documentDropdown.html.twig',
[
'salesCase' => $purchaseOrder->getSalesCase(),
]
);
$data['versions'] = $this->renderView(
'Document/documentVersions.html.twig',
[
'versions' => $purchaseOrder->getDocumentVersions(),
'url' => $this->generateUrl('app.purchase_order.pdf', ['purchaseOrder' => $purchaseOrder->getId()]),
'draft' => $purchaseOrder->isDraft(),
]
);
$data['versionsModal'] = $this->renderView(
'Document/documentVersionsModal.html.twig',
[
'versions' => $purchaseOrder->getDocumentVersions(),
'url' => $this->generateUrl('app.rest.purchase_order.version.create', ['purchaseOrder' => $purchaseOrder->getId()]),
]
);
} catch (Exception $e) {
$data['success'] = false;
$data['error'] = 'Failed to update the purchase order status.';
}
return new JsonResponse($data);
}
/**
* @Route(
* "/purchase-orders/{purchaseOrder}/update",
* name="app.purchase_order.update",
* requirements={"purchaseOrder" = "\d+"}
* )
*
* @param Request $request
* @param PurchaseOrder $purchaseOrder
*
* @return Response
*
* @throws AccessDeniedException
* @throws CurrencyException
*/
public function updateAction(Request $request, PurchaseOrder $purchaseOrder): Response
{
$this->purchaseOrderService->assertRead($purchaseOrder);
$this->productService->populateLimitedAvailability($purchaseOrder);
$form = $this->createForm(
PurchaseOrderType::class,
$purchaseOrder,
[
'action' => $this->generateUrl(
'app.purchase_order.update',
[
'purchaseOrder' => $purchaseOrder->getId()
]
),
'disabled' => ! $this->purchaseOrderService->canUpdate($purchaseOrder)
]
);
$notesForm = $this->createForm(
DocumentNotesType::class,
$purchaseOrder,
[
'action' => $this->generateUrl(
'app.rest.purchase_order.notes.update',
[
'purchaseOrder' => $purchaseOrder->getId(),
]
),
]
);
$logEntryFormView = null;
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
try {
$this->purchaseOrderService->assertInventoryBalanceNotExceeded($purchaseOrder);
$this->purchaseOrderService->update($purchaseOrder);
$logEntry = $purchaseOrder->getLastLogEntry();
$logEntryForm = $this->createForm(
LogEntryType::class,
$logEntry,
[
'action' => $this->generateUrl(
'app.rest.log_entry.update',
[
'logEntry' => $logEntry->getId(),
]
),
]
);
$logEntryFormView = $logEntryForm->createView();
$this->flashSuccess('entity.success.update', [], 'PurchaseOrder');
} catch (Exception $e) {
$this->flashError('entity.error.update', [], 'PurchaseOrder');
if ($this->getCurrentUser()->isSuperAdmin()) {
$this->flashError($e->getMessage());
}
}
} else {
$this->flashError('entity.error.validate', [], 'PurchaseOrder');
}
}
return $this->render(
'PurchaseOrder/update.html.twig',
[
'salesCase' => $purchaseOrder->getSalesCase(),
'purchaseOrder' => $purchaseOrder,
'form' => $form->createView(),
'logEntryForm' => $logEntryFormView,
'notesForm' => $notesForm->createView(),
'salesItemUpdates' => $this->purchaseOrderService->getPriceUpdatesForPurchaseOrder($purchaseOrder),
'productConfigurationItems' => $this->productConfigurationItemRepository->findAll(),
]
);
}
}