[ 'carrier:item:read', 'client:read', 'client_address:read', 'supplier:read', 'supplier_address:read', 'site:read', 'default:read', ]], ), new Post( uriTemplate: '/carriers/{carrierId}/prices', uriVariables: [ 'carrierId' => new Link(fromClass: Carrier::class, toProperty: 'carrier'), ], // read:false : pas de stade lecture du parent. Le Link toProperty // resoudrait l'enfant (SELECT CarrierPrice ... WHERE carrier = :id) et // casse en NonUniqueResult des >= 2 enfants. Le parent est rattache // manuellement par CarrierPriceProcessor::linkParent (404 si absent). read: false, security: "is_granted('transport.carriers.manage')", normalizationContext: ['groups' => [ 'carrier:item:read', 'client:read', 'client_address:read', 'supplier:read', 'supplier_address:read', 'site:read', 'default:read', ]], denormalizationContext: ['groups' => ['carrier:write:prices']], processor: CarrierPriceProcessor::class, ), new Patch( security: "is_granted('transport.carriers.manage')", normalizationContext: ['groups' => [ 'carrier:item:read', 'client:read', 'client_address:read', 'supplier:read', 'supplier_address:read', 'site:read', 'default:read', ]], denormalizationContext: ['groups' => ['carrier:write:prices']], processor: CarrierPriceProcessor::class, ), new Delete( security: "is_granted('transport.carriers.manage')", processor: CarrierPriceProcessor::class, ), ], )] #[ORM\Entity] #[ORM\Table(name: 'carrier_price')] #[ORM\Index(name: 'idx_carrier_price_carrier', columns: ['carrier_id'])] #[ORM\Index(name: 'idx_carrier_price_client', columns: ['client_id'])] #[ORM\Index(name: 'idx_carrier_price_client_address', columns: ['client_delivery_address_id'])] #[ORM\Index(name: 'idx_carrier_price_departure_site', columns: ['departure_site_id'])] #[ORM\Index(name: 'idx_carrier_price_supplier', columns: ['supplier_id'])] #[ORM\Index(name: 'idx_carrier_price_supplier_address', columns: ['supplier_supply_address_id'])] #[ORM\Index(name: 'idx_carrier_price_delivery_site', columns: ['delivery_site_id'])] #[ORM\Index(name: 'idx_carrier_price_created_by', columns: ['created_by'])] #[ORM\Index(name: 'idx_carrier_price_updated_by', columns: ['updated_by'])] #[Auditable] class CarrierPrice implements TimestampableInterface, BlamableInterface { use TimestampableBlamableTrait; #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] #[Groups(['carrier:item:read'])] private ?int $id = null; #[ORM\ManyToOne(targetEntity: Carrier::class, inversedBy: 'prices')] #[ORM\JoinColumn(name: 'carrier_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')] private ?Carrier $carrier = null; /** CLIENT|FOURNISSEUR (RG-4.09) — pilote la branche active. */ #[ORM\Column(length: 12)] #[Assert\NotBlank(message: 'Le sens du prix est obligatoire.')] #[Assert\Choice(choices: ['CLIENT', 'FOURNISSEUR'], message: 'Le sens du prix est invalide.')] #[Groups(['carrier:item:read', 'carrier:write:prices'])] private ?string $direction = null; // === Branche CLIENT (RG-4.10) === // Obligation conditionnelle (direction=CLIENT) + appartenance de l'adresse au // client : portees par le CarrierPriceProcessor (relations resolues a la // denormalisation, hors portee d'une contrainte d'attribut). #[ORM\ManyToOne(targetEntity: ClientInterface::class)] #[ORM\JoinColumn(name: 'client_id', referencedColumnName: 'id', nullable: true, onDelete: 'RESTRICT')] #[Groups(['carrier:item:read', 'carrier:write:prices'])] private ?ClientInterface $client = null; #[ORM\ManyToOne(targetEntity: ClientAddressInterface::class)] #[ORM\JoinColumn(name: 'client_delivery_address_id', referencedColumnName: 'id', nullable: true, onDelete: 'RESTRICT')] #[Groups(['carrier:item:read', 'carrier:write:prices'])] private ?ClientAddressInterface $clientDeliveryAddress = null; /** Adresse de depart = un des 3 sites (86/17/82). */ #[ORM\ManyToOne(targetEntity: SiteInterface::class)] #[ORM\JoinColumn(name: 'departure_site_id', referencedColumnName: 'id', nullable: true, onDelete: 'RESTRICT')] #[Groups(['carrier:item:read', 'carrier:write:prices'])] private ?SiteInterface $departureSite = null; // === Branche FOURNISSEUR (RG-4.11) === #[ORM\ManyToOne(targetEntity: SupplierInterface::class)] #[ORM\JoinColumn(name: 'supplier_id', referencedColumnName: 'id', nullable: true, onDelete: 'RESTRICT')] #[Groups(['carrier:item:read', 'carrier:write:prices'])] private ?SupplierInterface $supplier = null; #[ORM\ManyToOne(targetEntity: SupplierAddressInterface::class)] #[ORM\JoinColumn(name: 'supplier_supply_address_id', referencedColumnName: 'id', nullable: true, onDelete: 'RESTRICT')] #[Groups(['carrier:item:read', 'carrier:write:prices'])] private ?SupplierAddressInterface $supplierSupplyAddress = null; /** Adresse de livraison = un des 3 sites (86/17/82). */ #[ORM\ManyToOne(targetEntity: SiteInterface::class)] #[ORM\JoinColumn(name: 'delivery_site_id', referencedColumnName: 'id', nullable: true, onDelete: 'RESTRICT')] #[Groups(['carrier:item:read', 'carrier:write:prices'])] private ?SiteInterface $deliverySite = null; // === Commun (toujours obligatoires, RG-4.10/4.11) === /** BENNE|FOND_MOUVANT. */ #[ORM\Column(name: 'container_type', length: 12)] #[Assert\NotBlank(message: 'Le type de contenant est obligatoire.')] #[Assert\Choice(choices: ['BENNE', 'FOND_MOUVANT'], message: 'Le type de contenant est invalide.')] #[Groups(['carrier:item:read', 'carrier:write:prices'])] private ?string $containerType = null; /** FORFAIT|TONNE. */ #[ORM\Column(name: 'pricing_unit', length: 8)] #[Assert\NotBlank(message: 'L\'unite de tarification est obligatoire.')] #[Assert\Choice(choices: ['FORFAIT', 'TONNE'], message: 'L\'unite de tarification est invalide.')] #[Groups(['carrier:item:read', 'carrier:write:prices'])] private ?string $pricingUnit = null; #[ORM\Column(type: 'decimal', precision: 12, scale: 2)] #[Assert\NotBlank(message: 'Le prix est obligatoire.')] #[Assert\PositiveOrZero(message: 'Le prix ne peut pas etre negatif.')] #[Groups(['carrier:item:read', 'carrier:write:prices'])] private ?string $price = null; /** EN_COURS|VALIDE|NON_VALIDE. */ #[ORM\Column(name: 'price_state', length: 12)] #[Assert\NotBlank(message: 'L\'etat du prix est obligatoire.')] #[Assert\Choice(choices: ['EN_COURS', 'VALIDE', 'NON_VALIDE'], message: 'L\'etat du prix est invalide.')] #[Groups(['carrier:item:read', 'carrier:write:prices'])] private ?string $priceState = null; #[ORM\Column(options: ['default' => 0])] private int $position = 0; public function getId(): ?int { return $this->id; } public function getCarrier(): ?Carrier { return $this->carrier; } public function setCarrier(?Carrier $carrier): static { $this->carrier = $carrier; return $this; } public function getDirection(): ?string { return $this->direction; } public function setDirection(?string $direction): static { $this->direction = $direction; return $this; } public function getClient(): ?ClientInterface { return $this->client; } public function setClient(?ClientInterface $client): static { $this->client = $client; return $this; } public function getClientDeliveryAddress(): ?ClientAddressInterface { return $this->clientDeliveryAddress; } public function setClientDeliveryAddress(?ClientAddressInterface $clientDeliveryAddress): static { $this->clientDeliveryAddress = $clientDeliveryAddress; return $this; } public function getDepartureSite(): ?SiteInterface { return $this->departureSite; } public function setDepartureSite(?SiteInterface $departureSite): static { $this->departureSite = $departureSite; return $this; } public function getSupplier(): ?SupplierInterface { return $this->supplier; } public function setSupplier(?SupplierInterface $supplier): static { $this->supplier = $supplier; return $this; } public function getSupplierSupplyAddress(): ?SupplierAddressInterface { return $this->supplierSupplyAddress; } public function setSupplierSupplyAddress(?SupplierAddressInterface $supplierSupplyAddress): static { $this->supplierSupplyAddress = $supplierSupplyAddress; return $this; } public function getDeliverySite(): ?SiteInterface { return $this->deliverySite; } public function setDeliverySite(?SiteInterface $deliverySite): static { $this->deliverySite = $deliverySite; return $this; } public function getContainerType(): ?string { return $this->containerType; } public function setContainerType(?string $containerType): static { $this->containerType = $containerType; return $this; } public function getPricingUnit(): ?string { return $this->pricingUnit; } public function setPricingUnit(?string $pricingUnit): static { $this->pricingUnit = $pricingUnit; return $this; } public function getPrice(): ?string { return $this->price; } public function setPrice(?string $price): static { $this->price = $price; return $this; } public function getPriceState(): ?string { return $this->priceState; } public function setPriceState(?string $priceState): static { $this->priceState = $priceState; return $this; } public function getPosition(): int { return $this->position; } public function setPosition(int $position): static { $this->position = $position; return $this; } }