src/Entity/AbstractInvoice.php line 44

Open in your IDE?
  1. <?php
  2. namespace App\Entity;
  3. use App\Exception\CurrencyException;
  4. use App\Exception\InvalidArgumentException;
  5. use App\Model\Country;
  6. use DateTime;
  7. use Doctrine\Common\Collections\ArrayCollection;
  8. use Doctrine\Common\Collections\Collection;
  9. use Doctrine\ORM\Mapping as ORM;
  10. use Exception;
  11. use Symfony\Component\Validator\Constraints as Assert;
  12. use Symfony\Component\Validator\Context\ExecutionContextInterface;
  13. /**
  14.  * @ORM\Entity(
  15.  *     repositoryClass="App\Repository\InvoiceRepository"
  16.  * )
  17.  * @ORM\Table(
  18.  *     name="invoice",
  19.  *     options={"collate"="utf8_swedish_ci"},
  20.  *     uniqueConstraints={
  21.  *         @ORM\UniqueConstraint(
  22.  *             name="invoice_order_number",
  23.  *             columns={"entity","order_number"}
  24.  *         )
  25.  *     }
  26.  * )
  27.  * @ORM\InheritanceType("SINGLE_TABLE")
  28.  * @ORM\DiscriminatorColumn(
  29.  *     name="entity",
  30.  *     type="string",
  31.  *     length=50
  32.  * )
  33.  * @ORM\DiscriminatorMap({
  34.  *     "invoice"="Invoice",
  35.  *     "credit"="CreditInvoice",
  36.  *     "proforma"="ProformaInvoice",
  37.  *     "down_payment"="DownPaymentInvoice"
  38.  * })
  39.  * @ORM\HasLifecycleCallbacks
  40.  */
  41. abstract class AbstractInvoice implements EntityInterfaceLoggableInterfaceSalesDocumentInterfaceSalesDocumentDiscountInterface
  42. {
  43.     use DocumentTrait;
  44.     use LoggableTrait;
  45.     use SalesItemListTrait;
  46.     use VatJustificationTrait;
  47.     const STATUS_DRAFT 'draft';
  48.     const STATUS_INVOICED 'invoiced';
  49.     /**
  50.      * @ORM\Id
  51.      * @ORM\Column(
  52.      *     type="integer"
  53.      * )
  54.      * @ORM\GeneratedValue(
  55.      *     strategy="AUTO"
  56.      * )
  57.      *
  58.      * @var int|null
  59.      */
  60.     protected ?int $id null;
  61.     /**
  62.      * @ORM\OneToOne(
  63.      *     targetEntity="CompanyDocumentAddress",
  64.      *     cascade={"persist","remove"},
  65.      *     orphanRemoval=true
  66.      * )
  67.      * @ORM\JoinColumn(
  68.      *     name="buyer_id"
  69.      * )
  70.      *
  71.      * @Assert\Valid
  72.      *
  73.      * @var CompanyDocumentAddress|null
  74.      */
  75.     protected ?CompanyDocumentAddress $buyer null;
  76.     /**
  77.      * @ORM\Column(
  78.      *     name="commodity_code",
  79.      *     type="string",
  80.      *     length=50,
  81.      *     nullable=true
  82.      * )
  83.      *
  84.      * @var string|null
  85.      */
  86.     protected ?string $commodityCode null;
  87.     /**
  88.      * @ORM\Column(
  89.      *     name="confirmation_of_exit",
  90.      *     type="boolean",
  91.      *     nullable=true
  92.      * )
  93.      *
  94.      * @var bool|null
  95.      */
  96.     protected ?bool $confirmationOfExit null;
  97.     /**
  98.      * @ORM\Column(
  99.      *     name="confirmation_of_exit_set",
  100.      *     type="boolean"
  101.      * )
  102.      *
  103.      * @var bool
  104.      */
  105.     protected bool $confirmationOfExitSet false;
  106.     /**
  107.      * This is used when CoE is set in a draft invoice, because not set and value NULL must be handled separately.
  108.      *
  109.      * @var string|null
  110.      */
  111.     protected ?string $confirmationOfExitTemp null;
  112.     /**
  113.      * @ORM\OneToOne(
  114.      *     targetEntity="CompanyDocumentAddress",
  115.      *     cascade={"persist","remove"},
  116.      *     orphanRemoval=true
  117.      * )
  118.      * @ORM\JoinColumn(
  119.      *     name="consignee_id"
  120.      * )
  121.      *
  122.      * @Assert\Valid
  123.      *
  124.      * @var CompanyDocumentAddress|null
  125.      */
  126.     protected ?CompanyDocumentAddress $consignee null;
  127.     /**
  128.      * @ORM\Column(
  129.      *     name="customs_data_per_item",
  130.      *     type="boolean"
  131.      * )
  132.      *
  133.      * @var bool
  134.      */
  135.     protected bool $customsDataPerItem false;
  136.     /**
  137.      * @ORM\Column(
  138.      *     name="country_of_origin",
  139.      *     type="string",
  140.      *     length=100,
  141.      *     nullable=true
  142.      * )
  143.      *
  144.      * @var string|null
  145.      */
  146.     protected ?string $countryOfOrigin 'EC - Finland';
  147.     /**
  148.      * @ORM\OneToOne(
  149.      *     targetEntity="CompanyDocumentAddress",
  150.      *     cascade={"persist","remove"},
  151.      *     orphanRemoval=true
  152.      * )
  153.      * @ORM\JoinColumn(
  154.      *     name="delivery_address_id"
  155.      * )
  156.      *
  157.      * @Assert\Valid
  158.      *
  159.      * @var CompanyDocumentAddress|null
  160.      */
  161.     protected ?CompanyDocumentAddress $deliveryAddress null;
  162.     /**
  163.      * @ORM\Column(
  164.      *     name="delivery_date",
  165.      *     type="date",
  166.      *     nullable=true
  167.      * )
  168.      *
  169.      * @var DateTime|null
  170.      */
  171.     protected ?DateTime $deliveryDate null;
  172.     /**
  173.      * @ORM\Column(
  174.      *     name="description_of_goods",
  175.      *     type="string",
  176.      *     length=1000,
  177.      *     nullable=true
  178.      * )
  179.      *
  180.      * @var string|null
  181.      */
  182.     protected ?string $descriptionOfGoods null;
  183.     /**
  184.      * @ORM\OneToMany(
  185.      *     targetEntity="DocumentVersionInvoice",
  186.      *     mappedBy="invoice",
  187.      *     cascade={"persist","remove"}
  188.      * )
  189.      * @ORM\OrderBy({"created" = "DESC"})
  190.      *
  191.      * @var Collection<DocumentVersionInvoice>
  192.      */
  193.     protected Collection $documentVersions;
  194.     /**
  195.      * @ORM\Column(
  196.      *     type="json",
  197.      *     nullable=true
  198.      * )
  199.      *
  200.      * @var array|null
  201.      */
  202.     protected ?array $electronicInvoiceResponse null;
  203.     /**
  204.      * @ORM\Column(
  205.      *     name="final_destination",
  206.      *     type="string",
  207.      *     length=5,
  208.      *     nullable=true
  209.      * )
  210.      *
  211.      * @var string|null
  212.      */
  213.     protected ?string $finalDestination null;
  214.     /**
  215.      * @ORM\OneToOne(
  216.      *     targetEntity="CompanyDocumentAddress",
  217.      *     cascade={"persist","remove"},
  218.      *     orphanRemoval=true
  219.      * )
  220.      * @ORM\JoinColumn(
  221.      *     name="invoicing_address_id"
  222.      * )
  223.      *
  224.      * @Assert\Valid
  225.      *
  226.      * @var CompanyDocumentAddress|null
  227.      */
  228.     protected ?CompanyDocumentAddress $invoicingAddress null;
  229.     /**
  230.      * @ORM\ManyToMany(
  231.      *     targetEntity="LogEntry",
  232.      *     cascade={"persist","remove"},
  233.      *     orphanRemoval=true
  234.      * )
  235.      * @ORM\JoinTable(
  236.      *     name="invoice_log_entry",
  237.      *     joinColumns={
  238.      *         @ORM\JoinColumn(
  239.      *             name="invoice_id",
  240.      *             referencedColumnName="id",
  241.      *             onDelete="cascade"
  242.      *         )
  243.      *     },
  244.      *     inverseJoinColumns={
  245.      *         @ORM\JoinColumn(
  246.      *             name="log_entry_id",
  247.      *             referencedColumnName="id",
  248.      *             unique=true,
  249.      *             onDelete="cascade"
  250.      *         )
  251.      *     }
  252.      * )
  253.      * @ORM\OrderBy({"time" = "ASC"})
  254.      *
  255.      * @Assert\Valid
  256.      *
  257.      * @var Collection<LogEntry>
  258.      */
  259.     protected $logEntries;
  260.     /**
  261.      * @ORM\Column(
  262.      *     name="marks_and_numbers",
  263.      *     type="string",
  264.      *     length=100,
  265.      *     nullable=true
  266.      * )
  267.      *
  268.      * @var string|null
  269.      */
  270.     protected ?string $marksAndNumbers null;
  271.     /**
  272.      * @ORM\Column(
  273.      *     type="string",
  274.      *     length=1,
  275.      *     nullable=true
  276.      * )
  277.      *
  278.      * @var string|null
  279.      */
  280.     protected ?string $modeOfTransport null;
  281.     /**
  282.      * @ORM\Column(
  283.      *     type="string",
  284.      *     length=2,
  285.      *     nullable=true
  286.      * )
  287.      * @Assert\Choice(
  288.      *     callback="getNatureOfTransactionChoices"
  289.      * )
  290.      *
  291.      * @var string|null
  292.      */
  293.     protected ?string $natureOfTransaction null;
  294.     /**
  295.      * @ORM\Column(
  296.      *     type="string",
  297.      *     length=5000,
  298.      *     nullable=true
  299.      * )
  300.      *
  301.      * @var string|null
  302.      */
  303.     protected ?string $notes null;
  304.     /**
  305.      * @ORM\Column(
  306.      *     name="order_number",
  307.      *     type="integer",
  308.      *     nullable=true
  309.      * )
  310.      *
  311.      * @var int|null
  312.      */
  313.     protected ?int $orderNumber null;
  314.     /**
  315.      * @ORM\Column(
  316.      *     name="place_of_loading",
  317.      *     type="string",
  318.      *     length=200,
  319.      *     nullable=true
  320.      * )
  321.      *
  322.      * @var string|null
  323.      */
  324.     protected ?string $placeOfLoading null;
  325.     /**
  326.      * @ORM\ManyToOne(
  327.      *     targetEntity="SalesCase",
  328.      *     inversedBy="invoices"
  329.      * )
  330.      * @ORM\JoinColumn(
  331.      *     name="sales_case_id",
  332.      *     referencedColumnName="id",
  333.      *     onDelete="cascade"
  334.      * )
  335.      *
  336.      * @Assert\NotNull
  337.      *
  338.      * @var SalesCase
  339.      */
  340.     protected SalesCase $salesCase;
  341.     /**
  342.      * @ORM\ManyToMany(
  343.      *     targetEntity="SalesItem",
  344.      *     cascade={"persist","remove"},
  345.      *     orphanRemoval=true,
  346.      *     fetch="EXTRA_LAZY"
  347.      * )
  348.      * @ORM\JoinTable(
  349.      *     name="invoice_sales_item",
  350.      *     joinColumns={
  351.      *         @ORM\JoinColumn(
  352.      *             name="invoice_id",
  353.      *             referencedColumnName="id",
  354.      *             onDelete="cascade"
  355.      *         )
  356.      *     },
  357.      *     inverseJoinColumns={
  358.      *         @ORM\JoinColumn(
  359.      *             name="sales_item_id",
  360.      *             referencedColumnName="id",
  361.      *             unique=true,
  362.      *             onDelete="cascade"
  363.      *         )
  364.      *     }
  365.      * )
  366.      * @ORM\OrderBy({"position" = "ASC"})
  367.      *
  368.      * @Assert\Valid
  369.      *
  370.      * @var Collection<SalesItem>
  371.      */
  372.     protected $salesItems;
  373.     /**
  374.      * @var bool
  375.      */
  376.     protected bool $salesItemsPopulated false;
  377.     /**
  378.      * @ORM\Column(
  379.      *     name="terms_of_delivery",
  380.      *     type="string",
  381.      *     length=500,
  382.      *     nullable=true
  383.      * )
  384.      *
  385.      * @var string|null
  386.      */
  387.     protected ?string $termsOfDelivery null;
  388.     /**
  389.      * @ORM\ManyToMany(
  390.      *     targetEntity="TermsOfPaymentRow",
  391.      *     cascade={"persist","remove"},
  392.      *     orphanRemoval=true
  393.      * )
  394.      * @ORM\JoinTable(
  395.      *     name="invoice_terms_of_payment_row",
  396.      *     joinColumns={
  397.      *         @ORM\JoinColumn(
  398.      *             name="invoice_id",
  399.      *             referencedColumnName="id",
  400.      *             onDelete="cascade"
  401.      *         )
  402.      *     },
  403.      *     inverseJoinColumns={
  404.      *         @ORM\JoinColumn(
  405.      *             name="terms_of_payment_row_id",
  406.      *             referencedColumnName="id",
  407.      *             unique=true,
  408.      *             onDelete="cascade"
  409.      *         )
  410.      *     }
  411.      * )
  412.      *
  413.      * @Assert\Valid
  414.      *
  415.      * @var Collection<TermsOfPaymentRow>
  416.      */
  417.     protected $termsOfPaymentRows;
  418.     /**
  419.      * @ORM\Column(
  420.      *     type="string",
  421.      *     length=2000,
  422.      *     nullable=true
  423.      * )
  424.      *
  425.      * @var string|null
  426.      */
  427.     protected ?string $text null;
  428.     /**
  429.      * @ORM\Column(
  430.      *     name="total_volume",
  431.      *     type="string",
  432.      *     length=100,
  433.      *     nullable=true
  434.      * )
  435.      *
  436.      * @var string|null
  437.      */
  438.     protected ?string $totalVolume null;
  439.     /**
  440.      * @ORM\Column(
  441.      *     name="total_weight_gross",
  442.      *     type="integer",
  443.      *     nullable=true
  444.      * )
  445.      * @Assert\Range(
  446.      *     min=0
  447.      * )
  448.      *
  449.      * @var int|null
  450.      */
  451.     protected ?int $totalWeightGross null;
  452.     /**
  453.      * @ORM\Column(
  454.      *     name="total_weight_net",
  455.      *     type="integer",
  456.      *     nullable=true
  457.      * )
  458.      * @Assert\Range(
  459.      *     min=0
  460.      * )
  461.      *
  462.      * @var int|null
  463.      */
  464.     protected ?int $totalWeightNet null;
  465.     /**
  466.      * @ORM\Column(
  467.      *     type="string",
  468.      *     length=500,
  469.      *     nullable=true
  470.      * )
  471.      *
  472.      * @var string|null
  473.      */
  474.     protected ?string $transportation null;
  475.     /**
  476.      * @param SalesCase $salesCase
  477.      */
  478.     public function __construct(SalesCase $salesCase)
  479.     {
  480.         $this->salesCase $salesCase;
  481.         $this->documentVersions = new ArrayCollection();
  482.         $this->logEntries = new ArrayCollection();
  483.         $this->salesItems = new ArrayCollection();
  484.         $this->termsOfPaymentRows = new ArrayCollection();
  485.     }
  486.     /**
  487.      * @param DocumentVersionInvoice $documentVersion
  488.      *
  489.      * @throws InvalidArgumentException
  490.      */
  491.     public function addDocumentVersion(DocumentVersionInvoice $documentVersion)
  492.     {
  493.         if ($documentVersion->getInvoice() !== $this) {
  494.             throw new InvalidArgumentException('Document version has mismatching invoice document.');
  495.         }
  496.         $this->documentVersions->add($documentVersion);
  497.     }
  498.     /**
  499.      * @param TermsOfPaymentRow $row
  500.      */
  501.     public function addTermsOfPaymentRow(TermsOfPaymentRow $row): void
  502.     {
  503.         if ($this->termsOfPaymentRows === null) {
  504.             $this->termsOfPaymentRows = new ArrayCollection();
  505.         }
  506.         $this->termsOfPaymentRows->add($row);
  507.     }
  508.     /**
  509.      * @return CompanyDocumentAddress|null
  510.      */
  511.     public function getBuyer(): ?CompanyDocumentAddress
  512.     {
  513.         return $this->buyer;
  514.     }
  515.     /**
  516.      * @return string|null
  517.      */
  518.     public function getCommodityCode(): ?string
  519.     {
  520.         return $this->commodityCode;
  521.     }
  522.     /**
  523.      * @return bool|null
  524.      */
  525.     public function getConfirmationOfExit(): ?bool
  526.     {
  527.         return $this->confirmationOfExit;
  528.     }
  529.     /**
  530.      * @return string|null
  531.      */
  532.     public function getConfirmationOfExitTemp(): ?string
  533.     {
  534.         if ($this->confirmationOfExitSet) {
  535.             if ($this->confirmationOfExit === false) {
  536.                 return 'waiting';
  537.             } elseif ($this->confirmationOfExit === true) {
  538.                 return 'received';
  539.             } else {
  540.                 return 'not_required';
  541.             }
  542.         }
  543.         return null;
  544.     }
  545.     /**
  546.      * @return CompanyDocumentAddress|null
  547.      */
  548.     public function getConsignee(): ?CompanyDocumentAddress
  549.     {
  550.         return $this->consignee;
  551.     }
  552.     /**
  553.      * @return null|string
  554.      */
  555.     public function getCountryOfOrigin(): ?string
  556.     {
  557.         return $this->countryOfOrigin;
  558.     }
  559.     /**
  560.      * @return CompanyDocumentAddress|null
  561.      */
  562.     public function getDeliveryAddress(): ?CompanyDocumentAddress
  563.     {
  564.         return $this->deliveryAddress;
  565.     }
  566.     /**
  567.      * @return DateTime|null
  568.      */
  569.     public function getDeliveryDate(): ?DateTime
  570.     {
  571.         return $this->deliveryDate;
  572.     }
  573.     /**
  574.      * @return null|string
  575.      */
  576.     public function getDescriptionOfGoods(): ?string
  577.     {
  578.         return $this->descriptionOfGoods;
  579.     }
  580.     /**
  581.      * @return DocumentVersionInvoice|null
  582.      */
  583.     public function getDocumentVersion(): ?DocumentVersionInvoice
  584.     {
  585.         if ($this->documentVersions->count() > 0) {
  586.             return $this->documentVersions->first();
  587.         }
  588.         return null;
  589.     }
  590.     /**
  591.      * @return ArrayCollection|DocumentVersionInvoice[]
  592.      */
  593.     public function getDocumentVersions()
  594.     {
  595.         return $this->documentVersions;
  596.     }
  597.     /**
  598.      * @return DateTime|null
  599.      *
  600.      * @throws Exception
  601.      */
  602.     public function getDueDate(): ?DateTime
  603.     {
  604.         if (count($this->getTermsOfPaymentRows()) > 0) {
  605.             /* @var TermsOfPaymentRow $first */
  606.             $first $this->termsOfPaymentRows->first();
  607.             if (null !== $dueDate $first->getDueDate()) {
  608.                 return $dueDate;
  609.             }
  610.             if (null !== $days $first->getDays()) {
  611.                 if (null !== $deliveryDate $this->getDeliveryDate()) {
  612.                     $dueDate = clone $deliveryDate;
  613.                     $dueDate->add(new \DateInterval('P' $days 'D'));
  614.                     return $dueDate;
  615.                 }
  616.             }
  617.         }
  618.         return null;
  619.     }
  620.     /**
  621.      * @return DateTime|null
  622.      *
  623.      * @throws Exception
  624.      */
  625.     public function getDueDateAfterDelivery(): ?DateTime
  626.     {
  627.         if (null !== $deliveryDate $this->getDeliveryDate()) {
  628.             if (count($this->getTermsOfPaymentRows()) > 0) {
  629.                 /* @var TermsOfPaymentRow $first */
  630.                 $first $this->termsOfPaymentRows->first();
  631.                 if (null !== $days $first->getDays()) {
  632.                     $dueDate = clone $deliveryDate;
  633.                     $dueDate->add(new \DateInterval('P' $days 'D'));
  634.                     return $dueDate;
  635.                 }
  636.             }
  637.         }
  638.         return null;
  639.     }
  640.     /**
  641.      * @return string|null
  642.      */
  643.     public function getElectronicInvoiceId(): ?string
  644.     {
  645.         if (null !== $response $this->electronicInvoiceResponse) {
  646.             return $response['id'];
  647.         }
  648.         return null;
  649.     }
  650.     /**
  651.      * @return array|null
  652.      */
  653.     public function getElectronicInvoiceResponse(): ?array
  654.     {
  655.         return $this->electronicInvoiceResponse;
  656.     }
  657.     /**
  658.      * @return DateTime|null
  659.      *
  660.      * @throws Exception
  661.      */
  662.     public function getElectronicInvoiceSent(): ?DateTime
  663.     {
  664.         if (null !== $response $this->electronicInvoiceResponse) {
  665.             return new DateTime($response['created_at']);
  666.         }
  667.         return null;
  668.     }
  669.     /**
  670.      * @return string|null
  671.      */
  672.     public function getElectronicInvoiceStatus(): ?string
  673.     {
  674.         if (null !== $response $this->electronicInvoiceResponse) {
  675.             return $response['status'];
  676.         }
  677.         return null;
  678.     }
  679.     /**
  680.      * @return null|string
  681.      */
  682.     public function getFinalDestination(): ?string
  683.     {
  684.         return $this->finalDestination;
  685.     }
  686.     /**
  687.      * @return string|null
  688.      */
  689.     public function getFinalDestinationName(): ?string
  690.     {
  691.         if ($this->finalDestination !== null) {
  692.             $country = new Country($this->finalDestination);
  693.             return $country->getName();
  694.         }
  695.         return null;
  696.     }
  697.     /**
  698.      * @return float
  699.      *
  700.      * @throws CurrencyException
  701.      * @throws Exception
  702.      */
  703.     public function getGrossMargin(): float
  704.     {
  705.         $purchasePrices = [];
  706.         foreach ($this->salesCase->getPurchaseOrders() as $purchaseOrder) {
  707.             if ($purchaseOrder->isSent()) {
  708.                 foreach ($purchaseOrder->getSalesItems() as $salesItem) {
  709.                     if (null !== $purchasePrice $salesItem->getPurchasePrice()) {
  710.                         $purchasePrices[$salesItem->getCode()] = $purchasePrice;
  711.                     }
  712.                 }
  713.             }
  714.         }
  715.         $totalCostOfSales 0;
  716.         $noPurchasePrice = [];
  717.         foreach ($this->salesItems as $salesItem) {
  718.             if (array_key_exists($salesItem->getCode(), $purchasePrices)) {
  719.                 $totalCostOfSales += $purchasePrices[$salesItem->getCode()] * $salesItem->getQuantity();
  720.             } else {
  721.                 $noPurchasePrice[] = $salesItem->getPositionDisplay();
  722.             }
  723.         }
  724.         if (count($noPurchasePrice) > 0) {
  725.             throw new Exception('No purchase price could be found in POs for positions ' implode(', '$noPurchasePrice) . '.');
  726.         }
  727.         if (($totalSales $this->getValueNet()) <= 0) {
  728.             throw new Exception('The OC has no total sales value.');
  729.         }
  730.         return ($totalSales $totalCostOfSales) / $totalSales;
  731.     }
  732.     /**
  733.      * @return int|null
  734.      */
  735.     public function getId(): ?int
  736.     {
  737.         return $this->id;
  738.     }
  739.     /**
  740.      * @return CompanyDocumentAddress|null
  741.      */
  742.     public function getInvoicingAddress(): ?CompanyDocumentAddress
  743.     {
  744.         return $this->invoicingAddress;
  745.     }
  746.     /**
  747.      * @return null|string
  748.      */
  749.     public function getMarksAndNumbers(): ?string
  750.     {
  751.         return $this->marksAndNumbers;
  752.     }
  753.     /**
  754.      * @return null|string
  755.      */
  756.     public function getModeOfTransport(): ?string
  757.     {
  758.         return $this->modeOfTransport;
  759.     }
  760.     /**
  761.      * @return string[]
  762.      */
  763.     public static function getModeOfTransportChoices(): array
  764.     {
  765.         return [
  766.             '1''2''3''4''5''7''8''9',
  767.         ];
  768.     }
  769.     /**
  770.      * @return string|null
  771.      */
  772.     public function getNatureOfTransaction(): ?string
  773.     {
  774.         return $this->natureOfTransaction;
  775.     }
  776.     /**
  777.      * @return string[]
  778.      */
  779.     public static function getNatureOfTransactionChoices(): array
  780.     {
  781.         return [
  782.             '11''12',
  783.             '21''22''23',
  784.             '31''32''33''34',
  785.             '41''42',
  786.             '51''52',
  787.             '60',
  788.             '71''72',
  789.             '80',
  790.             '91''99',
  791.         ];
  792.     }
  793.     /**
  794.      * @return null|string
  795.      */
  796.     public function getNotes(): ?string
  797.     {
  798.         return $this->notes;
  799.     }
  800.     /**
  801.      * @param string $numberPrefix
  802.      * @param int    $orderNumberPrefix
  803.      *
  804.      * @return string
  805.      */
  806.     protected function getNumberParent(string $numberPrefixint $orderNumberPrefix): string
  807.     {
  808.         return $numberPrefix str_pad($this->salesCase->getId(), 6'0'STR_PAD_LEFT)
  809.             . '-'
  810.             . ($this->isDraft() ? 'DRAFT' $orderNumberPrefix str_pad($this->getOrderNumber(), 5'0'STR_PAD_LEFT));
  811.     }
  812.     /**
  813.      * @return int|null
  814.      */
  815.     public function getOrderNumber(): ?int
  816.     {
  817.         return $this->orderNumber;
  818.     }
  819.     /**
  820.      * @return int
  821.      */
  822.     public function getOrderNumberWithType(): int
  823.     {
  824.         list(,$orderNumber) = explode('-'$this->getNumber());
  825.         return (int) $orderNumber;
  826.     }
  827.     /**
  828.      * @return null|string
  829.      */
  830.     public function getPlaceOfLoading(): ?string
  831.     {
  832.         return $this->placeOfLoading;
  833.     }
  834.     /**
  835.      * @return string
  836.      */
  837.     public function getPreviewFileName(): string
  838.     {
  839.         $type 'Invoice';
  840.         if ($this instanceof CreditInvoice) {
  841.             $type 'Credit Invoice';
  842.         } elseif ($this instanceof ProformaInvoice) {
  843.             $type 'Proforma Invoice';
  844.         } elseif ($this instanceof DownPaymentInvoice) {
  845.             $type 'Down Payment Invoice';
  846.         }
  847.         return $type ' - ' $this->getNumber() . ' - Preview ' date('Y-m-d') . '.pdf';
  848.     }
  849.     /**
  850.      * @return string|null
  851.      */
  852.     public function getReferenceNumber(): ?string
  853.     {
  854.         if ($this->isDraft()) {
  855.             return null;
  856.         }
  857.         list(,$orderNumber) = explode('-'$this->getNumber());
  858.         $number = (string) $orderNumber;
  859.         $j 0;
  860.         $sum 0;
  861.         $weight = [731];
  862.         for ($i strlen($number); $i 0$i--) {
  863.             $sum += $number[$i 1] * $weight[$j++ % 3];
  864.         }
  865.         return $number . ((10 - ($sum 10)) % 10);
  866.     }
  867.     /**
  868.      * @return SalesCase
  869.      */
  870.     public function getSalesCase(): SalesCase
  871.     {
  872.         return $this->salesCase;
  873.     }
  874.     /**
  875.      * @return Collection<SalesItem>
  876.      */
  877.     public function getSalesItems(): Collection
  878.     {
  879.         if (!$this->salesItemsPopulated) {
  880.             $currency $this->getCurrency();
  881.             foreach ($this->salesItems as $salesItem) {
  882.                 $salesItem->setCurrency($currency);
  883.                 $salesItem->setCurrencyExchangeRate($this->currencyExchangeRate);
  884.             }
  885.             $this->salesItemsPopulated true;
  886.         }
  887.         return $this->salesItems;
  888.     }
  889.     /**
  890.      * @return array
  891.      */
  892.     public static function getStatusChoices(): array
  893.     {
  894.         return [
  895.             self::STATUS_DRAFT,
  896.             self::STATUS_INVOICED,
  897.         ];
  898.     }
  899.     /**
  900.      * @return null|string
  901.      */
  902.     public function getTermsOfDelivery(): ?string
  903.     {
  904.         return $this->termsOfDelivery;
  905.     }
  906.     /**
  907.      * @return ArrayCollection|TermsOfPaymentRow[]
  908.      */
  909.     public function getTermsOfPaymentRows()
  910.     {
  911.         return $this->termsOfPaymentRows;
  912.     }
  913.     /**
  914.      * @return null|string
  915.      */
  916.     public function getText(): ?string
  917.     {
  918.         return $this->text;
  919.     }
  920.     /**
  921.      * @return null|string
  922.      */
  923.     public function getTotalVolume(): ?string
  924.     {
  925.         return $this->totalVolume;
  926.     }
  927.     /**
  928.      * @return null|int
  929.      */
  930.     public function getTotalWeightGross(): ?int
  931.     {
  932.         return $this->totalWeightGross;
  933.     }
  934.     /**
  935.      * @return null|int
  936.      */
  937.     public function getTotalWeightNet(): ?int
  938.     {
  939.         return $this->totalWeightNet;
  940.     }
  941.     /**
  942.      * @return null|string
  943.      */
  944.     public function getTransportation(): ?string
  945.     {
  946.         return $this->transportation;
  947.     }
  948.     /**
  949.      * @return string
  950.      */
  951.     abstract public function getType(): string;
  952.     /**
  953.      * @return bool
  954.      */
  955.     public function isCurrencyExchangeRateLocked(): bool
  956.     {
  957.         return !in_array(
  958.             $this->status,
  959.             [
  960.                 self::STATUS_DRAFT,
  961.             ]
  962.         );
  963.     }
  964.     /**
  965.      * @return bool
  966.      */
  967.     public function isCustomsDataPerItem(): bool
  968.     {
  969.         return $this->customsDataPerItem;
  970.     }
  971.     /**
  972.      * @return bool
  973.      */
  974.     public function isDraft(): bool
  975.     {
  976.         return $this->getStatus() == self::STATUS_DRAFT;
  977.     }
  978.     /**
  979.      * @return bool
  980.      */
  981.     public function isElectronicInvoiceStatusPending(): bool
  982.     {
  983.         if (null !== $response $this->electronicInvoiceResponse) {
  984.             return $response['status'] == 'PENDING';
  985.         }
  986.         return false;
  987.     }
  988.     /**
  989.      * @return bool
  990.      */
  991.     public function isElectronicInvoicingPossible(): bool
  992.     {
  993.         if (!(($this instanceof Invoice) || ($this instanceof DownPaymentInvoice) || ($this instanceof CreditInvoice))) {
  994.             return false;
  995.         }
  996.         if (!$this->isInvoiced()) {
  997.             return false;
  998.         }
  999.         if (null === $this->salesCase->getCompany()->getElectronicInvoicingIdentifier()) {
  1000.             return false;
  1001.         }
  1002.         return true;
  1003.     }
  1004.     /**
  1005.      * @return bool
  1006.      */
  1007.     public function isElectronicInvoiceSent(): bool
  1008.     {
  1009.         return (null !== $this->electronicInvoiceResponse);
  1010.     }
  1011.     /**
  1012.      * @return bool
  1013.      */
  1014.     public function isInvoiced(): bool
  1015.     {
  1016.         return $this->getStatus() == self::STATUS_INVOICED;
  1017.     }
  1018.     /**
  1019.      * @param TermsOfPaymentRow $row
  1020.      */
  1021.     public function removeTermsOfPaymentRow(TermsOfPaymentRow $row)
  1022.     {
  1023.         if (null !== $this->termsOfPaymentRows) {
  1024.             $this->termsOfPaymentRows->removeElement($row);
  1025.         }
  1026.     }
  1027.     /**
  1028.      * @param CompanyDocumentAddress|null $buyer
  1029.      */
  1030.     public function setBuyer(?CompanyDocumentAddress $buyer)
  1031.     {
  1032.         $this->buyer $buyer;
  1033.     }
  1034.     /**
  1035.      * @param string|null $commodityCode
  1036.      */
  1037.     public function setCommodityCode(?string $commodityCode)
  1038.     {
  1039.         $this->commodityCode $commodityCode;
  1040.     }
  1041.     /**
  1042.      * @param bool|null $confirmationOfExit
  1043.      */
  1044.     public function setConfirmationOfExit(?bool $confirmationOfExit)
  1045.     {
  1046.         $this->confirmationOfExit $confirmationOfExit;
  1047.     }
  1048.     /**
  1049.      * @param string|null $confirmationOfExitTemp
  1050.      */
  1051.     public function setConfirmationOfExitTemp(?string $confirmationOfExitTemp)
  1052.     {
  1053.         $this->confirmationOfExitTemp $confirmationOfExitTemp;
  1054.         $this->confirmationOfExitSet true;
  1055.         switch ($confirmationOfExitTemp) {
  1056.             case 'not_required':
  1057.                 $this->setConfirmationOfExit(null);
  1058.                 break;
  1059.             case 'waiting':
  1060.                 $this->setConfirmationOfExit(false);
  1061.                 break;
  1062.         }
  1063.     }
  1064.     /**
  1065.      * @param CompanyDocumentAddress|null $consignee
  1066.      */
  1067.     public function setConsignee(?CompanyDocumentAddress $consignee)
  1068.     {
  1069.         $this->consignee $consignee;
  1070.     }
  1071.     /**
  1072.      * @param string|null $countryOfOrigin
  1073.      */
  1074.     public function setCountryOfOrigin(?string $countryOfOrigin)
  1075.     {
  1076.         $this->countryOfOrigin $countryOfOrigin;
  1077.     }
  1078.     /**
  1079.      * @param bool $customsDataPerItem
  1080.      */
  1081.     public function setCustomsDataPerItem(bool $customsDataPerItem)
  1082.     {
  1083.         $this->customsDataPerItem $customsDataPerItem;
  1084.     }
  1085.     /**
  1086.      * @param CompanyDocumentAddress|null $deliveryAddress
  1087.      */
  1088.     public function setDeliveryAddress(?CompanyDocumentAddress $deliveryAddress)
  1089.     {
  1090.         $this->deliveryAddress $deliveryAddress;
  1091.     }
  1092.     /**
  1093.      * @param DateTime|null $deliveryDate
  1094.      */
  1095.     public function setDeliveryDate(?DateTime $deliveryDate)
  1096.     {
  1097.         $this->deliveryDate $deliveryDate;
  1098.     }
  1099.     /**
  1100.      * @param string|null $descriptionOfGoods
  1101.      */
  1102.     public function setDescriptionOfGoods(?string $descriptionOfGoods)
  1103.     {
  1104.         $this->descriptionOfGoods $descriptionOfGoods;
  1105.     }
  1106.     /**
  1107.      * @param array|null $electronicInvoiceResponse
  1108.      */
  1109.     public function setElectronicInvoiceResponse(?array $electronicInvoiceResponse): void
  1110.     {
  1111.         $this->electronicInvoiceResponse $electronicInvoiceResponse;
  1112.     }
  1113.     /**
  1114.      * @param string|null $finalDestination
  1115.      */
  1116.     public function setFinalDestination(?string $finalDestination)
  1117.     {
  1118.         $this->finalDestination $finalDestination;
  1119.     }
  1120.     /**
  1121.      * @param CompanyDocumentAddress|null $invoicingAddress
  1122.      */
  1123.     public function setInvoicingAddress(?CompanyDocumentAddress $invoicingAddress)
  1124.     {
  1125.         $this->invoicingAddress $invoicingAddress;
  1126.     }
  1127.     /**
  1128.      * @param string|null $marksAndNumbers
  1129.      */
  1130.     public function setMarksAndNumbers(?string $marksAndNumbers)
  1131.     {
  1132.         $this->marksAndNumbers $marksAndNumbers;
  1133.     }
  1134.     /**
  1135.      * @param string|null $modeOfTransport
  1136.      */
  1137.     public function setModeOfTransport(?string $modeOfTransport): void
  1138.     {
  1139.         $this->modeOfTransport $modeOfTransport;
  1140.     }
  1141.     /**
  1142.      * @param string|null $natureOfTransaction
  1143.      */
  1144.     public function setNatureOfTransaction(?string $natureOfTransaction): void
  1145.     {
  1146.         $this->natureOfTransaction $natureOfTransaction;
  1147.     }
  1148.     /**
  1149.      * @param string|null $notes
  1150.      */
  1151.     public function setNotes(?string $notes)
  1152.     {
  1153.         $this->notes $notes;
  1154.     }
  1155.     /**
  1156.      * @param int|null $orderNumber
  1157.      */
  1158.     public function setOrderNumber(?int $orderNumber)
  1159.     {
  1160.         $this->orderNumber $orderNumber;
  1161.     }
  1162.     /**
  1163.      * @param string|null $placeOfLoading
  1164.      */
  1165.     public function setPlaceOfLoading(?string $placeOfLoading)
  1166.     {
  1167.         $this->placeOfLoading $placeOfLoading;
  1168.     }
  1169.     /**
  1170.      * @param string|null $totalVolume
  1171.      */
  1172.     public function setTotalVolume(?string $totalVolume)
  1173.     {
  1174.         $this->totalVolume $totalVolume;
  1175.     }
  1176.     /**
  1177.      * @param int|null $totalWeightGross
  1178.      */
  1179.     public function setTotalWeightGross(?int $totalWeightGross)
  1180.     {
  1181.         $this->totalWeightGross $totalWeightGross;
  1182.     }
  1183.     /**
  1184.      * @param int|null $totalWeightNet
  1185.      */
  1186.     public function setTotalWeightNet(?int $totalWeightNet)
  1187.     {
  1188.         $this->totalWeightNet $totalWeightNet;
  1189.     }
  1190.     /**
  1191.      * @param string|null $termsOfDelivery
  1192.      */
  1193.     public function setTermsOfDelivery(?string $termsOfDelivery)
  1194.     {
  1195.         $this->termsOfDelivery $termsOfDelivery;
  1196.     }
  1197.     /**
  1198.      * @param string|null $text
  1199.      */
  1200.     public function setText(?string $text)
  1201.     {
  1202.         $this->text $text;
  1203.     }
  1204.     /**
  1205.      * @param string|null $transportation
  1206.      */
  1207.     public function setTransportation(?string $transportation)
  1208.     {
  1209.         $this->transportation $transportation;
  1210.     }
  1211.     /**
  1212.      * @Assert\Callback
  1213.      *
  1214.      * @param ExecutionContextInterface $context
  1215.      *
  1216.      * @throws Exception
  1217.      */
  1218.     public function validateDeliveryDate(ExecutionContextInterface $context)
  1219.     {
  1220.         /**
  1221.          * If days have been defined in any of the terms of payment rows
  1222.          * then delivery date must be defined in the invoice.
  1223.          */
  1224.         if (null === $this->deliveryDate) {
  1225.             foreach ($this->termsOfPaymentRows as $termsOfPaymentRow) {
  1226.                 if (null !== $termsOfPaymentRow->getDays()) {
  1227.                     $context->buildViolation('validation.deliveryDate.required')
  1228.                         ->setTranslationDomain('Invoice')
  1229.                         ->atPath('deliveryDate')
  1230.                         ->addViolation();
  1231.                 }
  1232.             }
  1233.         }
  1234.         /**
  1235.          * Validate the due date.
  1236.          */
  1237.         if (null === $this->getDueDate()) {
  1238.             $context->buildViolation('validation.dueDate.required')
  1239.                 ->setTranslationDomain('Invoice')
  1240.                 ->addViolation();
  1241.         }
  1242.     }
  1243.     /**
  1244.      * @Assert\Callback
  1245.      *
  1246.      * @param ExecutionContextInterface $context
  1247.      */
  1248.     public function validateVatJustification(ExecutionContextInterface $context)
  1249.     {
  1250.         if ($this->vat == && $this->vatJustification === null) {
  1251.             $context->buildViolation('validation.vatJustification.not_selected')
  1252.                 ->setTranslationDomain('Invoice')
  1253.                 ->atPath('vatJustification')
  1254.                 ->addViolation();
  1255.         }
  1256.     }
  1257. }