src/Entity/PurchaseOrder.php line 24

Open in your IDE?
  1. <?php
  2. namespace App\Entity;
  3. use App\Exception\InvalidArgumentException;
  4. use DateTime;
  5. use Doctrine\Common\Collections\ArrayCollection;
  6. use Doctrine\Common\Collections\Collection;
  7. use Doctrine\ORM\Mapping as ORM;
  8. use Exception;
  9. use Symfony\Component\Validator\Constraints as Assert;
  10. use Symfony\Component\Validator\Context\ExecutionContextInterface;
  11. /**
  12.  * @ORM\Entity(
  13.  *     repositoryClass="App\Repository\PurchaseOrderRepository"
  14.  * )
  15.  * @ORM\Table(
  16.  *     name="purchase_order",
  17.  *     options={"collate"="utf8_swedish_ci"}
  18.  * )
  19.  * @ORM\HasLifecycleCallbacks
  20.  */
  21. class PurchaseOrder implements EntityInterfaceLoggableInterfaceSalesDocumentInterfaceSalesDocumentDiscountInterface
  22. {
  23.     use DocumentTrait;
  24.     use LoggableTrait;
  25.     use SalesItemListTrait;
  26.     const NUMBER_PREFIX 'PO';
  27.     const ORDER_NUMBER_PREFIX 2;
  28.     const STATUS_DRAFT 'draft';
  29.     const STATUS_SENT 'sent';
  30.     const STATUS_REJECTED 'rejected';
  31.     /**
  32.      * @ORM\Id
  33.      * @ORM\Column(
  34.      *     type="integer"
  35.      * )
  36.      * @ORM\GeneratedValue(
  37.      *     strategy="AUTO"
  38.      * )
  39.      *
  40.      * @var int|null
  41.      */
  42.     protected ?int $id null;
  43.     /**
  44.      * @ORM\Column(
  45.      *     type="string",
  46.      *     length=3
  47.      * )
  48.      * @Assert\Choice(
  49.      *     callback={"\App\Model\Currency", "getCodeChoices"}
  50.      * )
  51.      *
  52.      * @var string
  53.      */
  54.     protected string $currency;
  55.     /**
  56.      * @ORM\OneToOne(
  57.      *     targetEntity="CompanyDocumentAddress",
  58.      *     cascade={"persist","remove"},
  59.      *     orphanRemoval=true
  60.      * )
  61.      * @ORM\JoinColumn(
  62.      *     name="delivery_address_id"
  63.      * )
  64.      *
  65.      * @Assert\Valid
  66.      *
  67.      * @var CompanyDocumentAddress|null
  68.      */
  69.     protected ?CompanyDocumentAddress $deliveryAddress null;
  70.     /**
  71.      * @ORM\Column(
  72.      *     name="delivery_date",
  73.      *     type="date",
  74.      *     nullable=true
  75.      * )
  76.      *
  77.      * @var DateTime|null
  78.      */
  79.     protected ?DateTime $deliveryDate null;
  80.     /**
  81.      * @ORM\OneToMany(
  82.      *     targetEntity="DocumentVersionPurchaseOrder",
  83.      *     mappedBy="purchaseOrder",
  84.      *     cascade={"persist","remove"}
  85.      * )
  86.      * @ORM\OrderBy({"created" = "DESC"})
  87.      *
  88.      * @var Collection<DocumentVersionPurchaseOrder>
  89.      */
  90.     protected Collection $documentVersions;
  91.     /**
  92.      * @ORM\OneToOne(
  93.      *     targetEntity="DocumentEndCustomer",
  94.      *     cascade={"persist","remove"},
  95.      *     orphanRemoval=true
  96.      * )
  97.      * @ORM\JoinColumn(
  98.      *     name="end_customer_id"
  99.      * )
  100.      *
  101.      * @Assert\Valid
  102.      *
  103.      * @var DocumentEndCustomer|null
  104.      */
  105.     protected ?DocumentEndCustomer $endCustomer null;
  106.     /**
  107.      * @ORM\OneToOne(
  108.      *     targetEntity="CompanyDocumentAddress",
  109.      *     cascade={"persist","remove"},
  110.      *     orphanRemoval=true
  111.      * )
  112.      * @ORM\JoinColumn(
  113.      *     name="invoicing_address_id"
  114.      * )
  115.      *
  116.      * @Assert\Valid
  117.      *
  118.      * @var CompanyDocumentAddress|null
  119.      */
  120.     protected ?CompanyDocumentAddress $invoicingAddress null;
  121.     /**
  122.      * @ORM\ManyToMany(
  123.      *     targetEntity="LogEntry",
  124.      *     cascade={"persist","remove"},
  125.      *     orphanRemoval=true
  126.      * )
  127.      * @ORM\JoinTable(
  128.      *     name="purchase_order_log_entry",
  129.      *     joinColumns={
  130.      *         @ORM\JoinColumn(
  131.      *             name="purchase_order_id",
  132.      *             referencedColumnName="id",
  133.      *             onDelete="cascade"
  134.      *         )
  135.      *     },
  136.      *     inverseJoinColumns={
  137.      *         @ORM\JoinColumn(
  138.      *             name="log_entry_id",
  139.      *             referencedColumnName="id",
  140.      *             unique=true,
  141.      *             onDelete="cascade"
  142.      *         )
  143.      *     }
  144.      * )
  145.      * @ORM\OrderBy({"time" = "ASC"})
  146.      *
  147.      * @Assert\Valid
  148.      *
  149.      * @var Collection<LogEntry>
  150.      */
  151.     protected $logEntries;
  152.     /**
  153.      * @ORM\Column(
  154.      *     type="string",
  155.      *     length=5000,
  156.      *     nullable=true
  157.      * )
  158.      *
  159.      * @var string|null
  160.      */
  161.     protected ?string $notes null;
  162.     /**
  163.      * @ORM\Column(
  164.      *     name="order_number",
  165.      *     type="integer",
  166.      *     nullable=true
  167.      * )
  168.      *
  169.      * @var int|null
  170.      */
  171.     protected ?int $orderNumber null;
  172.     /**
  173.      * @ORM\ManyToOne(
  174.      *     targetEntity="SalesCase",
  175.      *     inversedBy="purchaseOrders"
  176.      * )
  177.      * @ORM\JoinColumn(
  178.      *     name="sales_case_id",
  179.      *     referencedColumnName="id",
  180.      *     onDelete="cascade"
  181.      * )
  182.      * @Assert\NotNull
  183.      *
  184.      * @var SalesCase
  185.      */
  186.     protected SalesCase $salesCase;
  187.     /**
  188.      * @ORM\ManyToMany(
  189.      *     targetEntity="SalesItem",
  190.      *     cascade={"persist","remove"},
  191.      *     orphanRemoval=true
  192.      * )
  193.      * @ORM\JoinTable(
  194.      *     name="purchase_order_sales_item",
  195.      *     joinColumns={
  196.      *         @ORM\JoinColumn(
  197.      *             name="purchase_order_id",
  198.      *             referencedColumnName="id",
  199.      *             onDelete="cascade"
  200.      *         )
  201.      *     },
  202.      *     inverseJoinColumns={
  203.      *         @ORM\JoinColumn(
  204.      *             name="sales_item_id",
  205.      *             referencedColumnName="id",
  206.      *             unique=true,
  207.      *             onDelete="cascade"
  208.      *         )
  209.      *     }
  210.      * )
  211.      * @ORM\OrderBy({"position" = "ASC"})
  212.      *
  213.      * @Assert\Valid
  214.      *
  215.      * @var Collection<SalesItem>
  216.      */
  217.     protected Collection $salesItems;
  218.     /**
  219.      * @ORM\Column(
  220.      *     name="shipping_marks",
  221.      *     type="string",
  222.      *     length=500,
  223.      *     nullable=true
  224.      * )
  225.      *
  226.      * @var string|null
  227.      */
  228.     protected ?string $shippingMarks null;
  229.     /**
  230.      * @ORM\Column(
  231.      *     name="soc_number",
  232.      *     type="string",
  233.      *     length=50,
  234.      *     nullable=true
  235.      * )
  236.      *
  237.      * @var string|null
  238.      */
  239.     protected ?string $socNumber null;
  240.     /**
  241.      * @ORM\ManyToOne(
  242.      *     targetEntity="Supplier",
  243.      *     inversedBy="purchaseOrders"
  244.      * )
  245.      * @ORM\JoinColumn(
  246.      *     name="supplier_id",
  247.      *     referencedColumnName="id",
  248.      *     onDelete="SET NULL"
  249.      * )
  250.      *
  251.      * @Assert\NotNull
  252.      *
  253.      * @var Supplier
  254.      */
  255.     protected Supplier $supplier;
  256.     /**
  257.      * @ORM\OneToOne(
  258.      *     targetEntity="CompanyDocumentAddress",
  259.      *     cascade={"persist","remove"},
  260.      *     orphanRemoval=true
  261.      * )
  262.      * @ORM\JoinColumn(
  263.      *     name="supplier_address_id"
  264.      * )
  265.      * @Assert\Valid
  266.      *
  267.      * @var CompanyDocumentAddress|null
  268.      */
  269.     protected ?CompanyDocumentAddress $supplierAddress null;
  270.     /**
  271.      * @ORM\Column(
  272.      *     name="terms_of_delivery",
  273.      *     type="string",
  274.      *     length=500,
  275.      *     nullable=true
  276.      * )
  277.      *
  278.      * @var string|null
  279.      */
  280.     protected ?string $termsOfDelivery null;
  281.     /**
  282.      * @ORM\ManyToMany(
  283.      *     targetEntity="TermsOfPaymentRow",
  284.      *     cascade={"persist","remove"},
  285.      *     orphanRemoval=true
  286.      * )
  287.      * @ORM\JoinTable(
  288.      *     name="purchase_order_terms_of_payment_row",
  289.      *     joinColumns={
  290.      *         @ORM\JoinColumn(
  291.      *             name="purchase_order_id",
  292.      *             referencedColumnName="id",
  293.      *             onDelete="cascade"
  294.      *         )
  295.      *     },
  296.      *     inverseJoinColumns={
  297.      *         @ORM\JoinColumn(
  298.      *             name="terms_of_payment_row_id",
  299.      *             referencedColumnName="id",
  300.      *             unique=true,
  301.      *             onDelete="cascade"
  302.      *         )
  303.      *     }
  304.      * )
  305.      * @Assert\Valid
  306.      *
  307.      * @var Collection<TermsOfPaymentRow>
  308.      */
  309.     protected Collection $termsOfPaymentRows;
  310.     /**
  311.      * @ORM\Column(
  312.      *     type="text",
  313.      *     nullable=true
  314.      * )
  315.      *
  316.      * @var string|null
  317.      */
  318.     protected ?string $text null;
  319.     /**
  320.      * @ORM\Column(
  321.      *     type="string",
  322.      *     length=500,
  323.      *     nullable=true
  324.      * )
  325.      *
  326.      * @var string|null
  327.      */
  328.     protected ?string $transportation null;
  329.     /**
  330.      * @param SalesCase $salesCase
  331.      * @param Supplier $supplier
  332.      */
  333.     public function __construct(SalesCase $salesCaseSupplier $supplier)
  334.     {
  335.         $this->salesCase $salesCase;
  336.         $this->setSupplier($supplier);
  337.         $this->documentVersions = new ArrayCollection();
  338.         $this->logEntries = new ArrayCollection();
  339.         $this->salesItems = new ArrayCollection();
  340.         $this->termsOfPaymentRows = new ArrayCollection();
  341.     }
  342.     public function __clone(): void
  343.     {
  344.         $this->id null;
  345.         // Clone objects
  346.         if (null !== $this->deliveryAddress) {
  347.             $this->deliveryAddress = clone $this->deliveryAddress;
  348.         }
  349.         if (null !== $this->endCustomer) {
  350.             $this->endCustomer = clone $this->endCustomer;
  351.         }
  352.         if (null !== $this->invoicingAddress) {
  353.             $this->invoicingAddress = clone $this->invoicingAddress;
  354.         }
  355.         if (null !== $this->supplierAddress) {
  356.             $this->supplierAddress = clone $this->supplierAddress;
  357.         }
  358.         // Clone collections
  359.         $this->documentVersions = new ArrayCollection();
  360.         $this->logEntries = new ArrayCollection();
  361.         $salesItems = new ArrayCollection();
  362.         foreach ($this->salesItems as $salesItem) {
  363.             $salesItems->add(clone $salesItem);
  364.         }
  365.         $this->salesItems $salesItems;
  366.         $termsOfPaymentRows = new ArrayCollection();
  367.         foreach ($this->termsOfPaymentRows as $termsOfPaymentRow) {
  368.             $termsOfPaymentRows->add(clone $termsOfPaymentRow);
  369.         }
  370.         $this->termsOfPaymentRows $termsOfPaymentRows;
  371.     }
  372.     /**
  373.      * @param DocumentVersionPurchaseOrder $documentVersion
  374.      *
  375.      * @throws InvalidArgumentException
  376.      */
  377.     public function addDocumentVersion(DocumentVersionPurchaseOrder $documentVersion): void
  378.     {
  379.         if ($documentVersion->getPurchaseOrder() !== $this) {
  380.             throw new InvalidArgumentException('Document version has mismatching purchase order document.');
  381.         }
  382.         $this->documentVersions->add($documentVersion);
  383.     }
  384.     /**
  385.      * @param TermsOfPaymentRow $row
  386.      */
  387.     public function addTermsOfPaymentRow(TermsOfPaymentRow $row): void
  388.     {
  389.         if ($this->termsOfPaymentRows === null) {
  390.             $this->termsOfPaymentRows = new ArrayCollection();
  391.         }
  392.         $this->termsOfPaymentRows->add($row);
  393.     }
  394.     /**
  395.      * @return string
  396.      */
  397.     public function getCurrency(): string
  398.     {
  399.         return $this->currency;
  400.     }
  401.     /**
  402.      * @return CompanyDocumentAddress|null
  403.      */
  404.     public function getDeliveryAddress(): ?CompanyDocumentAddress
  405.     {
  406.         return $this->deliveryAddress;
  407.     }
  408.     /**
  409.      * @return DateTime|null
  410.      */
  411.     public function getDeliveryDate(): ?DateTime
  412.     {
  413.         return $this->deliveryDate;
  414.     }
  415.     /**
  416.      * @return Collection<DocumentVersionPurchaseOrder>
  417.      */
  418.     public function getDocumentVersions(): Collection
  419.     {
  420.         return $this->documentVersions;
  421.     }
  422.     /**
  423.      * @return DocumentEndCustomer|null
  424.      */
  425.     public function getEndCustomer(): ?DocumentEndCustomer
  426.     {
  427.         return $this->endCustomer;
  428.     }
  429.     /**
  430.      * @return float
  431.      *
  432.      * @throws Exception
  433.      */
  434.     public function getGrossMargin(): float
  435.     {
  436.         throw new Exception('Purchase orders do not have gross margins.');
  437.     }
  438.     /**
  439.      * @return int|null
  440.      */
  441.     public function getId(): ?int
  442.     {
  443.         return $this->id;
  444.     }
  445.     /**
  446.      * @return CompanyDocumentAddress|null
  447.      */
  448.     public function getInvoicingAddress(): ?CompanyDocumentAddress
  449.     {
  450.         return $this->invoicingAddress;
  451.     }
  452.     /**
  453.      * @return null|string
  454.      */
  455.     public function getNotes(): ?string
  456.     {
  457.         return $this->notes;
  458.     }
  459.     /**
  460.      * @return string
  461.      */
  462.     public function getNumber(): string
  463.     {
  464.         return self::NUMBER_PREFIX str_pad($this->salesCase->getId(), 6'0'STR_PAD_LEFT)
  465.         . '-'
  466.         . ($this->isDraft() ? 'DRAFT' self::ORDER_NUMBER_PREFIX str_pad($this->getOrderNumber(), 5'0'STR_PAD_LEFT));
  467.     }
  468.     /**
  469.      * @return int|null
  470.      */
  471.     public function getOrderNumber(): ?int
  472.     {
  473.         return $this->orderNumber;
  474.     }
  475.     /**
  476.      * @return string
  477.      */
  478.     public function getPreviewFileName(): string
  479.     {
  480.         return 'Purchase Order - ' $this->getNumber() . ' - Preview ' date('Y-m-d') . '.pdf';
  481.     }
  482.     /**
  483.      * @return SalesCase
  484.      */
  485.     public function getSalesCase(): SalesCase
  486.     {
  487.         return $this->salesCase;
  488.     }
  489.     /**
  490.      * @return null|string
  491.      */
  492.     public function getShippingMarks(): ?string
  493.     {
  494.         return $this->shippingMarks;
  495.     }
  496.     /**
  497.      * @return null|string
  498.      */
  499.     public function getSocNumber(): ?string
  500.     {
  501.         return $this->socNumber;
  502.     }
  503.     /**
  504.      * @return array<string>
  505.      */
  506.     public static function getStatusChoices(): array
  507.     {
  508.         return [
  509.             self::STATUS_DRAFT,
  510.             self::STATUS_SENT,
  511.             self::STATUS_REJECTED,
  512.         ];
  513.     }
  514.     /**
  515.      * @return Supplier
  516.      */
  517.     public function getSupplier(): Supplier
  518.     {
  519.         return $this->supplier;
  520.     }
  521.     /**
  522.      * @return CompanyDocumentAddress|null
  523.      */
  524.     public function getSupplierAddress(): ?CompanyDocumentAddress
  525.     {
  526.         return $this->supplierAddress;
  527.     }
  528.     /**
  529.      * @return null|string
  530.      */
  531.     public function getTermsOfDelivery(): ?string
  532.     {
  533.         return $this->termsOfDelivery;
  534.     }
  535.     /**
  536.      * @return Collection<TermsOfPaymentRow>
  537.      */
  538.     public function getTermsOfPaymentRows(): Collection
  539.     {
  540.         return $this->termsOfPaymentRows;
  541.     }
  542.     /**
  543.      * @return null|string
  544.      */
  545.     public function getText(): ?string
  546.     {
  547.         return $this->text;
  548.     }
  549.     /**
  550.      * @param bool $defaultCurrency
  551.      *
  552.      * @return int
  553.      *
  554.      * @throws Exception
  555.      */
  556.     public function getTotalPurchaseValue(bool $defaultCurrency false): int
  557.     {
  558.         $total 0;
  559.         $noPurchasePrice = [];
  560.         foreach ($this->salesItems as $salesItem) {
  561.             if (null !== $purchasePrice $salesItem->getPurchasePrice($defaultCurrency)) {
  562.                 $temp $purchasePrice $salesItem->getQuantity();
  563.                 if (null !== $discountValue $salesItem->getDiscountValue()) {
  564.                     $temp -= $discountValue;
  565.                 } elseif (null !== $discountPercentage $salesItem->getDiscountPercentage()) {
  566.                     $temp -= round($temp $discountPercentage 100);
  567.                 }
  568.                 $total += $temp;
  569.             } elseif ($this->getSupplier()->isGrossMarginPurchasesZero()) {
  570.                 $total += 0;
  571.             } else {
  572.                 $noPurchasePrice[] = $salesItem->getPositionDisplay();
  573.             }
  574.         }
  575.         if (count($noPurchasePrice) > 0) {
  576.             throw new Exception('Purchase order ' $this->getNumber() . ', positions ' implode(', '$noPurchasePrice) . ' have no purchase price.');
  577.         }
  578.         return $total;
  579.     }
  580.     /**
  581.      * @param bool $defaultCurrency
  582.      *
  583.      * @return array
  584.      */
  585.     public function getTotalPurchaseValueDisplay(bool $defaultCurrency false): array
  586.     {
  587.         $value null;
  588.         $error null;
  589.         try {
  590.             $value $this->getTotalPurchaseValue($defaultCurrency);
  591.         } catch (Exception $e) {
  592.             $error $e->getMessage();
  593.         }
  594.         return [$value$error];
  595.     }
  596.     /**
  597.      * @return null|string
  598.      */
  599.     public function getTransportation(): ?string
  600.     {
  601.         return $this->transportation;
  602.     }
  603.     /**
  604.      * @return bool
  605.      */
  606.     public function isDraft(): bool
  607.     {
  608.         return $this->getStatus() == self::STATUS_DRAFT;
  609.     }
  610.     /**
  611.      * @return bool
  612.      */
  613.     public function isRejected(): bool
  614.     {
  615.         return $this->getStatus() == self::STATUS_REJECTED;
  616.     }
  617.     /**
  618.      * @return bool
  619.      */
  620.     public function isSent(): bool
  621.     {
  622.         return $this->getStatus() == self::STATUS_SENT;
  623.     }
  624.     /**
  625.      * @ORM\PostLoad
  626.      */
  627.     public function postLoadSetCurrency(): void
  628.     {
  629.         foreach ($this->salesItems as $salesItem) {
  630.             $salesItem->setCurrency($this->getCurrency());
  631.             if (null !== $exchangeRate $this->getCurrencyExchangeRate()) {
  632.                 $salesItem->setCurrencyExchangeRate($exchangeRate->getRate());
  633.             }
  634.         }
  635.     }
  636.     /**
  637.      * @param TermsOfPaymentRow $row
  638.      */
  639.     public function removeTermsOfPaymentRow(TermsOfPaymentRow $row): void
  640.     {
  641.         if ($this->termsOfPaymentRows !== null) {
  642.             $this->termsOfPaymentRows->removeElement($row);
  643.         }
  644.     }
  645.     /**
  646.      * @param CompanyDocumentAddress|null $deliveryAddress
  647.      */
  648.     public function setDeliveryAddress(?CompanyDocumentAddress $deliveryAddress): void
  649.     {
  650.         $this->deliveryAddress $deliveryAddress;
  651.     }
  652.     /**
  653.      * @param DateTime|null $deliveryDate
  654.      */
  655.     public function setDeliveryDate(?DateTime $deliveryDate): void
  656.     {
  657.         $this->deliveryDate $deliveryDate;
  658.     }
  659.     /**
  660.      * @param DocumentEndCustomer|null $endCustomer
  661.      */
  662.     public function setEndCustomer(?DocumentEndCustomer $endCustomer): void
  663.     {
  664.         $this->endCustomer $endCustomer;
  665.     }
  666.     /**
  667.      * @param CompanyDocumentAddress|null $invoicingAddress
  668.      */
  669.     public function setInvoicingAddress(?CompanyDocumentAddress $invoicingAddress): void
  670.     {
  671.         $this->invoicingAddress $invoicingAddress;
  672.     }
  673.     /**
  674.      * @param string|null $notes
  675.      */
  676.     public function setNotes(?string $notes): void
  677.     {
  678.         $this->notes $notes;
  679.     }
  680.     /**
  681.      * @param int|null $orderNumber
  682.      */
  683.     public function setOrderNumber(?int $orderNumber): void
  684.     {
  685.         $this->orderNumber $orderNumber;
  686.     }
  687.     /**
  688.      * @param string|null $shippingMarks
  689.      */
  690.     public function setShippingMarks(?string $shippingMarks): void
  691.     {
  692.         $this->shippingMarks $shippingMarks;
  693.     }
  694.     /**
  695.      * @param string|null $socNumber
  696.      */
  697.     public function setSocNumber(?string $socNumber): void
  698.     {
  699.         $this->socNumber $socNumber;
  700.     }
  701.     /**
  702.      * @param Supplier $supplier
  703.      */
  704.     private function setSupplier(Supplier $supplier): void
  705.     {
  706.         $this->supplier $supplier;
  707.         $this->currency $supplier->getCurrency();
  708.         $this->setSupplierAddress(
  709.             CompanyDocumentAddress::create($supplier)
  710.         );
  711.     }
  712.     /**
  713.      * @param CompanyDocumentAddress|null $supplierAddress
  714.      */
  715.     public function setSupplierAddress(?CompanyDocumentAddress $supplierAddress): void
  716.     {
  717.         $this->supplierAddress $supplierAddress;
  718.     }
  719.     /**
  720.      * @param string|null $termsOfDelivery
  721.      */
  722.     public function setTermsOfDelivery(?string $termsOfDelivery): void
  723.     {
  724.         $this->termsOfDelivery $termsOfDelivery;
  725.     }
  726.     /**
  727.      * @param string|null $text
  728.      */
  729.     public function setText(?string $text): void
  730.     {
  731.         $this->text $text;
  732.     }
  733.     /**
  734.      * @param string|null $transportation
  735.      */
  736.     public function setTransportation(?string $transportation): void
  737.     {
  738.         $this->transportation $transportation;
  739.     }
  740.     /**
  741.      * @Assert\Callback
  742.      *
  743.      * @param ExecutionContextInterface $context
  744.      */
  745.     public function validateCurrencyExchangeRate(ExecutionContextInterface $context): void
  746.     {
  747.         if ($this->currencyExchangeRate !== null && $this->currencyExchangeRateDate === null) {
  748.             $context->buildViolation('Please define the date for the exchange rate')
  749.                 ->atPath('currencyExchangeRate.date')
  750.                 ->addViolation()
  751.             ;
  752.         }
  753.     }
  754.     /**
  755.      * @Assert\Callback
  756.      *
  757.      * @param ExecutionContextInterface $context
  758.      */
  759.     public function validateVat(ExecutionContextInterface $context): void
  760.     {
  761.         if ($this->id === null && $this->vat === null) {
  762.             $context->buildViolation('Please define VAT')
  763.                 ->atPath('vat')
  764.                 ->addViolation()
  765.             ;
  766.         }
  767.     }
  768. }