src/Entity/Sales/WorkOrder.php line 196
<?php
namespace App\Entity\Sales;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use ApiPlatform\OpenApi\Model;
use App\Controller\Expediting\FreightDatasheetUploadAction;
use App\Controller\Sales\WorkOrderDocumentUploadAction;
use App\Doctrine\Type\Sales\Incoterm;
use App\Doctrine\Type\Sales\WorkOrderStatus;
use App\Dto\Common\ObservationInput;
use App\Dto\Expediting\FreightDatasheetInput;
use App\Dto\Expediting\ReceptionInput;
use App\Dto\Expediting\ReceptionPlanInput;
use App\Dto\Expediting\ShippingPlanInput;
use App\Dto\Sales\WorkOrderInput;
use App\Dto\Sales\WorkOrderOutput;
use App\Dto\Sales\WorkOrderStatusInput;
use App\Entity\Common\Blameable;
use App\Entity\Common\Company;
use App\Entity\Common\Observation;
use App\Entity\Common\Trackable;
use App\Entity\Delete\Waybill;
use App\Entity\Expediting\Freight;
use App\Entity\Expediting\Item;
use App\Entity\Expediting\Package;
use App\Entity\Expediting\ShippingPlan;
use App\Entity\File\File;
use App\Entity\File\Parsable;
use App\Entity\Identity\User;
use App\Entity\Places\Facility;
use App\Entity\Scheduler\Task;
use App\Processor\Expediting\ReceptionInputProcessor;
use App\Processor\Expediting\ReceptionPlanInputProcessor;
use App\Processor\Expediting\ShippingPlanCreationProcessor;
use App\Processor\Sales\FreightDatasheetProcessor;
use App\Processor\Sales\WorkOrderCreationProcessor;
use App\Processor\Sales\WorkOrderObservationProcessor;
use App\Processor\Sales\WorkOrderStatusInputProcessor;
use App\Provider\Finance\WorkOrderQuotationsProvider;
use App\Provider\Sales\WorkOrderProvider;
use App\Validator\IsValidEnum\IsValidEnum;
use ArrayObject;
use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Table(name: 'work_order')]
#[ORM\Index(columns: ['issued_by'], name: 'work_order_issued_by_idx')]
#[ORM\Index(columns: ['client_id'], name: 'work_order_client_id_idx')]
#[ORM\Index(columns: ['supplier_id'], name: 'work_order_supplier_id_idx')]
#[ORM\Index(columns: ['contract_id'], name: 'work_order_contract_id_idx')]
#[ORM\Index(columns: ['place_of_loading_id'], name: 'work_order_place_of_loading_id_idx')]
#[ORM\Index(columns: ['place_of_discharge_id'], name: 'work_order_place_of_discharge_id_idx')]
#[ORM\Index(columns: ['source_id'], name: 'work_order_source_id_idx')]
#[ORM\Index(columns: ['created_by'], name: 'work_order_created_by_idx')]
#[ORM\Index(columns: ['updated_by'], name: 'work_order_updated_by_idx')]
#[ORM\UniqueConstraint(name: 'work_order_slug_key', columns: ['slug'])]
#[ORM\UniqueConstraint(name: 'work_order_reference_key', columns: ['reference'])]
#[ORM\Entity]
#[UniqueEntity(fields: ['reference', 'slug'])]
#[ApiResource(
shortName: 'WorkOrder',
operations: [
new Get(output: WorkOrderOutput::class, provider: WorkOrderProvider::class),
new GetCollection(),
new GetCollection(uriTemplate: '/procurement/work_orders', name: 'work_order_procurement'),
new GetCollection(uriTemplate: '/work_orders/{slug}/quotations', shortName: 'Quotation', provider: WorkOrderQuotationsProvider::class),
new Post(input: WorkOrderInput::class, processor: WorkOrderCreationProcessor::class),
new Patch(inputFormats: ['json' => ['application/merge-patch+json']], input: WorkOrderInput::class, processor: WorkOrderCreationProcessor::class),
new Put(),
new Delete(),
],
order: ['id' => 'DESC']
)]
#[ApiResource(
operations: [
new Patch(
uriTemplate: '/work_orders/{slug}/status',
inputFormats: ['json' => ['application/merge-patch+json']],
input: WorkOrderStatusInput::class,
processor: WorkOrderStatusInputProcessor::class
),
new Post(
uriTemplate: '/work_orders/{slug}/plan',
input: ShippingPlanInput::class,
processor: ShippingPlanCreationProcessor::class
),
new Post(
uriTemplate: '/work_orders/{slug}/etr',
input: ReceptionPlanInput::class,
processor: ReceptionPlanInputProcessor::class
),
new Post(
uriTemplate: '/work_orders/{slug}/atr',
input: ReceptionInput::class,
processor: ReceptionInputProcessor::class
),
new Post(
uriTemplate: '/work_orders/{slug}/datasheet',
input: FreightDatasheetInput::class,
processor: FreightDatasheetProcessor::class
),
new Post(
uriTemplate: '/work_orders/{slug}/observations',
input: ObservationInput::class,
processor: WorkOrderObservationProcessor::class
),
new GetCollection(
uriTemplate: '/scope/{slug}/work_orders',
uriVariables: [
'slug' => new Link(fromProperty: 'workOrders', fromClass: Contract::class),
],
),
]
)]
#[ApiResource(
// FIXME : This does not work well with docker
uriTemplate: '/work_orders/{slug}/import',
operations: [
new Post(
controller: FreightDatasheetUploadAction::class,
openapi: new Model\Operation(
requestBody: new Model\RequestBody(
content: new ArrayObject([
'multipart/form-data' => [
'schema' => [
'type' => 'object',
'properties' => [
'file' => [
'type' => 'string',
'format' => 'binary'
]
]
]
]
])
)
),
shortName: 'Upload',
validationContext: ['groups' => ['Default', 'upload:create']],
deserialize: false,
),
],
)]
#[ApiResource(
// FIXME : This does not work well with docker
uriTemplate: '/work_orders/{slug}/attachments',
operations: [
new Post(
controller: WorkOrderDocumentUploadAction::class,
openapi: new Model\Operation(
requestBody: new Model\RequestBody(
content: new ArrayObject([
'multipart/form-data' => [
'schema' => [
'type' => 'object',
'properties' => [
'file' => [
'type' => 'string',
'format' => 'binary'
]
]
]
]
])
)
),
shortName: 'Upload',
validationContext: ['groups' => ['Default', 'upload:create']],
deserialize: false,
),
],
)]
#[ApiFilter(SearchFilter::class, properties: [
'reference' => 'ipartial',
'tasks.type' => 'exact',
'tasks.status' => 'exact',
'freights.order.number' => 'ipartial',
])]
class WorkOrder
{
use Blameable;
use Trackable;
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
#[ORM\SequenceGenerator(sequenceName: 'work_order_id_seq')]
#[ORM\Column(type: Types::INTEGER)]
#[ApiProperty(identifier: false)]
private ?int $id = null;
#[Gedmo\Slug(fields: ['reference'])]
#[ORM\Column(type: Types::STRING)]
#[ApiProperty(identifier: true)]
private ?string $slug = null;
#[Assert\NotNull]
#[Assert\Length(min: 5)]
#[ORM\Column(type: Types::STRING)]
private ?string $reference = null;
#[Assert\Type(type: User::class)]
#[ORM\ManyToOne(targetEntity: User::class)]
#[ORM\JoinColumn(name: 'issued_by')]
private ?User $issuedBy = null;
#[Assert\NotNull]
#[Assert\Type(type: Company::class)]
#[ORM\ManyToOne(targetEntity: Company::class)]
#[ORM\JoinColumn(name: 'client_id', nullable: false)]
private ?Company $client = null;
#[Assert\Type(type: Company::class)]
#[ORM\ManyToOne(targetEntity: Company::class)]
#[ORM\JoinColumn(name: 'supplier_id')]
private ?Company $supplier = null;
#[Assert\Type(type: Contract::class)]
#[ORM\ManyToOne(targetEntity: Contract::class, inversedBy: 'workOrders')]
#[ORM\JoinColumn(name: 'contract_id')]
private ?Contract $contract = null;
#[Assert\NotNull]
#[ORM\ManyToOne(targetEntity: Facility::class)]
#[ORM\JoinColumn(name: 'place_of_loading_id', nullable: false)]
private ?Facility $placeOfLoading = null;
#[Assert\NotNull]
#[ORM\ManyToOne(targetEntity: Facility::class)]
#[ORM\JoinColumn(name: 'place_of_discharge_id', nullable: false)]
private ?Facility $placeOfDischarge = null;
#[Assert\Type(type: Parsable::class)]
#[ORM\OneToOne(inversedBy: 'workOrder', targetEntity: Parsable::class)]
#[ORM\JoinColumn(name: 'source_id')]
private ?Parsable $source = null;
/** @var Collection<int, ShippingPlan> */
#[ORM\OneToMany(mappedBy: 'workOrder', targetEntity: ShippingPlan::class)]
#[ORM\OrderBy(['id' => 'DESC'])]
private Collection $shippingPlans;
#[IsValidEnum(enum: Incoterm::class)]
#[ORM\Column(type: Types::STRING, nullable: true, enumType: Incoterm::class)]
private ?Incoterm $incoterm = null;
#[Assert\NotNull]
#[Assert\Type(type: DateTimeInterface::class)]
#[ORM\Column(type: Types::DATE_MUTABLE)]
private ?DateTimeInterface $requestedOn = null;
#[Assert\NotNull]
#[Assert\Type(type: DateTimeInterface::class)]
#[ORM\Column(type: Types::DATE_MUTABLE)]
private ?DateTimeInterface $availableOn = null;
#[Assert\NotNull]
#[IsValidEnum(enum: WorkOrderStatus::class)]
#[ORM\Column(type: Types::STRING, enumType: WorkOrderStatus::class, options: ['default' => 'DRAFTED'])]
private ?WorkOrderStatus $status = WorkOrderStatus::DRAFTED;
/** @var Collection<int, Task> */
#[ORM\ManyToMany(targetEntity: Task::class, mappedBy: 'workOrders')]
private Collection $tasks;
/** @var Collection<int, Freight> */
#[ORM\OneToMany(mappedBy: 'workOrder', targetEntity: Freight::class, cascade: ['persist', 'remove'])]
private Collection $freights;
/** @var Collection<int, User> */
#[ORM\ManyToMany(targetEntity: User::class)]
#[ORM\JoinTable(name: 'work_order_customer_contact_map')]
#[ORM\JoinColumn(name: 'work_order_id')]
#[ORM\InverseJoinColumn(name: 'customer_contact_id')]
private Collection $customerContacts;
/** @var Collection<int, User> */
#[ORM\ManyToMany(targetEntity: User::class)]
#[ORM\JoinTable(name: 'work_order_supplier_contact_map')]
#[ORM\JoinColumn(name: 'work_order_id')]
#[ORM\InverseJoinColumn(name: 'supplier_contact_id')]
private Collection $supplierContacts;
/** @var Collection<int, File> */
#[ORM\ManyToMany(targetEntity: File::class, cascade: ['persist', 'remove'])]
#[ORM\JoinTable(name: 'work_order_file_map')]
#[ORM\JoinColumn(name: 'work_order_id')]
#[ORM\InverseJoinColumn(name: 'file_id')]
#[ORM\OrderBy(['id' => 'ASC'])]
private Collection $attachments;
/** @var Collection<int, Observation> */
#[ORM\OneToMany(mappedBy: 'workOrder', targetEntity: Observation::class, cascade: ['persist', 'remove'])]
private Collection $observations;
#[Assert\Type(type: FreightDatasheetInput::class)]
#[ORM\Column(type: 'json_document', options: ['jsonb' => true])]
private FreightDatasheetInput $datasheet;
#[Assert\Valid]
#[Assert\Type(type: Waybill::class)]
#[ORM\Column(type: 'json_document', nullable: true, options: ['jsonb' => true])]
private Waybill $waybill;
public function __construct()
{
$this->tasks = new ArrayCollection();
$this->freights = new ArrayCollection();
$this->customerContacts = new ArrayCollection();
$this->supplierContacts = new ArrayCollection();
$this->datasheet = new FreightDatasheetInput();
$this->attachments = new ArrayCollection();
$this->observations = new ArrayCollection();
$this->shippingPlans = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getSlug(): ?string
{
return $this->slug;
}
public function setSlug(string $slug): self
{
$this->slug = $slug;
return $this;
}
public function getReference(): ?string
{
return $this->reference;
}
public function setReference(string $reference): self
{
$this->reference = $reference;
return $this;
}
public function getIssuedBy(): ?User
{
return $this->issuedBy;
}
public function setIssuedBy(?User $issuedBy): self
{
$this->issuedBy = $issuedBy;
return $this;
}
public function getClient(): ?Company
{
return $this->client;
}
public function setClient(?Company $client): self
{
$this->client = $client;
return $this;
}
public function getSupplier(): ?Company
{
return $this->supplier;
}
public function setSupplier(?Company $supplier): self
{
$this->supplier = $supplier;
return $this;
}
public function getContract(): ?Contract
{
return $this->contract;
}
public function setContract(?Contract $contract): self
{
$this->contract = $contract;
return $this;
}
public function getPlaceOfLoading(): ?Facility
{
return $this->placeOfLoading;
}
public function setPlaceOfLoading(?Facility $placeOfLoading): self
{
$this->placeOfLoading = $placeOfLoading;
return $this;
}
public function getPlaceOfDischarge(): ?Facility
{
return $this->placeOfDischarge;
}
public function setPlaceOfDischarge(?Facility $placeOfDischarge): self
{
$this->placeOfDischarge = $placeOfDischarge;
return $this;
}
public function getSource(): ?Parsable
{
return $this->source;
}
public function setSource(?Parsable $source): self
{
$this->source = $source;
return $this;
}
/** @return Collection<int, ShippingPlan> */
public function getShippingPlans(): Collection
{
return $this->shippingPlans;
}
public function addShippingPlan(ShippingPlan $shippingPlan): static
{
if (!$this->shippingPlans->contains($shippingPlan)) {
$this->shippingPlans[] = $shippingPlan;
$shippingPlan->setWorkOrder($this);
}
return $this;
}
public function removeShippingPlan(ShippingPlan $shippingPlan): static
{
if ($this->shippingPlans->removeElement($shippingPlan)) {
// set the owning side to null (unless already changed)
if ($shippingPlan->getWorkOrder() === $this) {
$shippingPlan->setWorkOrder(null);
}
}
return $this;
}
public function getIncoterm(): ?Incoterm
{
return $this->incoterm;
}
public function setIncoterm(?Incoterm $incoterm): self
{
$this->incoterm = $incoterm;
return $this;
}
public function getRequestedOn(): ?DateTimeInterface
{
return $this->requestedOn;
}
public function setRequestedOn(?DateTimeInterface $requestedOn): self
{
$this->requestedOn = $requestedOn;
return $this;
}
public function getAvailableOn(): ?DateTimeInterface
{
return $this->availableOn;
}
public function setAvailableOn(?DateTimeInterface $availableOn): self
{
$this->availableOn = $availableOn;
return $this;
}
public function getStatus(): ?WorkOrderStatus
{
return $this->status;
}
public function setStatus(WorkOrderStatus $status): self
{
$this->status = $status;
return $this;
}
/**
* @return Collection<int, Task>
*/
public function getTasks(): Collection
{
return $this->tasks;
}
public function addTask(Task $task): self
{
if (!$this->tasks->contains($task)) {
$this->tasks[] = $task;
$task->addWorkOrder($this);
}
return $this;
}
public function removeTask(Task $task): self
{
if ($this->tasks->removeElement($task)) {
$task->removeWorkOrder($this);
}
return $this;
}
/**
* @return Collection<int, Freight>
*/
public function getFreights(): Collection
{
return $this->freights;
}
public function addFreight(Freight $freight): self
{
if (!$this->freights->contains($freight)) {
$this->freights->add($freight);
$freight->setWorkOrder($this);
}
return $this;
}
public function removeFreight(Freight $freight): self
{
if ($this->freights->removeElement($freight)) {
// set the owning side to null (unless already changed)
if ($freight->getWorkOrder() === $this) {
$freight->setWorkOrder(null);
}
}
return $this;
}
/**
* @return Collection<int, Package>
*/
public function getPackages(): Collection
{
$packages = new ArrayCollection();
foreach ($this->freights as $freight) {
foreach ($freight->getItems() as $item) {
foreach ($item->getPackages() as $package) {
if (!$packages->contains($package)) {
$packages->add($package);
}
}
}
}
return $packages;
}
/**
* @return Collection<int, Item>
*/
public function getItems(): Collection
{
$items = new ArrayCollection();
foreach ($this->freights as $freight) {
foreach ($freight->getItems() as $item) {
if (!$items->contains($item)) {
$items->add($item);
}
}
}
return $items;
}
/**
* @return Collection<int, User>
*/
public function getCustomerContacts(): Collection
{
return $this->customerContacts;
}
public function addCustomerContact(User $customerContact): self
{
if (!$this->customerContacts->contains($customerContact)) {
$this->customerContacts->add($customerContact);
}
return $this;
}
public function removeCustomerContact(User $customerContact): self
{
$this->customerContacts->removeElement($customerContact);
return $this;
}
/**
* @return Collection<int, User>
*/
public function getSupplierContacts(): Collection
{
return $this->supplierContacts;
}
public function addSupplierContact(User $supplierContact): self
{
if (!$this->supplierContacts->contains($supplierContact)) {
$this->supplierContacts->add($supplierContact);
}
return $this;
}
public function removeSupplierContact(User $supplierContact): self
{
$this->supplierContacts->removeElement($supplierContact);
return $this;
}
/**
* @return Collection<int, File>
*/
public function getAttachments(): Collection
{
return $this->attachments;
}
public function addAttachment(File $attachment): self
{
if (!$this->attachments->contains($attachment)) {
$this->attachments->add($attachment);
}
return $this;
}
public function removeAttachment(File $attachment): self
{
$this->attachments->removeElement($attachment);
return $this;
}
/**
* @return Collection<int, Observation>
*/
public function getObservations(): Collection
{
return $this->observations;
}
public function addObservation(Observation $observation): self
{
if (!$this->observations->contains($observation)) {
$this->observations->add($observation);
$observation->setWorkOrder($this);
}
return $this;
}
public function removeObservation(Observation $observation): self
{
if ($this->observations->removeElement($observation)) {
// set the owning side to null (unless already changed)
if ($observation->getWorkOrder() === $this) {
$observation->setWorkOrder(null);
}
}
return $this;
}
public function getDatasheet(): FreightDatasheetInput
{
return $this->datasheet;
}
public function setDatasheet(FreightDatasheetInput $datasheet): self
{
$this->datasheet = $datasheet;
return $this;
}
public function getWaybill(): Waybill
{
return $this->waybill;
}
public function setWaybill(?Waybill $waybill): static
{
$this->waybill = $waybill;
return $this;
}
}