vendor/bluue/products-bundle/src/Entity/Declination.php line 42

Open in your IDE?
  1. <?php
  2. /**
  3.  * @author Leo BANNHOLTZER (contact@scaledev.fr)
  4.  * @copyright 2021 - ScaleDEV SAS, 12 RUE CHARLES MORET, 10120 ST ANDRE LES VERGERS
  5.  * @license commercial
  6.  */
  7. declare(strict_types=1);
  8. namespace Bluue\ProductsBundle\Entity;
  9. use App\Entity\Context;
  10. use App\Entity\TaxRule;
  11. use App\Entity\Currency;
  12. use App\Entity\UnitMeasure;
  13. use Symfony\Component\Uid\Uuid;
  14. use Doctrine\ORM\Mapping as ORM;
  15. use Gedmo\Mapping\Annotation as Gedmo;
  16. use App\Entity\Traits\ContextServiceEntity;
  17. use Doctrine\Common\Collections\Collection;
  18. use Bluue\ProductsBundle\Entity\PackProduct;
  19. use App\Entity\Traits\TranslatorServiceEntity;
  20. use Doctrine\Common\Collections\ArrayCollection;
  21. use Bluue\ProductsBundle\Entity\DeclinationImage;
  22. use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
  23. use Bluue\ProductsBundle\Repository\DeclinationRepository;
  24. use App\DoctrineExtensions\Timestampable\Traits\UserTimestampableEntity;
  25. use App\DoctrineExtensions\SoftDeleteable\Traits\UserSoftDeleteableEntity;
  26. /**
  27.  * @ORM\Entity(repositoryClass=DeclinationRepository::class)
  28.  * @ORM\Table(name="products_bundle__declination", indexes={
  29.  *  @ORM\Index(name="initial_unit", columns={"initial_unit"}),
  30.  *  @ORM\Index(name="weight", columns={"weight"}),
  31.  *  @ORM\Index(name="deleted_at", columns={"deleted_at"}),
  32.  *  @ORM\Index(name="created_at", columns={"created_at"}),
  33.  *  @ORM\Index(name="updated_at", columns={"updated_at"})
  34.  * })
  35.  * @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false, hardDelete=false)
  36.  */
  37. class Declination
  38. {
  39.     use UserTimestampableEntity;
  40.     use UserSoftDeleteableEntity;
  41.     use TranslatorServiceEntity;
  42.     use ContextServiceEntity;
  43.     public const ATTRIBUTE_SEPARATOR ':';
  44.     public const SEPARATOR_BETWEEN_NAME_AND_ATTRIBUTE '-';
  45.     /**
  46.      * @ORM\Id
  47.      * @ORM\Column(type="uuid")
  48.      * @ORM\GeneratedValue(strategy="CUSTOM")
  49.      * @ORM\CustomIdGenerator(class=UuidGenerator::class)
  50.      */
  51.     private ?Uuid $id null;
  52.     /**
  53.      * @ORM\ManyToOne(targetEntity=Product::class, inversedBy="declinations")
  54.      * @ORM\JoinColumn(nullable=false)
  55.      */
  56.     private Product $product;
  57.     /**
  58.      * @ORM\OneToMany(
  59.      *  targetEntity=Ean13::class,
  60.      *  mappedBy="declination",
  61.      *  fetch="EXTRA_LAZY",
  62.      *  cascade={"persist", "remove"})
  63.      * )
  64.      */
  65.     private Collection $ean13s;
  66.     /**
  67.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable=true)
  68.      */
  69.     private ?string $initial_unit;
  70.     /**
  71.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable=true)
  72.      */
  73.     private ?string $weight;
  74.     /**
  75.      * @ORM\ManyToOne(targetEntity=Declination::class, inversedBy="virtualDeclinations")
  76.      */
  77.     private ?Declination $virtualParent null;
  78.     /**
  79.      * @ORM\Column(type="integer", nullable=true)
  80.      */
  81.     private ?int $virtualQuantity null;
  82.     /**
  83.      * @ORM\OneToMany(
  84.      *     targetEntity=Declination::class,
  85.      *     mappedBy="virtualParent",
  86.      *     fetch="EXTRA_LAZY"
  87.      * )
  88.      */
  89.     private Collection $virtualDeclinations;
  90.     /**
  91.      * @ORM\OneToMany(
  92.      *  targetEntity=DeclinationAttribute::class,
  93.      *  mappedBy="declination",
  94.      *  cascade={"remove"},
  95.      *  fetch="EXTRA_LAZY"
  96.      * )
  97.      */
  98.     private Collection $declinationAttributes;
  99.     /**
  100.      * @ORM\ManyToOne(targetEntity=UnitMeasure::class)
  101.      */
  102.     private ?UnitMeasure $unit_measure null;
  103.     /**
  104.      * @ORM\OneToMany(
  105.      *  targetEntity=DeclinationContext::class,
  106.      *  mappedBy="declination",
  107.      *  cascade={"remove"},
  108.      *  fetch="EXTRA_LAZY"
  109.      * )
  110.      */
  111.     private Collection $declinationContexts;
  112.     /**
  113.      * @ORM\OneToMany(targetEntity="DeclinationImage", mappedBy="declination", cascade={"remove"}, fetch="EXTRA_LAZY")
  114.      */
  115.     private Collection $declinationImages;
  116.     /**
  117.      * @ORM\OneToMany(targetEntity=PackProduct::class, mappedBy="declination", cascade={"remove"}, fetch="EXTRA_LAZY")
  118.      * @ORM\OrderBy({"position" = "ASC"})
  119.      */
  120.     private Collection $packProducts;
  121.     /**
  122.      * @ORM\OneToMany(
  123.      *  targetEntity=PackProduct::class,
  124.      *  mappedBy="parentDeclination",
  125.      *  cascade={"remove"},
  126.      *  fetch="EXTRA_LAZY"
  127.      * )
  128.      * @ORM\OrderBy({"position" = "ASC"})
  129.      */
  130.     private Collection $packProductsWithParentDeclination;
  131.     private ?Context $context null;
  132.     /**
  133.      * @ORM\Column(type="json")
  134.      */
  135.     private array $options = [];
  136.     // For stock bundle
  137.     private Collection $stockProducts;
  138.     private ?object $stockProductRoot null;
  139.     public $quantity_real null;
  140.     public $quantity_virtual null;
  141.     // For suppliers
  142.     private Collection $product_suppliers;
  143.     public function __construct()
  144.     {
  145.         $this->virtualDeclinations = new ArrayCollection();
  146.         $this->declinationAttributes = new ArrayCollection();
  147.         $this->declinationContexts = new ArrayCollection();
  148.         $this->declinationImages = new ArrayCollection();
  149.         $this->packProducts = new ArrayCollection();
  150.         $this->packProductsWithParentDeclination = new ArrayCollection();
  151.         $this->stockProducts = new ArrayCollection();
  152.         $this->ean13s = new ArrayCollection();
  153.         $this->product_suppliers = new ArrayCollection();
  154.     }
  155.     /**
  156.      * @return Uuid|null
  157.      */
  158.     public function getId(): ?Uuid
  159.     {
  160.         return $this->id;
  161.     }
  162.     /**
  163.      * @return Product|null
  164.      */
  165.     public function getProduct(): ?Product
  166.     {
  167.         return $this->product;
  168.     }
  169.     /**
  170.      * @param Product|null $product
  171.      * @return $this
  172.      */
  173.     public function setProduct(?Product $product): self
  174.     {
  175.         $this->product $product;
  176.         return $this;
  177.     }
  178.     /**
  179.      * Exemple : Nom Produit - Quantité : 50g
  180.      * @param ?bool $withProductName
  181.      * @param Context|null $context
  182.      * @return string|null
  183.      */
  184.     public function getName(?bool $withProductName true, ?Context $context null): ?string
  185.     {
  186.         $return $withProductName $this->getProduct()->getName($context) : '';
  187.         foreach ($this->getDeclinationAttributes() as $declinationAttribute) {
  188.             // ordonner par attribute groupe name
  189.             $return .= ' ' self::SEPARATOR_BETWEEN_NAME_AND_ATTRIBUTE ' ';
  190.             $attribute $declinationAttribute->getAttribute();
  191.             $return .= $attribute->getAttributeGroup()->getName() . ' ' self::ATTRIBUTE_SEPARATOR ' ';
  192.             $return .= $declinationAttribute->getAttribute()->getName();
  193.         }
  194.         return $return;
  195.     }
  196.     /**
  197.      * @return Collection|Ean13[]
  198.      */
  199.     public function getEan13s(): Collection
  200.     {
  201.         return $this->ean13s;
  202.     }
  203.     /**
  204.      * @param Ean13 $ean13
  205.      * @return $this
  206.      */
  207.     public function addEan13(Ean13 $ean13): self
  208.     {
  209.         if (!$this->ean13s->contains($ean13)) {
  210.             $this->ean13s[] = $ean13;
  211.             $ean13->setDeclination($this);
  212.         }
  213.         return $this;
  214.     }
  215.     /**
  216.      * @param Ean13 $ean13
  217.      * @return $this
  218.      */
  219.     public function removeEan13(Ean13 $ean13): self
  220.     {
  221.         $this->ean13s->removeElement($ean13);
  222.         return $this;
  223.     }
  224.     /**
  225.      * @return $this
  226.      */
  227.     public function removeAllEan13(): self
  228.     {
  229.         $this->ean13s->slice(0);
  230.         return $this;
  231.     }
  232.     public function getDefaultEan13()
  233.     {
  234.         $ean13 $this->ean13s->filter(function ($item) {
  235.             return $item->getIsDefault();
  236.         });
  237.         return count($ean13) ? $ean13->first() : false;
  238.     }
  239.     public function showDefaultEan13(): string
  240.     {
  241.         $default_ean13 $this->getDefaultEan13();
  242.         return $default_ean13 && !empty($default_ean13->getValue()) ? $default_ean13->getValue() : '';
  243.     }
  244.     /**
  245.      * @return string|null
  246.      */
  247.     public function getInitialUnit(): ?string
  248.     {
  249.         if ($this->isVirtual()) {
  250.             return (string) ((float) $this->virtualParent->getInitialUnit() * $this->virtualQuantity);
  251.         } else {
  252.             return $this->initial_unit;
  253.         }
  254.     }
  255.     /**
  256.      * @param string|null $initial_unit
  257.      * @return $this
  258.      */
  259.     public function setInitialUnit(?string $initial_unit): self
  260.     {
  261.         $this->initial_unit $initial_unit;
  262.         return $this;
  263.     }
  264.     /**
  265.      * @return string|null
  266.      */
  267.     public function getWeight(): ?string
  268.     {
  269.         if ($this->isVirtual()) {
  270.             return (string) ((float) $this->virtualParent->getWeight() * $this->virtualQuantity);
  271.         } else {
  272.             return $this->weight;
  273.         }
  274.     }
  275.     /**
  276.      * @param string|null $weight
  277.      * @return $this
  278.      */
  279.     public function setWeight(?string $weight): self
  280.     {
  281.         $this->weight $weight;
  282.         return $this;
  283.     }
  284.     /**
  285.      * @return Collection|Declination[]
  286.      */
  287.     public function getVirtualDeclinations(): Collection
  288.     {
  289.         return $this->virtualDeclinations;
  290.     }
  291.     /**
  292.      * @return Collection|DeclinationAttribute[]
  293.      */
  294.     public function getDeclinationAttributes(): Collection
  295.     {
  296.         return $this->declinationAttributes;
  297.     }
  298.     /**
  299.      * @param DeclinationAttribute $declinationAttribute
  300.      * @return $this
  301.      */
  302.     public function addDeclinationAttribute(DeclinationAttribute $declinationAttribute): self
  303.     {
  304.         if (!$this->declinationAttributes->contains($declinationAttribute)) {
  305.             $this->declinationAttributes[] = $declinationAttribute;
  306.             $declinationAttribute->setDeclination($this);
  307.         }
  308.         return $this;
  309.     }
  310.     /**
  311.      * @param DeclinationAttribute $declinationAttribute
  312.      * @return $this
  313.      */
  314.     public function removeDeclinationAttribute(DeclinationAttribute $declinationAttribute): self
  315.     {
  316.         if ($this->declinationAttributes->removeElement($declinationAttribute)) {
  317.             if ($declinationAttribute->getDeclination() === $this) {
  318.                 $declinationAttribute->setDeclination(null);
  319.             }
  320.         }
  321.         return $this;
  322.     }
  323.     /**
  324.      * @return UnitMeasure|null
  325.      */
  326.     public function getUnitMeasure(): ?UnitMeasure
  327.     {
  328.         if ($this->isVirtual() && $this->virtualParent->getUnitMeasure()) {
  329.             return $this->virtualParent->getUnitMeasure();
  330.         } elseif (!$this->unit_measure) {
  331.             return $this->product->getUnitMeasure();
  332.         }
  333.         return $this->unit_measure;
  334.     }
  335.     /**
  336.      * @param UnitMeasure|null $unit_measure
  337.      * @return $this
  338.      */
  339.     public function setUnitMeasure(?UnitMeasure $unit_measure): self
  340.     {
  341.         $this->unit_measure $unit_measure;
  342.         return $this;
  343.     }
  344.     /**
  345.      * @return array
  346.      */
  347.     public function getOptions(): array
  348.     {
  349.         return $this->options;
  350.     }
  351.     /**
  352.      * @param array $options
  353.      * @return $this
  354.      */
  355.     public function setOptions(array $options): self
  356.     {
  357.         $this->options $options;
  358.         return $this;
  359.     }
  360.     /**
  361.      * @param array $options
  362.      * @return $this
  363.      */
  364.     public function addOptions(array $options): self
  365.     {
  366.         return $this->setOptions(array_merge($this->options$options));
  367.     }
  368.     /**
  369.      * @return Collection|DeclinationContext[]
  370.      */
  371.     public function getDeclinationContexts(): Collection
  372.     {
  373.         return $this->declinationContexts;
  374.     }
  375.     /**
  376.      * @param DeclinationContext $declinationContext
  377.      * @return $this
  378.      */
  379.     public function addDeclinationContext(DeclinationContext $declinationContext): self
  380.     {
  381.         if (!$this->declinationContexts->contains($declinationContext)) {
  382.             $this->declinationContexts[] = $declinationContext;
  383.             $declinationContext->setDeclination($this);
  384.         }
  385.         return $this;
  386.     }
  387.     /**
  388.      * @param DeclinationContext $declinationContext
  389.      * @return $this
  390.      */
  391.     public function removeDeclinationContext(DeclinationContext $declinationContext): self
  392.     {
  393.         if ($this->declinationContexts->removeElement($declinationContext)) {
  394.             if ($declinationContext->getDeclination() === $this) {
  395.                 $declinationContext->setDeclination(null);
  396.             }
  397.         }
  398.         return $this;
  399.     }
  400.     /**
  401.      * @param ?Context $context
  402.      * @return DeclinationContext|null
  403.      */
  404.     public function getDeclinationContext(?Context $context null): ?DeclinationContext
  405.     {
  406.         $value null;
  407.         if ($this->getId()) {
  408.             if (!$this->context) {
  409.                 $this->context $context ?: $this->contextService->getActualOrDefault();
  410.             } elseif ($context && $context != $this->context) {
  411.                 $this->context $context;
  412.             }
  413.             foreach ($this->getDeclinationContexts() as $dc) {
  414.                 if ($dc->getContext() == $this->context) {
  415.                     $value $dc;
  416.                 }
  417.             }
  418.         }
  419.         return $value;
  420.     }
  421.     /**
  422.      * @return Collection|DeclinationImage[]
  423.      */
  424.     public function getDeclinationImages(): Collection
  425.     {
  426.         return $this->declinationImages;
  427.     }
  428.     /**
  429.      * @param ?Context $context
  430.      * @return ProductImage|null
  431.      */
  432.     public function getCoverImage(?Context $context null): ?ProductImage
  433.     {
  434.         $image null;
  435.         if ($this->getId() && $this->getProduct()) {
  436.             $image $this->getProduct()->getCoverImageOfProductContext($context);
  437.             $decliImages $this->getDeclinationImages($context)->getValues();
  438.             usort($decliImages, function (DeclinationImage $aDeclinationImage $b) {
  439.                 return $b->getProductImage()->getPosition() <=> $a->getProductImage()->getPosition();
  440.             });
  441.             foreach ($decliImages as $di) {
  442.                 $image $di->getProductImage();
  443.                 if ($di->getProductImage()->getIsCover()) {
  444.                     break;
  445.                 }
  446.             }
  447.         }
  448.         return $image;
  449.     }
  450.     /**
  451.      * @return Collection|PackProduct[]
  452.      */
  453.     public function getPackProducts(): Collection
  454.     {
  455.         return $this->packProducts;
  456.     }
  457.     /**
  458.      * @return Collection|PackProduct[]
  459.      */
  460.     public function getPackProductsWithParentDeclination(): Collection
  461.     {
  462.         return $this->packProductsWithParentDeclination;
  463.     }
  464.     /**
  465.      * @param ?Context $context
  466.      * @return string|null
  467.      */
  468.     public function getReference(?Context $context null): ?string
  469.     {
  470.         if (!$this->context) {
  471.             $this->context $context ?: $this->contextService->getActualOrDefault();
  472.         } elseif ($context && $context != $this->context) {
  473.             $this->context $context;
  474.         }
  475.         $value null;
  476.         foreach ($this->getDeclinationContexts() as $dc) {
  477.             if ($dc->getContext() == $this->context) {
  478.                 $value $dc->getReference();
  479.             }
  480.         }
  481.         return $value;
  482.     }
  483.     /**
  484.      * @param ?Context $context
  485.      * @return string|null
  486.      */
  487.     public function getWholesalePrice(?Context $context null): ?string
  488.     {
  489.         if (!$this->context) {
  490.             $this->context $context ?: $this->contextService->getActualOrDefault();
  491.         } elseif ($context && $context != $this->context) {
  492.             $this->context $context;
  493.         }
  494.         $value null;
  495.         foreach ($this->getDeclinationContexts() as $dc) {
  496.             if ($dc->getContext() == $this->context) {
  497.                 $value $dc->getWholesalePrice();
  498.             }
  499.         }
  500.         return $value;
  501.     }
  502.     /**
  503.      * @param ?Context $context
  504.      * @return string|null
  505.      */
  506.     public function getSellPrice(?Context $context null): ?string
  507.     {
  508.         if (!$this->context) {
  509.             $this->context $context ?: $this->contextService->getActualOrDefault();
  510.         } elseif ($context && $context != $this->context) {
  511.             $this->context $context;
  512.         }
  513.         $value null;
  514.         foreach ($this->getDeclinationContexts() as $dc) {
  515.             if ($dc->getContext() == $this->context) {
  516.                 $value $dc->getSellPrice();
  517.             }
  518.         }
  519.         return $value;
  520.     }
  521.     /**
  522.      * @param ?Context $context
  523.      * @return bool|null
  524.      */
  525.     public function getIsActive(?Context $context null): ?bool
  526.     {
  527.         if (!$this->context) {
  528.             $this->context $context ?: $this->contextService->getActualOrDefault();
  529.         } elseif ($context && $context != $this->context) {
  530.             $this->context $context;
  531.         }
  532.         $value null;
  533.         foreach ($this->getDeclinationContexts() as $dc) {
  534.             if ($dc->getContext() == $this->context) {
  535.                 $value $dc->getIsActive();
  536.             }
  537.         }
  538.         return $value;
  539.     }
  540.     /**
  541.      * @param ?Context $context
  542.      * @return bool|null
  543.      */
  544.     public function getIsDefault(?Context $context null): ?bool
  545.     {
  546.         if (!$this->context) {
  547.             $this->context $context ?: $this->contextService->getActualOrDefault();
  548.         } elseif ($context && $context != $this->context) {
  549.             $this->context $context;
  550.         }
  551.         $value null;
  552.         foreach ($this->getDeclinationContexts() as $dc) {
  553.             if ($dc->getContext() == $this->context) {
  554.                 $value $dc->getIsDefault();
  555.             }
  556.         }
  557.         return $value;
  558.     }
  559.     /**
  560.      * @param ?Context $context
  561.      * @param ?bool $withProductName
  562.      * @param bool $withRef
  563.      * @return string
  564.      */
  565.     public function getChoiceLabel(
  566.         ?Context $context null,
  567.         ?bool $withProductName true,
  568.         bool $withRef true
  569.     ): string {
  570.         if (
  571.             !$context
  572.             && !$this->getDeclinationContexts()->isEmpty()
  573.             && !$this->getDeclinationContext($this->context ?: $this->contextService->getActualOrDefault())
  574.         ) {
  575.             $context $this->getDeclinationContexts()->first()->getContext();
  576.         }
  577.         $label '';
  578.         if ($withRef && $ref $this->getReference($context)) {
  579.             $label .= '[' $ref '] ';
  580.         }
  581.         $label .= $this->getName($withProductName$context);
  582.         if (empty($this->getDeclinationAttributes()->getValues())) {
  583.             $label .= ' - ' $this->translatorService->trans('No attributes', [], 'ProductsBundle');
  584.         }
  585.         return $label;
  586.     }
  587.     // For stock bundle
  588.     /**
  589.      * @param Uuid|null $warehouseId
  590.      * @param Context|null $context
  591.      * @return Collection
  592.      */
  593.     public function getStockProducts(?Uuid $warehouseId null, ?Context $context null): Collection
  594.     {
  595.         if ($context) {
  596.             $stockProducts $this->stockProducts->filter(function ($stockProduct) use ($context) {
  597.                 return $stockProduct->getStockProductRootContexts()->filter(
  598.                     function ($stockProductRootContext) use ($context) {
  599.                         return $stockProductRootContext->getContext() == $context;
  600.                     }
  601.                 )->count() > 0;
  602.             });
  603.         } else {
  604.             $stockProducts $this->stockProducts;
  605.         }
  606.         if ($warehouseId) {
  607.             return $stockProducts->filter(function ($stockProduct) use ($warehouseId) {
  608.                 return $stockProduct->getWarehouse()->getId() == $warehouseId;
  609.             });
  610.         }
  611.         return $stockProducts;
  612.     }
  613.     /**
  614.      * @return object|null
  615.      */
  616.     public function getStockProductRoot(): ?object
  617.     {
  618.         return $this->stockProductRoot;
  619.     }
  620.     /**
  621.      * @param Context|null $context
  622.      * @return object|null
  623.      */
  624.     public function getStockProductRootContext(?Context $context null): ?object
  625.     {
  626.         if (!$this->context) {
  627.             $this->context $context ?: $this->contextService->getActualOrDefault();
  628.         } elseif ($context && $context != $this->context) {
  629.             $this->context $context;
  630.         }
  631.         return $this->getStockProductRoot() ?
  632.             $this->getStockProductRoot()->getStockProductRootContext($this->context) : null
  633.         ;
  634.     }
  635.     /**
  636.      * @param Uuid|null $warehouseId
  637.      * @return int
  638.      */
  639.     public function getStockProductsFinalQuantity(?Uuid $warehouseId null): int
  640.     {
  641.         $quantity 0;
  642.         if ($warehouseId) {
  643.             foreach ($this->getStockProducts($warehouseId) as $stock_product) {
  644.                 $quantity += $stock_product->getQuantityAvailable();
  645.             }
  646.         } else {
  647.             $quantity += $this->getStockProductRoot() ? $this->getStockProductRoot()->getQuantityAvailable() : 0;
  648.         }
  649.         return $quantity;
  650.     }
  651.     /**
  652.      * @param $stockProducts
  653.      * @return Declination
  654.      */
  655.     public function setStockProducts($stockProducts): self
  656.     {
  657.         $this->stockProducts $stockProducts;
  658.         return $this;
  659.     }
  660.     /**
  661.      * @return Declination|null
  662.      */
  663.     public function getVirtualParent(): ?Declination
  664.     {
  665.         return $this->virtualParent;
  666.     }
  667.     /**
  668.      * @param Declination|null $virtualParent
  669.      * @return Declination
  670.      */
  671.     public function setVirtualParent(?Declination $virtualParent): self
  672.     {
  673.         $this->virtualParent $virtualParent;
  674.         return $this;
  675.     }
  676.     /**
  677.      * @return int|null
  678.      */
  679.     public function getVirtualQuantity(): ?int
  680.     {
  681.         return $this->virtualQuantity;
  682.     }
  683.     /**
  684.      * @param int|null $virtualQuantity
  685.      * @return Declination
  686.      */
  687.     public function setVirtualQuantity(?int $virtualQuantity): self
  688.     {
  689.         $this->virtualQuantity $virtualQuantity;
  690.         return $this;
  691.     }
  692.     /**
  693.      * @return bool
  694.      */
  695.     public function isVirtual(): bool
  696.     {
  697.         return $this->virtualQuantity && $this->virtualParent;
  698.     }
  699.     /**
  700.      * @return bool|null
  701.      */
  702.     public function getIsPack(): ?bool
  703.     {
  704.         return count($this->getPackProductsWithParentDeclination()->getValues()) > 0;
  705.     }
  706.     /**
  707.      * @param Context|null $context
  708.      * @return Currency|null
  709.      */
  710.     public function getCurrency(?Context $context null): ?Currency
  711.     {
  712.         return $this->getProduct()->getCurrency($context);
  713.     }
  714.     /**
  715.      * @return TaxRule|null
  716.      */
  717.     public function getTaxRule(): ?TaxRule
  718.     {
  719.         return $this->getProduct()->getTaxRule();
  720.     }
  721.     /**
  722.      * @return void
  723.      */
  724.     public function removeContextFromEntity(): void
  725.     {
  726.         $this->context null;
  727.     }
  728. }