src/EventSubscriber/Finance/Quotation/QuotationDraftsGenerator.php line 46
<?php
namespace App\EventSubscriber\Finance\Quotation;
use App\Doctrine\Type\Expediting\ShipmentType;
use App\Doctrine\Type\Finance\PayableStatus;
use App\Doctrine\Type\Scheduler\TaskStatus;
use App\Entity\Expediting\ShippingPlan;
use App\Entity\Expediting\ShippingSegment;
use App\Entity\Finance\Quotation;
use App\Entity\Sales\WorkOrder;
use App\Event\Expediting\FreightDatasheetImportEvent;
use App\Event\Expediting\ShippingPlanCreationEvent;
use App\Event\Finance\QuotationCreationEvent;
use App\Event\Sales\WorkOrderCancelationEvent;
use App\Event\Sales\WorkOrderReopeningEvent;
use App\EventSubscriber\TasksCancelerTrait;
use App\Repository\Common\CompanyRepository;
use App\Repository\Scheduler\TaskRepository;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
readonly class QuotationDraftsGenerator implements EventSubscriberInterface
{
use TasksCancelerTrait;
public function __construct(
private TaskRepository $taskRepository,
private CompanyRepository $companyRepository,
private EntityManagerInterface $entityManager,
private EventDispatcherInterface $eventDispatcher,
) {}
public static function getSubscribedEvents(): array
{
return [
ShippingPlanCreationEvent::IDENTIFIER => ['handleShippingPlan', 0],
FreightDatasheetImportEvent::IDENTIFIER => ['handleDatasheetImport', 0],
WorkOrderCancelationEvent::IDENTIFIER => ['handleCancellation', -100],
WorkOrderReopeningEvent::IDENTIFIER => ['handleReopening', 0],
];
}
public function handleShippingPlan(ShippingPlanCreationEvent $event): void
{
$shippingPlan = $event->getShippingPlan();
$workOrder = $shippingPlan->getWorkOrder();
if (!$workOrder instanceof WorkOrder) {
return;
}
$freightDatasheetImportTask = $this->taskRepository->getFreightDatasheetImportTask($workOrder);
if (TaskStatus::COMPLETED === $freightDatasheetImportTask->getStatus()) {
$this->process($workOrder, $shippingPlan);
}
}
public function handleDatasheetImport(FreightDatasheetImportEvent $event): void
{
/** @var WorkOrder $workOrder */
$workOrder = $this->entityManager->getRepository(WorkOrder::class)->findOneBy([
'reference' => $event->getDatasheet()->workOrderReference,
]);
$shipmentPlanningTask = $this->taskRepository->getShipmentPlanningTask($workOrder);
if (TaskStatus::COMPLETED === $shipmentPlanningTask->getStatus()) {
foreach ($workOrder->getShippingPlans() as $shippingPlan) {
$this->process($workOrder, $shippingPlan);
}
}
}
public function handleCancellation(WorkOrderCancelationEvent $event): void
{
/** @var Collection<int, Quotation> $quotations */
$quotations = $this->entityManager->getRepository(Quotation::class)->findBy([
'shippingPlan' => $event->getWorkOrder()->getShippingPlans()->first(),
]);
$pendingTasks = [];
foreach ($quotations as $quotation) {
if (PayableStatus::DRAFTED !== $quotation->getStatus()) {
continue;
}
$pendingTasks []= $this->taskRepository->getPickupQuotationRequestTask($quotation);
$pendingTasks []= $this->taskRepository->getPickupQuotationFillingTask($quotation);
}
$this->cancelTasks($pendingTasks);
$this->entityManager->flush();
}
public function handleReopening(WorkOrderReopeningEvent $event): void
{
/** @var Collection<int, Quotation> $quotations */
$quotations = $this->entityManager->getRepository(Quotation::class)->findBy([
'shippingPlan' => $event->getWorkOrder()->getShippingPlans()->first(),
]);
foreach ($quotations as $quotation) {
if (PayableStatus::DRAFTED !== $quotation->getStatus()) {
continue;
}
$pickupQuotationRequestTask = $this->taskRepository->getPickupQuotationRequestTask($quotation, createIfNull: true);
$pickupQuotationFillingTask = $this->taskRepository->getPickupQuotationFillingTask($quotation, createIfNull: true);
$this->entityManager->persist($pickupQuotationRequestTask);
$this->entityManager->persist($pickupQuotationFillingTask);
}
$this->entityManager->flush();
}
private function process(WorkOrder $workOrder, ShippingPlan $shippingPlan): void
{
$types = [];
$firstSegment = $shippingPlan->getShippingSegments()->first();
foreach ($shippingPlan->getShippingSegments() as $segment) {
$types = array_merge($types, $segment->getTypes());
}
// No quotation on work orders that consists of an hub reception if goods are expected to be part of a consolidation
$isConsolidation = in_array(ShipmentType::AIR_CONSOLIDATION, $types) || in_array(ShipmentType::SEA_CONSOLIDATION, $types);
if ($isConsolidation && in_array(ShipmentType::HUB_RECEPTION, $firstSegment->getTypes())) {
return;
}
if ($isConsolidation) {
// For pickups that are expected to be consolidated, only seek quotation for pickup
$this->createDraft($workOrder, $shippingPlan, [$shippingPlan->getShippingSegments()->first()]);
} else {
// For direct shipments, seek quotation for the whole shipping plan
$this->createDraft($workOrder, $shippingPlan, $shippingPlan->getShippingSegments()->toArray());
}
}
/**
* @param WorkOrder $workOrder
* @param ShippingPlan $shippingPlan
* @param ShippingSegment[] $segments
* @return void
*/
private function createDraft(WorkOrder $workOrder, ShippingPlan $shippingPlan, array $segments): void
{
$pickUpCountryCode = $segments[0]->getPlaceOfLoading()->getCountryCode();
$transportAgencies = $this->companyRepository->findTransportersByCountryCode($pickUpCountryCode);
$events = [];
foreach ($transportAgencies as $agency) {
$quotation = $this->entityManager->getRepository(Quotation::class)->findOneBy([
'shippingPlan' => $shippingPlan,
'payableTo' => $agency,
]);
if ($quotation instanceof Quotation) {
continue;
}
$quotation = (new Quotation())
->setShippingPlan($shippingPlan)
->setPayableBy($workOrder->getContract()->getTenant())
->setCreatedBy($shippingPlan->getCreatedBy())
->setPayableTo($agency);
$this->entityManager->persist($quotation);
$this->entityManager->flush();
$events []= new QuotationCreationEvent($quotation);
}
// Only dispatch events after flushing
foreach ($events as $event) {
$this->eventDispatcher->dispatch($event, $event::IDENTIFIER);
}
}
}