src/EventSubscriber/Finance/Quotation/QuotationDraftsGenerator.php line 46

  1. <?php
  2. namespace App\EventSubscriber\Finance\Quotation;
  3. use App\Doctrine\Type\Expediting\ShipmentType;
  4. use App\Doctrine\Type\Finance\PayableStatus;
  5. use App\Doctrine\Type\Scheduler\TaskStatus;
  6. use App\Entity\Expediting\ShippingPlan;
  7. use App\Entity\Expediting\ShippingSegment;
  8. use App\Entity\Finance\Quotation;
  9. use App\Entity\Sales\WorkOrder;
  10. use App\Event\Expediting\FreightDatasheetImportEvent;
  11. use App\Event\Expediting\ShippingPlanCreationEvent;
  12. use App\Event\Finance\QuotationCreationEvent;
  13. use App\Event\Sales\WorkOrderCancelationEvent;
  14. use App\Event\Sales\WorkOrderReopeningEvent;
  15. use App\EventSubscriber\TasksCancelerTrait;
  16. use App\Repository\Common\CompanyRepository;
  17. use App\Repository\Scheduler\TaskRepository;
  18. use Doctrine\Common\Collections\Collection;
  19. use Doctrine\ORM\EntityManagerInterface;
  20. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  21. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  22. readonly class QuotationDraftsGenerator implements EventSubscriberInterface
  23. {
  24.     use TasksCancelerTrait;
  25.     public function __construct(
  26.         private TaskRepository           $taskRepository,
  27.         private CompanyRepository        $companyRepository,
  28.         private EntityManagerInterface   $entityManager,
  29.         private EventDispatcherInterface $eventDispatcher,
  30.     ) {}
  31.     public static function getSubscribedEvents(): array
  32.     {
  33.         return [
  34.             ShippingPlanCreationEvent::IDENTIFIER => ['handleShippingPlan'0],
  35.             FreightDatasheetImportEvent::IDENTIFIER => ['handleDatasheetImport'0],
  36.             WorkOrderCancelationEvent::IDENTIFIER => ['handleCancellation', -100],
  37.             WorkOrderReopeningEvent::IDENTIFIER => ['handleReopening'0],
  38.         ];
  39.     }
  40.     public function handleShippingPlan(ShippingPlanCreationEvent $event): void
  41.     {
  42.         $shippingPlan $event->getShippingPlan();
  43.         $workOrder $shippingPlan->getWorkOrder();
  44.         if (!$workOrder instanceof WorkOrder) {
  45.             return;
  46.         }
  47.         $freightDatasheetImportTask $this->taskRepository->getFreightDatasheetImportTask($workOrder);
  48.         if (TaskStatus::COMPLETED === $freightDatasheetImportTask->getStatus()) {
  49.             $this->process($workOrder$shippingPlan);
  50.         }
  51.     }
  52.     public function handleDatasheetImport(FreightDatasheetImportEvent $event): void
  53.     {
  54.         /** @var WorkOrder $workOrder */
  55.         $workOrder $this->entityManager->getRepository(WorkOrder::class)->findOneBy([
  56.             'reference' => $event->getDatasheet()->workOrderReference,
  57.         ]);
  58.         $shipmentPlanningTask $this->taskRepository->getShipmentPlanningTask($workOrder);
  59.         if (TaskStatus::COMPLETED === $shipmentPlanningTask->getStatus()) {
  60.             foreach ($workOrder->getShippingPlans() as $shippingPlan) {
  61.                 $this->process($workOrder$shippingPlan);
  62.             }
  63.         }
  64.     }
  65.     public function handleCancellation(WorkOrderCancelationEvent $event): void
  66.     {
  67.         /** @var Collection<int, Quotation> $quotations */
  68.         $quotations $this->entityManager->getRepository(Quotation::class)->findBy([
  69.             'shippingPlan' => $event->getWorkOrder()->getShippingPlans()->first(),
  70.         ]);
  71.         $pendingTasks = [];
  72.         foreach ($quotations as $quotation) {
  73.             if (PayableStatus::DRAFTED !== $quotation->getStatus()) {
  74.                 continue;
  75.             }
  76.             $pendingTasks []= $this->taskRepository->getPickupQuotationRequestTask($quotation);
  77.             $pendingTasks []= $this->taskRepository->getPickupQuotationFillingTask($quotation);
  78.         }
  79.         $this->cancelTasks($pendingTasks);
  80.         $this->entityManager->flush();
  81.     }
  82.     public function handleReopening(WorkOrderReopeningEvent $event): void
  83.     {
  84.         /** @var Collection<int, Quotation> $quotations */
  85.         $quotations $this->entityManager->getRepository(Quotation::class)->findBy([
  86.             'shippingPlan' => $event->getWorkOrder()->getShippingPlans()->first(),
  87.         ]);
  88.         foreach ($quotations as $quotation) {
  89.             if (PayableStatus::DRAFTED !== $quotation->getStatus()) {
  90.                 continue;
  91.             }
  92.             $pickupQuotationRequestTask $this->taskRepository->getPickupQuotationRequestTask($quotationcreateIfNulltrue);
  93.             $pickupQuotationFillingTask $this->taskRepository->getPickupQuotationFillingTask($quotationcreateIfNulltrue);
  94.             $this->entityManager->persist($pickupQuotationRequestTask);
  95.             $this->entityManager->persist($pickupQuotationFillingTask);
  96.         }
  97.         $this->entityManager->flush();
  98.     }
  99.     private function process(WorkOrder $workOrderShippingPlan $shippingPlan): void
  100.     {
  101.         $types = [];
  102.         $firstSegment $shippingPlan->getShippingSegments()->first();
  103.         foreach ($shippingPlan->getShippingSegments() as $segment) {
  104.             $types array_merge($types$segment->getTypes());
  105.         }
  106.         // No quotation on work orders that consists of an hub reception if goods are expected to be part of a consolidation
  107.         $isConsolidation in_array(ShipmentType::AIR_CONSOLIDATION$types) || in_array(ShipmentType::SEA_CONSOLIDATION$types);
  108.         if ($isConsolidation && in_array(ShipmentType::HUB_RECEPTION$firstSegment->getTypes())) {
  109.             return;
  110.         }
  111.         if ($isConsolidation) {
  112.             // For pickups that are expected to be consolidated, only seek quotation for pickup
  113.             $this->createDraft($workOrder$shippingPlan, [$shippingPlan->getShippingSegments()->first()]);
  114.         } else {
  115.             // For direct shipments, seek quotation for the whole shipping plan
  116.             $this->createDraft($workOrder$shippingPlan$shippingPlan->getShippingSegments()->toArray());
  117.         }
  118.     }
  119.     /**
  120.      * @param WorkOrder $workOrder
  121.      * @param ShippingPlan $shippingPlan
  122.      * @param ShippingSegment[] $segments
  123.      * @return void
  124.      */
  125.     private function createDraft(WorkOrder $workOrderShippingPlan $shippingPlan, array $segments): void
  126.     {
  127.         $pickUpCountryCode $segments[0]->getPlaceOfLoading()->getCountryCode();
  128.         $transportAgencies $this->companyRepository->findTransportersByCountryCode($pickUpCountryCode);
  129.         $events = [];
  130.         foreach ($transportAgencies as $agency) {
  131.             $quotation $this->entityManager->getRepository(Quotation::class)->findOneBy([
  132.                 'shippingPlan' => $shippingPlan,
  133.                 'payableTo' => $agency,
  134.             ]);
  135.             if ($quotation instanceof Quotation) {
  136.                 continue;
  137.             }
  138.             $quotation = (new Quotation())
  139.                 ->setShippingPlan($shippingPlan)
  140.                 ->setPayableBy($workOrder->getContract()->getTenant())
  141.                 ->setCreatedBy($shippingPlan->getCreatedBy())
  142.                 ->setPayableTo($agency);
  143.             $this->entityManager->persist($quotation);
  144.             $this->entityManager->flush();
  145.             $events []= new QuotationCreationEvent($quotation);
  146.         }
  147.         // Only dispatch events after flushing
  148.         foreach ($events as $event) {
  149.             $this->eventDispatcher->dispatch($event$event::IDENTIFIER);
  150.         }
  151.     }
  152. }