src/Entity/SalesItem.php line 28

Open in your IDE?
  1. <?php
  2. namespace App\Entity;
  3. use App\Exception\CurrencyException;
  4. use App\Model\Currency;
  5. use App\Model\SalesItemDelivery;
  6. use App\Validator\ValidSalesItemCode;
  7. use DateTime;
  8. use Doctrine\ORM\Mapping as ORM;
  9. use Symfony\Component\Validator\Constraints as Assert;
  10. use Symfony\Component\Validator\Context\ExecutionContextInterface;
  11. /**
  12.  * @ORM\Entity(
  13.  *     repositoryClass="App\Repository\SalesItemRepository"
  14.  * )
  15.  * @ORM\Table(
  16.  *     name="sales_item",
  17.  *     options={"collate"="utf8_swedish_ci"}
  18.  * )
  19.  * @ORM\HasLifecycleCallbacks
  20.  *
  21.  * @ValidSalesItemCode(
  22.  *     groups={"Offer"}
  23.  * )
  24.  */
  25. class SalesItem implements EntityInterface
  26. {
  27.     use ProductTrait;
  28.     const POSITION_MULTIPLIER 1000;
  29.     /**
  30.      * @ORM\Id
  31.      * @ORM\Column(
  32.      *     type="integer"
  33.      * )
  34.      * @ORM\GeneratedValue(
  35.      *     strategy="AUTO"
  36.      * )
  37.      *
  38.      * @var int|null
  39.      */
  40.     protected ?int $id null;
  41.     /**
  42.      * @ORM\Column(
  43.      *     type="string",
  44.      *     length=5000,
  45.      *     nullable=true
  46.      * )
  47.      *
  48.      * @var string|null
  49.      */
  50.     protected ?string $additionalInfo null;
  51.     /**
  52.      * @ORM\Column(
  53.      *     type="string",
  54.      *     length=50,
  55.      *     nullable=true
  56.      * )
  57.      *
  58.      * @var string|null
  59.      */
  60.     protected ?string $commodityCode null;
  61.     /**
  62.      * Cost of sales is always in default currency EUR
  63.      *
  64.      * @var int|null
  65.      */
  66.     protected ?int $costOfSales null;
  67.     /**
  68.      * @ORM\Column(
  69.      *     type="datetime"
  70.      * )
  71.      * @Assert\NotNull
  72.      *
  73.      * @var DateTime
  74.      */
  75.     protected DateTime $created;
  76.     /**
  77.      * @var string
  78.      */
  79.     protected string $currency Currency::DEFAULT_CURRENCY;
  80.     /**
  81.      * @var float|null
  82.      */
  83.     protected ?float $currencyExchangeRate null;
  84.     /**
  85.      * @ORM\Column(
  86.      *     name="deliveries",
  87.      *     type="json",
  88.      *     nullable=true
  89.      * )
  90.      *
  91.      * @var array|null
  92.      */
  93.     protected ?array $deliveriesArray null;
  94.     /**
  95.      * @Assert\Valid
  96.      *
  97.      * @var array<SalesItemDelivery>
  98.      */
  99.     protected array $deliveries = [];
  100.     /**
  101.      * @ORM\Column(
  102.      *     type="string",
  103.      *     length=1000,
  104.      *     nullable=true
  105.      * )
  106.      *
  107.      * @var string|null
  108.      */
  109.     protected ?string $deliveryComments null;
  110.     /**
  111.      * @ORM\Column(
  112.      *     type="date",
  113.      *     nullable=true
  114.      * )
  115.      *
  116.      * @var DateTime|null
  117.      */
  118.     protected ?DateTime $deliveryDateEstimate null;
  119.     /**
  120.      * @ORM\Column(
  121.      *     type="decimal",
  122.      *     precision=5,
  123.      *     scale=2,
  124.      *     nullable=true
  125.      * )
  126.      * @Assert\Range(
  127.      *     min=0,
  128.      *     max=100
  129.      * )
  130.      *
  131.      * @var string|null
  132.      */
  133.     protected ?string $discountPercentage null;
  134.     /**
  135.      * @ORM\Column(
  136.      *     type="integer",
  137.      *     nullable=true
  138.      * )
  139.      * @Assert\Range(
  140.      *     min=0,
  141.      * )
  142.      *
  143.      * @var int|null
  144.      */
  145.     protected ?int $discountValue null;
  146.     /**
  147.      * @ORM\Column(
  148.      *     type="boolean"
  149.      * )
  150.      *
  151.      * @var bool
  152.      */
  153.     protected bool $excludePrice false;
  154.     /**
  155.      * @ORM\Column(
  156.      *     type="date",
  157.      *     nullable=true
  158.      * )
  159.      *
  160.      * @var DateTime|null
  161.      */
  162.     protected ?DateTime $exportLicenseExpiryDate null;
  163.     /**
  164.      * @ORM\Column(
  165.      *     type="date",
  166.      *     nullable=true
  167.      * )
  168.      *
  169.      * @var DateTime|null
  170.      */
  171.     protected ?DateTime $exportLicenseIssueDate null;
  172.     /**
  173.      * @ORM\Column(
  174.      *     type="string",
  175.      *     length=20,
  176.      *     nullable=true
  177.      * )
  178.      *
  179.      * @var string|null
  180.      */
  181.     protected ?string $exportLicenseNumberEu null;
  182.     /**
  183.      * @ORM\Column(
  184.      *     type="string",
  185.      *     length=20,
  186.      *     nullable=true
  187.      * )
  188.      *
  189.      * @var string|null
  190.      */
  191.     protected ?string $exportLicenseNumberUs null;
  192.     /**
  193.      * @ORM\Column(
  194.      *     type="integer",
  195.      *     nullable=true
  196.      * )
  197.      *
  198.      * @var int|null
  199.      */
  200.     protected ?int $exportLicenseQuantityEu null;
  201.     /**
  202.      * @ORM\Column(
  203.      *     type="integer",
  204.      *     nullable=true
  205.      * )
  206.      *
  207.      * @var int|null
  208.      */
  209.     protected ?int $exportLicenseQuantityUs null;
  210.     /**
  211.      * @ORM\Column(
  212.      *     type="date",
  213.      *     nullable=true
  214.      * )
  215.      *
  216.      * @var DateTime|null
  217.      */
  218.     protected ?DateTime $featuresExpiryDate null;
  219.     /**
  220.      * @ORM\Column(
  221.      *     type="boolean"
  222.      * )
  223.      *
  224.      * @var bool
  225.      */
  226.     protected bool $inventory false;
  227.     /**
  228.      * @ORM\Column(
  229.      *     type="string",
  230.      *     length=2000,
  231.      *     nullable=true
  232.      * )
  233.      *
  234.      * @var string|null
  235.      */
  236.     protected ?string $licenseSerialNumbers null;
  237.     /**
  238.      * @ORM\Column(
  239.      *     type="boolean"
  240.      * )
  241.      *
  242.      * @var bool
  243.      */
  244.     protected bool $licenseVisible false;
  245.     /**
  246.      * @ORM\Column(
  247.      *     type="integer"
  248.      * )
  249.      *
  250.      * @Assert\NotNull
  251.      *
  252.      * @var int
  253.      */
  254.     protected int $position;
  255.     /**
  256.      * @ORM\Column(
  257.      *     type="integer",
  258.      *     nullable=true
  259.      * )
  260.      *
  261.      * @var int|null
  262.      */
  263.     protected ?int $purchasePrice null;
  264.     /**
  265.      * @ORM\Column(
  266.      *     type="integer"
  267.      * )
  268.      *
  269.      * @Assert\NotNull
  270.      *
  271.      * @var int
  272.      */
  273.     protected int $quantity 1;
  274.     /**
  275.      * @ORM\Column(
  276.      *     type="integer",
  277.      *     nullable=true
  278.      * )
  279.      *
  280.      * @var int|null
  281.      */
  282.     protected ?int $quantityPerPci null;
  283.     /**
  284.      * @ORM\Column(
  285.      *     type="date",
  286.      *     nullable=true
  287.      * )
  288.      *
  289.      * @var DateTime|null
  290.      */
  291.     protected ?DateTime $serviceEnd null;
  292.     /**
  293.      * @ORM\Column(
  294.      *     type="string",
  295.      *     length=50,
  296.      *     nullable=true
  297.      * )
  298.      *
  299.      * @var string|null
  300.      */
  301.     protected ?string $serviceId null;
  302.     /**
  303.      * @ORM\Column(
  304.      *     type="boolean"
  305.      * )
  306.      *
  307.      * @var bool
  308.      */
  309.     protected bool $servicePeriodVisible false;
  310.     /**
  311.      * @ORM\Column(
  312.      *     type="date",
  313.      *     nullable=true
  314.      * )
  315.      *
  316.      * @var DateTime|null
  317.      */
  318.     protected ?DateTime $serviceStart null;
  319.     /**
  320.      * @ORM\ManyToOne(
  321.      *     targetEntity="Supplier",
  322.      *     inversedBy="salesItems"
  323.      * )
  324.      * @ORM\JoinColumn(
  325.      *     name="supplier_id",
  326.      *     referencedColumnName="id",
  327.      *     onDelete="SET NULL"
  328.      * )
  329.      *
  330.      * @var Supplier|null
  331.      */
  332.     protected ?Supplier $supplier null;
  333.     /**
  334.      * @var int
  335.      */
  336.     protected int $totalQuantity 0;
  337.     /**
  338.      * @ORM\Column(
  339.      *     type="string",
  340.      *     length=10,
  341.      *     nullable=true
  342.      * )
  343.      *
  344.      * @var string|null
  345.      */
  346.     protected ?string $unit 'pc';
  347.     /**
  348.      * @ORM\Column(
  349.      *     type="integer",
  350.      *     nullable=true
  351.      * )
  352.      *
  353.      * @var int|null
  354.      */
  355.     protected ?int $unitPrice null;
  356.     /**
  357.      * @ORM\Column(
  358.      *     type="string",
  359.      *     length=100,
  360.      *     nullable=true
  361.      * )
  362.      *
  363.      * @var string|null
  364.      */
  365.     protected ?string $versionCode null;
  366.     /**
  367.      * @param int $position
  368.      */
  369.     public function __construct(int $position)
  370.     {
  371.         $this->position $position;
  372.         $this->created = new DateTime();
  373.     }
  374.     public function __clone()
  375.     {
  376.         $this->id null;
  377.         $this->created = new DateTime();
  378.         $this->purchasePrice null;
  379.     }
  380.     /**
  381.      * @return string
  382.      */
  383.     public function __toString(): string
  384.     {
  385.         return $this->code;
  386.     }
  387.     /**
  388.      * @param int $price
  389.      *
  390.      * @return int
  391.      *
  392.      * @throws CurrencyException
  393.      */
  394.     private function convertPrice(int $price): int
  395.     {
  396.         if (null === $this->currencyExchangeRate) {
  397.             throw new CurrencyException('No exchange rate for ' $this->currency ' to ' Currency::DEFAULT_CURRENCY ' has been provided.');
  398.         }
  399.         return round($price $this->currencyExchangeRate);
  400.     }
  401.     /**
  402.      * @return null|string
  403.      */
  404.     public function getAdditionalInfo(): ?string
  405.     {
  406.         return $this->additionalInfo;
  407.     }
  408.     /**
  409.      * @return null|string
  410.      */
  411.     public function getCommodityCode(): ?string
  412.     {
  413.         return $this->commodityCode;
  414.     }
  415.     /**
  416.      * @return int|null
  417.      */
  418.     public function getCostOfSales(): ?int
  419.     {
  420.         return $this->costOfSales;
  421.     }
  422.     /**
  423.      * @return DateTime
  424.      */
  425.     public function getCreated(): DateTime
  426.     {
  427.         return $this->created;
  428.     }
  429.     /**
  430.      * @return string
  431.      */
  432.     public function getCurrency(): string
  433.     {
  434.         return $this->currency;
  435.     }
  436.     /**
  437.      * @return float|null
  438.      */
  439.     public function getCurrencyExchangeRate(): ?float
  440.     {
  441.         return $this->currencyExchangeRate;
  442.     }
  443.     /**
  444.      * @return array<SalesItemDelivery>
  445.      */
  446.     public function getDeliveries(): array
  447.     {
  448.         return $this->deliveries;
  449.     }
  450.     /**
  451.      * @return string|null
  452.      */
  453.     public function getDeliveryComments(): ?string
  454.     {
  455.         return $this->deliveryComments;
  456.     }
  457.     /**
  458.      * @return DateTime|null
  459.      */
  460.     public function getDeliveryDateEstimate(): ?DateTime
  461.     {
  462.         return $this->deliveryDateEstimate;
  463.     }
  464.     /**
  465.      * @return float|null
  466.      */
  467.     public function getDiscountPercentage(): ?float
  468.     {
  469.         return null !== $this->discountPercentage ? (float) $this->discountPercentage null;
  470.     }
  471.     /**
  472.      * @return int|null
  473.      */
  474.     public function getDiscountValue(): ?int
  475.     {
  476.         return $this->discountValue;
  477.     }
  478.     /**
  479.      * @return bool
  480.      */
  481.     public function getExcludePrice(): bool
  482.     {
  483.         return $this->excludePrice;
  484.     }
  485.     /**
  486.      * @return DateTime|null
  487.      */
  488.     public function getExportLicenseExpiryDate(): ?DateTime
  489.     {
  490.         return $this->exportLicenseExpiryDate;
  491.     }
  492.     /**
  493.      * @return DateTime|null
  494.      */
  495.     public function getExportLicenseIssueDate(): ?DateTime
  496.     {
  497.         return $this->exportLicenseIssueDate;
  498.     }
  499.     /**
  500.      * @return null|string
  501.      */
  502.     public function getExportLicenseNumberEu(): ?string
  503.     {
  504.         return $this->exportLicenseNumberEu;
  505.     }
  506.     /**
  507.      * @return null|string
  508.      */
  509.     public function getExportLicenseNumberUs(): ?string
  510.     {
  511.         return $this->exportLicenseNumberUs;
  512.     }
  513.     /**
  514.      * @return int|null
  515.      */
  516.     public function getExportLicenseQuantityEu(): ?int
  517.     {
  518.         return $this->exportLicenseQuantityEu;
  519.     }
  520.     /**
  521.      * @return int|null
  522.      */
  523.     public function getExportLicenseQuantityUs(): ?int
  524.     {
  525.         return $this->exportLicenseQuantityUs;
  526.     }
  527.     /**
  528.      * @return null|string
  529.      */
  530.     public function getLicenseSerialNumbers(): ?string
  531.     {
  532.         return $this->licenseSerialNumbers;
  533.     }
  534.     /**
  535.      * @return DateTime|null
  536.      */
  537.     public function getFeaturesExpiryDate(): ?DateTime
  538.     {
  539.         return $this->featuresExpiryDate;
  540.     }
  541.     /**
  542.      * @return int|null
  543.      * @throws CurrencyException
  544.      */
  545.     public function getFinvoiceRowVatExcludedAmount(): ?int
  546.     {
  547.         return $this->getPrice(false);
  548.     }
  549.     /**
  550.      * @return float|null
  551.      *
  552.      * @throws CurrencyException
  553.      */
  554.     public function getGrossMargin(): ?float
  555.     {
  556.         if ($this->unitPrice === null || $this->costOfSales === null) {
  557.             return null;
  558.         }
  559.         $sales $this->getPrice(true);
  560.         if ($sales 0) {
  561.             return ($sales - ($this->quantity $this->costOfSales)) / $sales;
  562.         }
  563.         return 0.0;
  564.     }
  565.     /**
  566.      * @return int|null
  567.      */
  568.     public function getId(): ?int
  569.     {
  570.         return $this->id;
  571.     }
  572.     /**
  573.      * @return int|null
  574.      */
  575.     public function getPciPosition(): ?int
  576.     {
  577.         if ($this->isPciSubItem()) {
  578.             return $this->position - ($this->position SalesItem::POSITION_MULTIPLIER);
  579.         }
  580.         return null;
  581.     }
  582.     /**
  583.      * @return int
  584.      */
  585.     public function getPosition(): int
  586.     {
  587.         return $this->position;
  588.     }
  589.     /**
  590.      * @return string
  591.      */
  592.     public function getPositionDisplay(): string
  593.     {
  594.         if ($this->position >= SalesItem::POSITION_MULTIPLIER) {
  595.             $sub $this->position SalesItem::POSITION_MULTIPLIER;
  596.             $position = ($this->position $sub) / SalesItem::POSITION_MULTIPLIER;
  597.             if ($sub 0) {
  598.                 return $position '.' $sub;
  599.             }
  600.             return $position;
  601.         }
  602.         return (string) $this->position;
  603.     }
  604.     /**
  605.      * @return int
  606.      */
  607.     public function getPositionNormalized(): int
  608.     {
  609.         if ($this->position self::POSITION_MULTIPLIER) {
  610.             return $this->position self::POSITION_MULTIPLIER;
  611.         }
  612.         return $this->position;
  613.     }
  614.     /**
  615.      * @param bool $defaultCurrency
  616.      *
  617.      * @return int|null
  618.      *
  619.      * @throws CurrencyException
  620.      */
  621.     public function getPrice(bool $defaultCurrency false): ?int
  622.     {
  623.         if (null === $this->unitPrice) {
  624.             return null;
  625.         }
  626.         return $this->getPricePreDiscount($defaultCurrency) - $this->getPriceOfDiscount($defaultCurrency);
  627.     }
  628.     /**
  629.      * @param bool $defaultCurrency
  630.      *
  631.      * @return int|null
  632.      *
  633.      * @throws CurrencyException
  634.      */
  635.     public function getTotalPrice(bool $defaultCurrency false): ?int
  636.     {
  637.         return $this->getPrice($defaultCurrency);
  638.     }
  639.     /**
  640.      * @param bool $defaultCurrency
  641.      *
  642.      * @return int
  643.      *
  644.      * @throws CurrencyException
  645.      */
  646.     public function getPriceOfDiscount(bool $defaultCurrency false): int
  647.     {
  648.         if (null !== $this->discountValue) {
  649.             if ($defaultCurrency && !$this->isDefaultCurrency()) {
  650.                 return $this->convertPrice($this->discountValue);
  651.             }
  652.             return $this->discountValue;
  653.         } elseif (null !== $this->discountPercentage) {
  654.             if (null !== $pricePreDiscount $this->getPricePreDiscount($defaultCurrency)) {
  655.                 return round($pricePreDiscount $this->discountPercentage 100);
  656.             }
  657.         }
  658.         return 0;
  659.     }
  660.     /**
  661.      * @param bool $defaultCurrency
  662.      *
  663.      * @return int|null
  664.      *
  665.      * @throws CurrencyException
  666.      */
  667.     public function getPricePreDiscount(bool $defaultCurrency false): ?int
  668.     {
  669.         if (null === $this->unitPrice) {
  670.             return null;
  671.         }
  672.         return $this->quantity $this->getUnitPrice($defaultCurrency);
  673.     }
  674.     /**
  675.      * @param bool $defaultCurrency
  676.      *
  677.      * @return int|null
  678.      *
  679.      * @throws CurrencyException
  680.      */
  681.     public function getPurchasePrice(bool $defaultCurrency false): ?int
  682.     {
  683.         if (null !== $this->purchasePrice) {
  684.             if ($defaultCurrency && !$this->isDefaultCurrency()) {
  685.                 return $this->convertPrice($this->purchasePrice);
  686.             }
  687.         }
  688.         return $this->purchasePrice;
  689.     }
  690.     /**
  691.      * @return int
  692.      */
  693.     public function getQuantity(): int
  694.     {
  695.         return $this->quantity;
  696.     }
  697.     /**
  698.      * @return int|null
  699.      */
  700.     public function getQuantityPerPci(): ?int
  701.     {
  702.         return $this->quantityPerPci;
  703.     }
  704.     /**
  705.      * @return DateTime|null
  706.      */
  707.     public function getServiceEnd(): ?DateTime
  708.     {
  709.         return $this->serviceEnd;
  710.     }
  711.     /**
  712.      * @return null|string
  713.      */
  714.     public function getServiceId(): ?string
  715.     {
  716.         return $this->serviceId;
  717.     }
  718.     /**
  719.      * @return DateTime|null
  720.      */
  721.     public function getServiceStart(): ?DateTime
  722.     {
  723.         return $this->serviceStart;
  724.     }
  725.     /**
  726.      * @return Supplier|null
  727.      */
  728.     public function getSupplier(): ?Supplier
  729.     {
  730.         return $this->supplier;
  731.     }
  732.     /**
  733.      * @return int
  734.      */
  735.     public function getTotalQuantity(): int
  736.     {
  737.         return $this->totalQuantity;
  738.     }
  739.     /**
  740.      * @return int|null
  741.      */
  742.     public function getTotalWeight(): ?int
  743.     {
  744.         if (null !== $this->weight) {
  745.             return $this->weight $this->quantity;
  746.         }
  747.         return null;
  748.     }
  749.     /**
  750.      * @return string|null
  751.      */
  752.     public function getUnit(): ?string
  753.     {
  754.         return $this->unit;
  755.     }
  756.     /**
  757.      * @return string[]
  758.      */
  759.     public static function getUnitChoices(): array
  760.     {
  761.         return [
  762.             'pc',
  763.             'batch',
  764.             'set',
  765.             'lot',
  766.             'ea',
  767.             'day',
  768.             'year',
  769.             'metre',
  770.             '',
  771.         ];
  772.     }
  773.     /**
  774.      * @param bool $defaultCurrency
  775.      *
  776.      * @return int|null
  777.      *
  778.      * @throws CurrencyException
  779.      */
  780.     public function getUnitPrice(bool $defaultCurrency false): ?int
  781.     {
  782.         if (null !== $this->unitPrice) {
  783.             if ($defaultCurrency && !$this->isDefaultCurrency()) {
  784.                 return $this->convertPrice($this->unitPrice);
  785.             }
  786.         }
  787.         return $this->unitPrice;
  788.     }
  789.     /**
  790.      * @return null|string
  791.      */
  792.     public function getVersionCode(): ?string
  793.     {
  794.         return $this->versionCode;
  795.     }
  796.     /**
  797.      * @return bool
  798.      */
  799.     public function hasDiscount(): bool
  800.     {
  801.         return $this->discountValue !== null || $this->discountPercentage !== null;
  802.     }
  803.     /**
  804.      * @return bool|null
  805.      */
  806.     public function isActive(): ?bool
  807.     {
  808.         if (null !== $isFuture $this->isFuture()) {
  809.             if (null !== $isExpired $this->isExpired()) {
  810.                 return $isFuture === false && $isExpired === false;
  811.             }
  812.         }
  813.         return null;
  814.     }
  815.     /**
  816.      * @return bool
  817.      */
  818.     public function isDefaultCurrency(): bool
  819.     {
  820.         return $this->currency == Currency::DEFAULT_CURRENCY;
  821.     }
  822.     /**
  823.      * @return bool
  824.      */
  825.     public function isExcludePrice(): bool
  826.     {
  827.         return $this->excludePrice;
  828.     }
  829.     /**
  830.      * @return bool
  831.      */
  832.     public function isInventory(): bool
  833.     {
  834.         return $this->inventory;
  835.     }
  836.     /**
  837.      * @return bool
  838.      */
  839.     public function isLicenseVisible(): bool
  840.     {
  841.         return $this->licenseVisible;
  842.     }
  843.     /**
  844.      * @return bool|null
  845.      */
  846.     public function isFuture(): ?bool
  847.     {
  848.         if ($this->isSupport() && null !== $serviceStart $this->getServiceStart()) {
  849.             return $serviceStart->getTimestamp() > time();
  850.         }
  851.         return null;
  852.     }
  853.     /**
  854.      * @return bool|null
  855.      */
  856.     public function isExpired(): ?bool
  857.     {
  858.         if ($this->isSupport() && null !== $serviceEnd $this->getServiceEnd()) {
  859.             return $serviceEnd->getTimestamp() <= time();
  860.         }
  861.         return null;
  862.     }
  863.     /**
  864.      * @return bool
  865.      */
  866.     public function isPciItem(): bool
  867.     {
  868.         return str_starts_with(strtolower($this->type), 'pci');
  869.     }
  870.     /**
  871.      * @return bool
  872.      */
  873.     public function isPciExpandItem(): bool
  874.     {
  875.         return strtolower($this->type) === 'pci:expand';
  876.     }
  877.     /**
  878.      * @return bool
  879.      */
  880.     public function isPciSubItem(): bool
  881.     {
  882.         return $this->position >= self::POSITION_MULTIPLIER && $this->position self::POSITION_MULTIPLIER 0;
  883.     }
  884.     /**
  885.      * @return bool
  886.      */
  887.     public function isServicePeriodVisible(): bool
  888.     {
  889.         return $this->servicePeriodVisible;
  890.     }
  891.     /**
  892.      * @ORM\PostLoad
  893.      */
  894.     public function postLoad(): void
  895.     {
  896.         // Set total quantity
  897.         $this->totalQuantity $this->quantity;
  898.         // Unserialize deliveries
  899.         $this->deliveries = [];
  900.         if (is_array($this->deliveriesArray)) {
  901.             foreach ($this->deliveriesArray as $delivery) {
  902.                 $this->deliveries[] = SalesItemDelivery::fromArray($delivery);
  903.             }
  904.         }
  905.     }
  906.     /**
  907.      * @ORM\PrePersist
  908.      */
  909.     public function prePersist(): void
  910.     {
  911.         // Ensure there is a created time
  912.         $this->created = new DateTime();
  913.     }
  914.     /**
  915.      * @ORM\PreUpdate
  916.      */
  917.     public function preUpdate(): void
  918.     {
  919.         // Serialize the deliveries
  920.         $this->deliveriesArray null;
  921.         if (count($this->deliveries) > 0) {
  922.             $this->deliveriesArray array_map(
  923.                 function (SalesItemDelivery $delivery): array {
  924.                     return $delivery->toArray();
  925.                 },
  926.                 $this->deliveries
  927.             );
  928.         }
  929.     }
  930.     /**
  931.      * @param string|null $additionalInfo
  932.      */
  933.     public function setAdditionalInfo(?string $additionalInfo): void
  934.     {
  935.         $this->additionalInfo $additionalInfo;
  936.     }
  937.     /**
  938.      * @param null|string $commodityCode
  939.      */
  940.     public function setCommodityCode(?string $commodityCode): void
  941.     {
  942.         $this->commodityCode $commodityCode;
  943.     }
  944.     /**
  945.      * @param int|null $costOfSales
  946.      */
  947.     public function setCostOfSales(?int $costOfSales null): void
  948.     {
  949.         $this->costOfSales $costOfSales;
  950.     }
  951.     /**
  952.      * @param string $currency
  953.      */
  954.     public function setCurrency(string $currency): void
  955.     {
  956.         $this->currency $currency;
  957.     }
  958.     /**
  959.      * @param float|null $currencyExchangeRate
  960.      */
  961.     public function setCurrencyExchangeRate(?float $currencyExchangeRate): void
  962.     {
  963.         $this->currencyExchangeRate $currencyExchangeRate;
  964.     }
  965.     /**
  966.      * @param array<SalesItemDelivery> $deliveries
  967.      */
  968.     public function setDeliveries(array $deliveries): void
  969.     {
  970.         $this->deliveries = [];
  971.         foreach ($deliveries as $delivery) {
  972.             if (null !== $delivery) {
  973.                 $this->deliveries[] = $delivery;
  974.             }
  975.         }
  976.     }
  977.     /**
  978.      * @param string|null $deliveryComments
  979.      */
  980.     public function setDeliveryComments(?string $deliveryComments): void
  981.     {
  982.         $this->deliveryComments $deliveryComments;
  983.     }
  984.     /**
  985.      * @param DateTime|null $deliveryDateEstimate
  986.      */
  987.     public function setDeliveryDateEstimate(?DateTime $deliveryDateEstimate): void
  988.     {
  989.         $this->deliveryDateEstimate $deliveryDateEstimate;
  990.     }
  991.     /**
  992.      * @param float|null $discountPercentage
  993.      */
  994.     public function setDiscountPercentage(?float $discountPercentage): void
  995.     {
  996.         $this->discountPercentage null !== $discountPercentage number_format($discountPercentage2'.''') : null;
  997.     }
  998.     /**
  999.      * @param int|null $discountValue
  1000.      */
  1001.     public function setDiscountValue(?int $discountValue): void
  1002.     {
  1003.         $this->discountValue $discountValue;
  1004.     }
  1005.     /**
  1006.      * @param bool $excludePrice
  1007.      */
  1008.     public function setExcludePrice(bool $excludePrice): void
  1009.     {
  1010.         $this->excludePrice $excludePrice;
  1011.     }
  1012.     /**
  1013.      * @param DateTime|null $exportLicenseExpiryDate
  1014.      */
  1015.     public function setExportLicenseExpiryDate(?DateTime $exportLicenseExpiryDate): void
  1016.     {
  1017.         $this->exportLicenseExpiryDate $exportLicenseExpiryDate;
  1018.     }
  1019.     /**
  1020.      * @param DateTime|null $exportLicenseIssueDate
  1021.      */
  1022.     public function setExportLicenseIssueDate(?DateTime $exportLicenseIssueDate): void
  1023.     {
  1024.         $this->exportLicenseIssueDate $exportLicenseIssueDate;
  1025.     }
  1026.     /**
  1027.      * @param int|null $exportLicenseQuantityEu
  1028.      */
  1029.     public function setExportLicenseQuantityEu(?int $exportLicenseQuantityEu): void
  1030.     {
  1031.         $this->exportLicenseQuantityEu $exportLicenseQuantityEu;
  1032.     }
  1033.     /**
  1034.      * @param int|null $exportLicenseQuantityUs
  1035.      */
  1036.     public function setExportLicenseQuantityUs(?int $exportLicenseQuantityUs): void
  1037.     {
  1038.         $this->exportLicenseQuantityUs $exportLicenseQuantityUs;
  1039.     }
  1040.     /**
  1041.      * @param DateTime|null $featuresExpiryDate
  1042.      */
  1043.     public function setFeaturesExpiryDate(?DateTime $featuresExpiryDate): void
  1044.     {
  1045.         $this->featuresExpiryDate $featuresExpiryDate;
  1046.     }
  1047.     /**
  1048.      * @param null|string $exportLicenseNumberEu
  1049.      */
  1050.     public function setExportLicenseNumberEu(?string $exportLicenseNumberEu): void
  1051.     {
  1052.         $this->exportLicenseNumberEu $exportLicenseNumberEu;
  1053.     }
  1054.     /**
  1055.      * @param null|string $exportLicenseNumberUs
  1056.      */
  1057.     public function setExportLicenseNumberUs(?string $exportLicenseNumberUs): void
  1058.     {
  1059.         $this->exportLicenseNumberUs $exportLicenseNumberUs;
  1060.     }
  1061.     /**
  1062.      * @param bool $inventory
  1063.      */
  1064.     public function setInventory(bool $inventory): void
  1065.     {
  1066.         $this->inventory $inventory;
  1067.     }
  1068.     /**
  1069.      * @param null|string $licenseSerialNumbers
  1070.      */
  1071.     public function setLicenseSerialNumbers(?string $licenseSerialNumbers): void
  1072.     {
  1073.         $this->licenseSerialNumbers $licenseSerialNumbers;
  1074.     }
  1075.     /**
  1076.      * @param bool $licenseVisible
  1077.      */
  1078.     public function setLicenseVisible(bool $licenseVisible): void
  1079.     {
  1080.         $this->licenseVisible $licenseVisible;
  1081.     }
  1082.     /**
  1083.      * @param int $position
  1084.      */
  1085.     public function setPosition(int $position): void
  1086.     {
  1087.         $this->position $position;
  1088.     }
  1089.     /**
  1090.      * @param int|null $purchasePrice
  1091.      */
  1092.     public function setPurchasePrice(?int $purchasePrice): void
  1093.     {
  1094.         $this->purchasePrice $purchasePrice;
  1095.     }
  1096.     /**
  1097.      * @param int $quantity
  1098.      */
  1099.     public function setQuantity(int $quantity): void
  1100.     {
  1101.         $this->quantity $quantity;
  1102.     }
  1103.     /**
  1104.      * @param int|null $quantityPerPci
  1105.      */
  1106.     public function setQuantityPerPci(?int $quantityPerPci): void
  1107.     {
  1108.         $this->quantityPerPci $quantityPerPci;
  1109.     }
  1110.     /**
  1111.      * @param DateTime|null $serviceEnd
  1112.      */
  1113.     public function setServiceEnd(?DateTime $serviceEnd): void
  1114.     {
  1115.         $this->serviceEnd $serviceEnd;
  1116.     }
  1117.     /**
  1118.      * @param string|null $serviceId
  1119.      */
  1120.     public function setServiceId(?string $serviceId): void
  1121.     {
  1122.         $this->serviceId $serviceId;
  1123.     }
  1124.     /**
  1125.      * @param bool $servicePeriodVisible
  1126.      */
  1127.     public function setServicePeriodVisible(bool $servicePeriodVisible): void
  1128.     {
  1129.         $this->servicePeriodVisible $servicePeriodVisible;
  1130.     }
  1131.     /**
  1132.      * @param DateTime|null $serviceStart
  1133.      */
  1134.     public function setServiceStart(?DateTime $serviceStart): void
  1135.     {
  1136.         $this->serviceStart $serviceStart;
  1137.     }
  1138.     /**
  1139.      * @param Supplier|null $supplier
  1140.      */
  1141.     public function setSupplier(?Supplier $supplier): void
  1142.     {
  1143.         $this->supplier $supplier;
  1144.     }
  1145.     /**
  1146.      * @param string|null $unit
  1147.      */
  1148.     public function setUnit(?string $unit): void
  1149.     {
  1150.         $this->unit $unit;
  1151.     }
  1152.     /**
  1153.      * @param int|null $unitPrice
  1154.      */
  1155.     public function setUnitPrice(?int $unitPrice): void
  1156.     {
  1157.         $this->unitPrice $unitPrice;
  1158.     }
  1159.     /**
  1160.      * @param string|null $versionCode
  1161.      */
  1162.     public function setVersionCode(?string $versionCode): void
  1163.     {
  1164.         $this->versionCode $versionCode;
  1165.     }
  1166.     /**
  1167.      * @param Product $product
  1168.      */
  1169.     public function updateProductData(Product $product): void
  1170.     {
  1171.         $this->setCode($product->getCode());
  1172.         $this->setPlatform($product->getPlatform());
  1173.         $this->setType($product->getType());
  1174.         $this->setHeader($product->getHeader());
  1175.         $this->setCustomsTariff($product->getCustomsTariff());
  1176.         $this->setCommodityCode($product->getCustomsTariff());
  1177.         $this->setCountryOfOrigin($product->getCountryOfOrigin());
  1178.         $this->setExportLicense($product->isExportLicense());
  1179.         $this->setLicenseFile($product->isLicenseFile());
  1180.         $this->setFeatures($product->getFeatures());
  1181.     }
  1182.     /**
  1183.      * @Assert\Callback
  1184.      *
  1185.      * @param ExecutionContextInterface $context
  1186.      */
  1187.     public function validateDeliveriesQuantity(ExecutionContextInterface $context): void
  1188.     {
  1189.         $deliveredQuantity 0;
  1190.         foreach ($this->deliveries as $delivery) {
  1191.             $deliveredQuantity += $delivery->getQuantity();
  1192.         }
  1193.         if ($deliveredQuantity $this->quantity) {
  1194.             $context->buildViolation('The quantity of delivered items cannot be greater than the ordered quantity.')
  1195.                 ->atPath('deliveries')
  1196.                 ->addViolation()
  1197.             ;
  1198.         }
  1199.     }
  1200.     /**
  1201.      * @Assert\Callback
  1202.      *
  1203.      * @param ExecutionContextInterface $context
  1204.      */
  1205.     public function validateDiscountValue(ExecutionContextInterface $context): void
  1206.     {
  1207.         if ($this->discountValue !== null && $this->discountPercentage !== null) {
  1208.             $context->buildViolation('You cannot define both discount value and percentage at the same time')
  1209.                 ->atPath('discountValue')
  1210.                 ->addViolation()
  1211.             ;
  1212.         }
  1213.     }
  1214.     /**
  1215.      * @Assert\Callback(
  1216.      *     groups={"Invoice"}
  1217.      * )
  1218.      *
  1219.      * @param ExecutionContextInterface $context
  1220.      */
  1221.     public function validateExportLicenseNumber(ExecutionContextInterface $context): void
  1222.     {
  1223.         if ($this->exportLicense) {
  1224.             if (empty($this->exportLicenseNumberEu)) {
  1225.                 $context->buildViolation('You need to specify an export license number')
  1226.                     ->atPath('exportLicenseNumberEu')
  1227.                     ->addViolation()
  1228.                 ;
  1229.             }
  1230.             if (empty($this->exportLicenseNumberUs)) {
  1231.                 $context->buildViolation('You need to specify an export license number')
  1232.                     ->atPath('exportLicenseNumberUs')
  1233.                     ->addViolation()
  1234.                 ;
  1235.             }
  1236.         }
  1237.     }
  1238. }