src/Entity/Expediting/Shipment.php line 178

  1. <?php
  2. namespace App\Entity\Expediting;
  3. use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
  4. use ApiPlatform\Metadata\ApiFilter;
  5. use ApiPlatform\Metadata\ApiProperty;
  6. use ApiPlatform\Metadata\ApiResource;
  7. use ApiPlatform\Metadata\Get;
  8. use ApiPlatform\Metadata\Patch;
  9. use ApiPlatform\Metadata\Post;
  10. use ApiPlatform\OpenApi\Model;
  11. use App\Controller\Expediting\ShipmentDocumentUploadAction;
  12. use App\Doctrine\Type\Expediting\ExpeditingStatus;
  13. use App\Doctrine\Type\Expediting\TransportMode;
  14. use App\Dto\Expediting\ConsolidationInput;
  15. use App\Dto\Expediting\ConsolidationOutput;
  16. use App\Dto\Expediting\ContainerRemovalInput;
  17. use App\Dto\Expediting\ContainersInput;
  18. use App\Dto\Expediting\ContainersOutput;
  19. use App\Dto\Expediting\DocumentationKitItemInput;
  20. use App\Dto\Expediting\DocumentationKitDetailsInput;
  21. use App\Dto\Expediting\ShipmentBookingInput;
  22. use App\Dto\Expediting\ShipmentInput;
  23. use App\Dto\Expediting\ShipmentMilestonesOutput;
  24. use App\Dto\Expediting\ShipmentPackageInput;
  25. use App\Dto\Expediting\ShipmentStatusInput;
  26. use App\Dto\Expediting\ShippingPlanInput;
  27. use App\Entity\Common as Common;
  28. use App\Entity\File\File;
  29. use App\Entity\Places\Facility;
  30. use App\Entity\Sales\WorkOrder;
  31. use App\Processor\Expediting\ConsolidationInputProcessor;
  32. use App\Processor\Expediting\ConsolidationPackageAdditionProcessor;
  33. use App\Processor\Expediting\ConsolidationPackageRemovalProcessor;
  34. use App\Processor\Expediting\ConsolidationPlanCreationProcessor;
  35. use App\Processor\Expediting\ContainerDeletionProcessor;
  36. use App\Processor\Expediting\DocumentationKitDetailsProcessor;
  37. use App\Processor\Expediting\DocumentationKitItemProcessor;
  38. use App\Processor\Expediting\ShipmentBookingInputProcessor;
  39. use App\Processor\Expediting\ShipmentContainersInputProcessor;
  40. use App\Processor\Expediting\ShipmentInputProcessor;
  41. use App\Processor\Expediting\ShipmentStatusInputProcessor;
  42. use App\Provider\Expediting\ConsolidationOutputProvider;
  43. use App\Provider\Expediting\ShipmentContainersProvider;
  44. use App\Provider\Expediting\ShipmentDocumentationKitRequestProvider;
  45. use App\Provider\Expediting\ShipmentMilestonesProvider;
  46. use App\Validator\IsValidEnum\IsValidEnum;
  47. use ArrayObject;
  48. use Doctrine\Common\Collections\ArrayCollection;
  49. use Doctrine\Common\Collections\Collection;
  50. use Doctrine\DBAL\Types\Types;
  51. use Doctrine\ORM\Mapping as ORM;
  52. use Gedmo\Mapping\Annotation as Gedmo;
  53. use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
  54. use Symfony\Component\Serializer\Annotation\Ignore;
  55. use Symfony\Component\Validator\Constraints as Assert;
  56. #[ORM\Table(name'expediting_shipment')]
  57. #[ORM\Index(columns: ['created_by'], name'expediting_shipment_created_by_idx')]
  58. #[ORM\Index(columns: ['updated_by'], name'expediting_shipment_updated_by_idx')]
  59. #[ORM\UniqueConstraint(name'expediting_shipment_reference_key'columns: ['reference'])]
  60. #[ORM\UniqueConstraint(name'expediting_shipment_slug_key'columns: ['slug'])]
  61. #[ORM\Entity]
  62. #[UniqueEntity(fields: ['reference''slug'])]
  63. #[ApiResource(
  64.     routePrefix'/expediting',
  65.     inputShipmentInput::class,
  66.     processorShipmentInputProcessor::class,
  67. )]
  68. #[ApiResource(
  69.     operations: [
  70.         new Get(
  71.             uriTemplate'/expediting/shipments/{slug}/milestones',
  72.             outputShipmentMilestonesOutput::class,
  73.             providerShipmentMilestonesProvider::class
  74.         ),
  75.         new Get(
  76.             uriTemplate'/expediting/shipments/{slug}/containers',
  77.             outputContainersOutput::class,
  78.             providerShipmentContainersProvider::class
  79.         ),
  80.         new Get(
  81.             uriTemplate'/expediting/shipments/{slug}/shipment',
  82.             outputConsolidationOutput::class,
  83.             providerConsolidationOutputProvider::class
  84.         ),
  85.         new Get(
  86.             uriTemplate'/expediting/shipments/{slug}/kit-request',
  87.             providerShipmentDocumentationKitRequestProvider::class
  88.         ),
  89.         new Post(
  90.             uriTemplate'/expediting/shipments/{slug}/kit/details',
  91.             inputDocumentationKitDetailsInput::class,
  92.             processorDocumentationKitDetailsProcessor::class
  93.         ),
  94.         new Post(
  95.             uriTemplate'/expediting/shipments/{slug}/kit/item',
  96.             inputDocumentationKitItemInput::class,
  97.             processorDocumentationKitItemProcessor::class
  98.         ),
  99.         new Post(
  100.             uriTemplate'/expediting/shipments/{slug}/containers',
  101.             inputContainersInput::class,
  102.             processorShipmentContainersInputProcessor::class
  103.         ),
  104.         new Post(
  105.             uriTemplate'/expediting/shipments/{slug}/container-remove',
  106.             inputContainerRemovalInput::class,
  107.             processorContainerDeletionProcessor::class
  108.         ),
  109.         new Post(
  110.             uriTemplate'/expediting/shipments/{slug}/plan',
  111.             inputShippingPlanInput::class,
  112.             processorConsolidationPlanCreationProcessor::class
  113.         ),
  114.         new Post(
  115.             uriTemplate'/expediting/shipments/{slug}/booking',
  116.             inputShipmentBookingInput::class,
  117.             processorShipmentBookingInputProcessor::class
  118.         ),
  119.         new Post(
  120.             uriTemplate'/expediting/shipments/{slug}/add',
  121.             inputShipmentPackageInput::class,
  122.             processorConsolidationPackageAdditionProcessor::class
  123.         ),
  124.         new Post(
  125.             uriTemplate'/expediting/shipments/{slug}/remove',
  126.             inputShipmentPackageInput::class,
  127.             processorConsolidationPackageRemovalProcessor::class
  128.         ),
  129.         new Post(
  130.             uriTemplate'/expediting/shipments/{slug}/shipment',
  131.             inputConsolidationInput::class,
  132.             processorConsolidationInputProcessor::class
  133.         ),
  134.         new Patch(
  135.             uriTemplate'/expediting/shipments/{slug}/status',
  136.             inputFormats: ['json' => ['application/merge-patch+json']],
  137.             inputShipmentStatusInput::class,
  138.             processorShipmentStatusInputProcessor::class
  139.         ),
  140.     ]
  141. )]
  142. #[ApiResource(
  143.     // FIXME : This does not work well with docker
  144.     uriTemplate'/shipments/{slug}/attachments',
  145.     operations: [
  146.         new Post(
  147.             controllerShipmentDocumentUploadAction::class,
  148.             openapi: new Model\Operation(
  149.                 requestBody: new Model\RequestBody(
  150.                     content: new ArrayObject([
  151.                         'multipart/form-data' => [
  152.                             'schema' => [
  153.                                 'type' => 'object',
  154.                                 'properties' => [
  155.                                     'file' => [
  156.                                         'type' => 'string',
  157.                                         'format' => 'binary'
  158.                                     ]
  159.                                 ]
  160.                             ]
  161.                         ]
  162.                     ])
  163.                 )
  164.             ),
  165.             shortName'Upload',
  166.             validationContext: ['groups' => ['Default''upload:create']],
  167.             deserializefalse,
  168.         ),
  169.     ],
  170.     routePrefix'/expediting',
  171. )]
  172. #[ApiFilter(SearchFilter::class, properties: [
  173.     'reference' => 'ipartial',
  174. ])]
  175. class Shipment
  176. {
  177.     use Common\Blameable;
  178.     use Common\Trackable;
  179.     #[ORM\Id]
  180.     #[ORM\GeneratedValue(strategy'IDENTITY')]
  181.     #[ORM\SequenceGenerator(sequenceName'expediting_shipment_id_seq')]
  182.     #[ORM\Column(typeTypes::INTEGER)]
  183.     #[ApiProperty(identifierfalse)]
  184.     private ?int $id null;
  185.     #[Gedmo\Slug(fields: ['reference'])]
  186.     #[ORM\Column(typeTypes::STRING)]
  187.     #[ApiProperty(identifiertrue)]
  188.     private ?string $slug null;
  189.     #[Assert\NotBlank]
  190.     #[Assert\Type(typeTypes::STRING)]
  191.     #[ORM\Column(typeTypes::STRING)]
  192.     private ?string $reference null;
  193.     #[Assert\NotNull]
  194.     #[IsValidEnum(enumExpeditingStatus::class)]
  195.     #[ORM\Column(typeTypes::STRINGnullablefalseenumTypeExpeditingStatus::class, options: ['default' => 'PLANNED'])]
  196.     private ?ExpeditingStatus $status ExpeditingStatus::PLANNED;
  197.     /** @var Collection<int, ShippingPlan> */
  198.     #[ORM\OneToMany(mappedBy'shipment'targetEntityShippingPlan::class)]
  199.     private Collection $shippingPlans;
  200.     /** @var Collection<int, Container> */
  201.     #[ORM\OneToMany(mappedBy'shipment'targetEntityContainer::class)]
  202.     private Collection $containers;
  203.     /** @var Collection<int, Package> */
  204.     #[ORM\ManyToMany(targetEntityPackage::class, inversedBy'shipments')]
  205.     #[ORM\JoinTable(name'expediting_shipment_package_map')]
  206.     #[ORM\JoinColumn(name'shipment_id')]
  207.     #[ORM\InverseJoinColumn(name'package_id')]
  208.     private Collection $packages;
  209.     /** @var Collection<int, DocumentationKit> */
  210.     #[ORM\OneToMany(mappedBy'shipment'targetEntityDocumentationKit::class, cascade: ['persist''remove'])]
  211.     #[ORM\OrderBy(['id' => 'ASC'])]
  212.     #[Ignore]
  213.     private Collection $documentationKits;
  214.     /** @var Collection<int, File> */
  215.     #[ORM\ManyToMany(targetEntityFile::class, cascade: ['persist''remove'])]
  216.     #[ORM\JoinTable(name'expediting_shipment_file_map')]
  217.     #[ORM\JoinColumn(name'shipment_id')]
  218.     #[ORM\InverseJoinColumn(name'file_id')]
  219.     #[ORM\OrderBy(['id' => 'ASC'])]
  220.     private Collection $attachments;
  221.     public function __construct()
  222.     {
  223.         $this->shippingPlans = new ArrayCollection();
  224.         $this->containers = new ArrayCollection();
  225.         $this->packages = new ArrayCollection();
  226.         $this->documentationKits = new ArrayCollection();
  227.         $this->attachments = new ArrayCollection();
  228.     }
  229.     public function getId(): ?int
  230.     {
  231.         return $this->id;
  232.     }
  233.     public function getSlug(): ?string
  234.     {
  235.         return $this->slug;
  236.     }
  237.     public function setSlug(string $slug): self
  238.     {
  239.         $this->slug $slug;
  240.         return $this;
  241.     }
  242.     public function getReference(): ?string
  243.     {
  244.         return $this->reference;
  245.     }
  246.     public function setReference(string $reference): self
  247.     {
  248.         $this->reference $reference;
  249.         return $this;
  250.     }
  251.     #[ApiProperty(readableLinktrue)]
  252.     public function getShippingSegments(): ?Collection
  253.     {
  254.         $shippingPlan $this->shippingPlans->first();
  255.         if (!$shippingPlan instanceof ShippingPlan) {
  256.             return null;
  257.         }
  258.         return $shippingPlan->getShippingSegments();
  259.     }
  260.     #[ApiProperty(readableLinktrue)]
  261.     public function getPlaceOfLoading(): ?Facility
  262.     {
  263.         $shippingPlan $this->shippingPlans->first();
  264.         if (!$shippingPlan instanceof ShippingPlan) {
  265.             return null;
  266.         }
  267.         return $shippingPlan->getShippingSegments()?->first()?->getPlaceOfLoading();
  268.     }
  269.     #[ApiProperty(readableLinktrue)]
  270.     public function getPlaceOfDischarge(): ?Facility
  271.     {
  272.         $shippingPlan $this->shippingPlans->first();
  273.         if (!$shippingPlan instanceof ShippingPlan) {
  274.             return null;
  275.         }
  276.         return $shippingPlan->getShippingSegments()?->last()?->getPlaceOfDischarge();
  277.     }
  278.     public function getTransportMode(): ?TransportMode
  279.     {
  280.         $shippingPlan $this->shippingPlans->first();
  281.         if (!$shippingPlan instanceof ShippingPlan) {
  282.             return null;
  283.         }
  284.         return $shippingPlan->getShippingSegments()?->first()?->getTransportMode();
  285.     }
  286.     public function getStatus(): ?ExpeditingStatus
  287.     {
  288.         return $this->status;
  289.     }
  290.     public function setStatus(?ExpeditingStatus $status): self
  291.     {
  292.         $this->status $status;
  293.         return $this;
  294.     }
  295.     /** @return Collection<int, ShippingPlan> */
  296.     public function getShippingPlans(): Collection
  297.     {
  298.         return $this->shippingPlans;
  299.     }
  300.     public function addShippingPlan(ShippingPlan $shippingPlan): static
  301.     {
  302.         if (!$this->shippingPlans->contains($shippingPlan)) {
  303.             $this->shippingPlans[] = $shippingPlan;
  304.             $shippingPlan->setShipment($this);
  305.         }
  306.         return $this;
  307.     }
  308.     public function removeShippingPlan(ShippingPlan $shippingPlan): static
  309.     {
  310.         if ($this->shippingPlans->removeElement($shippingPlan)) {
  311.             // set the owning side to null (unless already changed)
  312.             if ($shippingPlan->getShipment() === $this) {
  313.                 $shippingPlan->setShipment(null);
  314.             }
  315.         }
  316.         return $this;
  317.     }
  318.     /** @return Collection<int, Container> */
  319.     public function getContainers(): Collection
  320.     {
  321.         return $this->containers;
  322.     }
  323.     public function addContainer(Container $container): static
  324.     {
  325.         if (!$this->containers->contains($container)) {
  326.             $this->containers[] = $container;
  327.             $container->setShipment($this);
  328.         }
  329.         return $this;
  330.     }
  331.     public function removeContainer(Container $container): static
  332.     {
  333.         if ($this->containers->removeElement($container)) {
  334.             // set the owning side to null (unless already changed)
  335.             if ($container->getShipment() === $this) {
  336.                 $container->setShipment(null);
  337.             }
  338.         }
  339.         return $this;
  340.     }
  341.     /**
  342.      * @return Collection<int, WorkOrder>
  343.      */
  344.     public function getWorkOrders(): Collection
  345.     {
  346.         $workOrders = new ArrayCollection();
  347.         foreach ($this->packages as $package) {
  348.             foreach ($package->getItems() as $item) {
  349.                 if ($item instanceof Part) {
  350.                     $workOrder $item->getItem()->getFreight()->getWorkOrder();
  351.                 } elseif ($item instanceof Item) {
  352.                     $workOrder $item->getFreight()->getWorkOrder();
  353.                 } else {
  354.                     continue;
  355.                 }
  356.                 if (!$workOrders->contains($workOrder)) {
  357.                     $workOrders->add($workOrder);
  358.                 }
  359.             }
  360.         }
  361.         return $workOrders;
  362.     }
  363.     /**
  364.      * @return Collection<int, Package>
  365.      */
  366.     public function getPackages(): Collection
  367.     {
  368.         return $this->packages;
  369.     }
  370.     public function addPackage(Package $package): self
  371.     {
  372.         if (!$this->packages->contains($package)) {
  373.             $this->packages->add($package);
  374.         }
  375.         return $this;
  376.     }
  377.     public function removePackage(Package $package): self
  378.     {
  379.         $this->packages->removeElement($package);
  380.         return $this;
  381.     }
  382.     public function hasDocumentationKit(): ?bool
  383.     {
  384.         return !$this->documentationKits->isEmpty();
  385.     }
  386.     public function getDocumentationKit(): ?DocumentationKit
  387.     {
  388.         return !$this->documentationKits->isEmpty()
  389.             ? $this->documentationKits->last()
  390.             : null;
  391.     }
  392.     /**
  393.      * @return Collection<int, DocumentationKit>
  394.      */
  395.     public function getDocumentationKits(): Collection
  396.     {
  397.         return $this->documentationKits;
  398.     }
  399.     public function addDocumentationKit(DocumentationKit $documentationKit): self
  400.     {
  401.         if (!$this->documentationKits->contains($documentationKit)) {
  402.             $this->documentationKits->add($documentationKit);
  403.             $documentationKit->setShipment($this);
  404.         }
  405.         return $this;
  406.     }
  407.     public function removeFreight(DocumentationKit $documentationKit): self
  408.     {
  409.         if ($this->documentationKits->removeElement($documentationKit)) {
  410.             // set the owning side to null (unless already changed)
  411.             if ($documentationKit->getShipment() === $this) {
  412.                 $documentationKit->setShipment(null);
  413.             }
  414.         }
  415.         return $this;
  416.     }
  417.     /**
  418.      * @return Collection<int, File>
  419.      */
  420.     public function getAttachments(): Collection
  421.     {
  422.         return $this->attachments;
  423.     }
  424.     public function addAttachment(File $attachment): self
  425.     {
  426.         if (!$this->attachments->contains($attachment)) {
  427.             $this->attachments->add($attachment);
  428.         }
  429.         return $this;
  430.     }
  431.     public function removeAttachment(File $attachment): self
  432.     {
  433.         $this->attachments->removeElement($attachment);
  434.         return $this;
  435.     }
  436. }