vendor/bluue/sales-bundle/src/Entity/QuotationLine.php line 39

Open in your IDE?
  1. <?php
  2. /**
  3.  * @author Quentin CHATELAIN (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\SalesBundle\Entity;
  9. use App\Entity\Traits\EntityManagerServiceEntity;
  10. use App\Services\SoftdeleteFilter;
  11. use DateTime;
  12. use App\Entity\TaxRule;
  13. use Doctrine\ORM\NonUniqueResultException;
  14. use Symfony\Component\Uid\Uuid;
  15. use Doctrine\ORM\Mapping as ORM;
  16. use Gedmo\Mapping\Annotation as Gedmo;
  17. use Doctrine\Common\Collections\Collection;
  18. use Doctrine\Common\Collections\ArrayCollection;
  19. use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
  20. use Bluue\SalesBundle\Repository\QuotationLineRepository;
  21. use App\DoctrineExtensions\Timestampable\Traits\UserTimestampableEntity;
  22. use App\DoctrineExtensions\SoftDeleteable\Traits\UserSoftDeleteableEntity;
  23. use Bluue\ProductsBundle\Entity\Declination;
  24. use Bluue\ProductsBundle\Entity\Product;
  25. /**
  26.  * @ORM\Entity(repositoryClass=QuotationLineRepository::class)
  27.  * @ORM\Table(name="sales_bundle__quotation_line", indexes={
  28.  *  @ORM\Index(name="deleted_at", columns={"deleted_at"}),
  29.  *  @ORM\Index(name="created_at", columns={"created_at"}),
  30.  *  @ORM\Index(name="updated_at", columns={"updated_at"})
  31.  * })
  32.  * @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false, hardDelete=false)
  33.  */
  34. class QuotationLine
  35. {
  36.     use UserTimestampableEntity;
  37.     use UserSoftDeleteableEntity;
  38.     use EntityManagerServiceEntity;
  39.     /**
  40.      * @ORM\Id
  41.      * @ORM\Column(type="uuid")
  42.      * @ORM\GeneratedValue(strategy="CUSTOM")
  43.      * @ORM\CustomIdGenerator(class=UuidGenerator::class)
  44.      */
  45.     private ?Uuid $id null;
  46.     /**
  47.      * @ORM\ManyToOne(targetEntity=Quotation::class, inversedBy="lines")
  48.      * @ORM\JoinColumn(nullable=false)
  49.      */
  50.     private ?Quotation $quotation null;
  51.     /**
  52.      * @ORM\ManyToOne(targetEntity=LineType::class)
  53.      */
  54.     private ?LineType $line_type null;
  55.     /**
  56.      * @ORM\ManyToOne(targetEntity=TaxRule::class)
  57.      */
  58.     private ?TaxRule $tax_rule null;
  59.     /**
  60.      * @ORM\ManyToOne(targetEntity="QuotationLine", inversedBy="childrens")
  61.      * @ORM\JoinColumn(name="parent_id", referencedColumnName="id")
  62.      */
  63.     private ?QuotationLine $parent null;
  64.     /**
  65.      * @ORM\Column(type="boolean")
  66.      */
  67.     private bool $is_group false;
  68.     /**
  69.      * @ORM\ManyToOne(targetEntity="QuotationLine", inversedBy="packChildrens")
  70.      * @ORM\JoinColumn(name="pack_parent_id", referencedColumnName="id")
  71.      */
  72.     private ?QuotationLine $packParent null;
  73.     /**
  74.      * @ORM\Column(type="boolean")
  75.      */
  76.     private bool $is_pack false;
  77.     /**
  78.      * @ORM\Column(type="string", length=128, nullable="true")
  79.      */
  80.     private ?string $reference null;
  81.     /**
  82.      * @ORM\Column(type="string", length=128, nullable="true")
  83.      */
  84.     private ?string $referenceBrand null;
  85.     /**
  86.      * @ORM\Column(type="string", length=255, nullable="true")
  87.      */
  88.     private ?string $name null;
  89.     /**
  90.      * @ORM\Column(type="text", nullable="true")
  91.      */
  92.     private ?string $description null;
  93.     /**
  94.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
  95.      */
  96.     private ?string $unit_price null;
  97.     /**
  98.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
  99.      */
  100.     private ?string $unitPriceWithTax null;
  101.     /**
  102.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
  103.      */
  104.     private ?string $quantity null;
  105.     /**
  106.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
  107.      */
  108.     private ?string $wholesale_price null;
  109.     /**
  110.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
  111.      */
  112.     private ?string $margin_ratio null;
  113.     /**
  114.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
  115.      */
  116.     private ?string $percentage_discount_untaxed null;
  117.     /**
  118.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
  119.      */
  120.     private ?string $total_discount_untaxed null;
  121.     /**
  122.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
  123.      */
  124.     private ?string $totalDiscount null;
  125.     /**
  126.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
  127.      */
  128.     private ?string $total_amount_no_discount_untaxed null;
  129.     /**
  130.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
  131.      */
  132.     private ?string $totalAmountNoDiscount null;
  133.     /**
  134.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
  135.      */
  136.     private ?string $total_amount_untaxed null;
  137.     /**
  138.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
  139.      */
  140.     private ?string $total_tax_amount null;
  141.     /**
  142.      * @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
  143.      */
  144.     private ?string $total_amount null;
  145.     /**
  146.      * @ORM\Column(type="integer")
  147.      */
  148.     private ?int $position null;
  149.     /**
  150.      * @ORM\Column(type="json")
  151.      */
  152.     private array $options = [];
  153.     /**
  154.      * @ORM\OneToMany(
  155.      *      targetEntity="QuotationLine",
  156.      *      mappedBy="parent",
  157.      *      cascade={"persist", "remove"},
  158.      *      fetch="EXTRA_LAZY"
  159.      * )
  160.      * @ORM\OrderBy({"position" = "ASC"})
  161.      */
  162.     private Collection $childrens;
  163.     /**
  164.      * @ORM\OneToMany(
  165.      *      targetEntity="QuotationLine",
  166.      *      mappedBy="packParent",
  167.      *      cascade={"persist", "remove"},
  168.      *      fetch="EXTRA_LAZY"
  169.      * )
  170.      * @ORM\OrderBy({"position" = "ASC"})
  171.      */
  172.     private Collection $packChildrens;
  173.     /**
  174.      * @ORM\ManyToOne(targetEntity=Product::class)
  175.      */
  176.     private ?Product $product null;
  177.     /**
  178.      * @ORM\ManyToOne(targetEntity=Declination::class)
  179.      */
  180.     private ?Declination $declination null;
  181.     public function __construct()
  182.     {
  183.         $this->childrens = new ArrayCollection();
  184.         $this->packChildrens = new ArrayCollection();
  185.     }
  186.     /**
  187.      * @return Uuid|null
  188.      */
  189.     public function getId(): ?Uuid
  190.     {
  191.         return $this->id;
  192.     }
  193.     /**
  194.      * @return QuotationLine
  195.      */
  196.     public function setId(): self
  197.     {
  198.         $this->id null;
  199.         return $this;
  200.     }
  201.     /**
  202.      * @return Quotation|null
  203.      */
  204.     public function getQuotation(): ?Quotation
  205.     {
  206.         return $this->quotation;
  207.     }
  208.     /**
  209.      * @param Quotation|null $quotation
  210.      * @return QuotationLine
  211.      */
  212.     public function setQuotation(?Quotation $quotation): self
  213.     {
  214.         $this->quotation $quotation;
  215.         return $this;
  216.     }
  217.     /**
  218.      * @return LineType|null
  219.      */
  220.     public function getLineType(): ?LineType
  221.     {
  222.         return $this->line_type;
  223.     }
  224.     /**
  225.      * @param LineType|null $line_type
  226.      * @return QuotationLine
  227.      */
  228.     public function setLineType(?LineType $line_type): self
  229.     {
  230.         $this->line_type $line_type;
  231.         return $this;
  232.     }
  233.     /**
  234.      * @return TaxRule|null
  235.      * @throws NonUniqueResultException
  236.      */
  237.     public function getTaxRule(): ?TaxRule
  238.     {
  239.         if ($this->em && $this->tax_rule) {
  240.             SoftdeleteFilter::disable($this->em, [TaxRule::class]);
  241.             $taxRule $this->em->getRepository(TaxRule::class)
  242.                 ->createQueryBuilder('tr')
  243.                 ->where('tr.id = :taxRuleId')
  244.                 ->setParameter('taxRuleId'$this->tax_rule->getId()->toBinary())
  245.                 ->getQuery()
  246.                 ->getOneOrNullResult();
  247.             SoftdeleteFilter::enable($this->em, [TaxRule::class]);
  248.             return $taxRule;
  249.         }
  250.         return $this->tax_rule;
  251.     }
  252.     /**
  253.      * @param TaxRule|null $tax_rule
  254.      * @return QuotationLine
  255.      */
  256.     public function setTaxRule(?TaxRule $tax_rule): self
  257.     {
  258.         $this->tax_rule $tax_rule;
  259.         return $this;
  260.     }
  261.     /**
  262.      * @return QuotationLine|null
  263.      */
  264.     public function getParent(): ?QuotationLine
  265.     {
  266.         return $this->parent;
  267.     }
  268.     /**
  269.      * @param QuotationLine|null $parent
  270.      * @return QuotationLine
  271.      */
  272.     public function setParent(?QuotationLine $parent): self
  273.     {
  274.         $this->parent $parent;
  275.         return $this;
  276.     }
  277.     /**
  278.      * @return bool
  279.      */
  280.     public function getIsGroup(): bool
  281.     {
  282.         return $this->is_group;
  283.     }
  284.     /**
  285.      * @param bool $is_group
  286.      * @return QuotationLine
  287.      */
  288.     public function setIsGroup(bool $is_group): self
  289.     {
  290.         $this->is_group $is_group;
  291.         return $this;
  292.     }
  293.     /**
  294.      * @return QuotationLine|null
  295.      */
  296.     public function getPackParent(): ?QuotationLine
  297.     {
  298.         return $this->packParent;
  299.     }
  300.     /**
  301.      * @param QuotationLine|null $packParent
  302.      * @return QuotationLine
  303.      */
  304.     public function setPackParent(?QuotationLine $packParent): self
  305.     {
  306.         $this->packParent $packParent;
  307.         return $this;
  308.     }
  309.     /**
  310.      * @return bool
  311.      */
  312.     public function getIsPack(): bool
  313.     {
  314.         return $this->is_pack;
  315.     }
  316.     /**
  317.      * @param bool $is_pack
  318.      * @return QuotationLine
  319.      */
  320.     public function setIsPack(bool $is_pack): self
  321.     {
  322.         $this->is_pack $is_pack;
  323.         return $this;
  324.     }
  325.     /**
  326.      * @return string|null
  327.      */
  328.     public function getReference(): ?string
  329.     {
  330.         return $this->reference;
  331.     }
  332.     /**
  333.      * @param string|null $reference
  334.      * @return QuotationLine
  335.      */
  336.     public function setReference(?string $reference): self
  337.     {
  338.         $this->reference $reference;
  339.         return $this;
  340.     }
  341.     /**
  342.      * @return string|null
  343.      */
  344.     public function getReferenceBrand(): ?string
  345.     {
  346.         return $this->referenceBrand;
  347.     }
  348.     /**
  349.      * @param string|null $referenceBrand
  350.      * @return QuotationLine
  351.      */
  352.     public function setReferenceBrand(?string $referenceBrand): self
  353.     {
  354.         $this->referenceBrand $referenceBrand;
  355.         return $this;
  356.     }
  357.     /**
  358.      * @return string|null
  359.      */
  360.     public function getName(): ?string
  361.     {
  362.         return $this->name;
  363.     }
  364.     /**
  365.      * @param string|null $name
  366.      * @return QuotationLine
  367.      */
  368.     public function setName(?string $name): self
  369.     {
  370.         $this->name $name;
  371.         return $this;
  372.     }
  373.     /**
  374.      * @return string|null
  375.      */
  376.     public function getDescription(): ?string
  377.     {
  378.         return $this->description;
  379.     }
  380.     /**
  381.      * @param string|null $description
  382.      * @return QuotationLine
  383.      */
  384.     public function setDescription(?string $description): self
  385.     {
  386.         $this->description $description;
  387.         return $this;
  388.     }
  389.     /**
  390.      * @return string|null
  391.      */
  392.     public function getUnitPrice(): ?string
  393.     {
  394.         return $this->unit_price;
  395.     }
  396.     /**
  397.      * @param string|null $unit_price
  398.      * @return QuotationLine
  399.      */
  400.     public function setUnitPrice(?string $unit_price): self
  401.     {
  402.         $this->unit_price $unit_price;
  403.         return $this;
  404.     }
  405.     /**
  406.      * @return string|null
  407.      */
  408.     public function getUnitPriceWithTax(): ?string
  409.     {
  410.         return $this->unitPriceWithTax;
  411.     }
  412.     /**
  413.      * @param string|null $unitPriceWithTax
  414.      * @return QuotationLine
  415.      */
  416.     public function setUnitPriceWithTax(?string $unitPriceWithTax): QuotationLine
  417.     {
  418.         $this->unitPriceWithTax $unitPriceWithTax;
  419.         return $this;
  420.     }
  421.     /**
  422.      * @return string|null
  423.      */
  424.     public function getQuantity(): ?string
  425.     {
  426.         return $this->quantity;
  427.     }
  428.     /**
  429.      * @param string|null $quantity
  430.      * @return QuotationLine
  431.      */
  432.     public function setQuantity(?string $quantity): self
  433.     {
  434.         $this->quantity $quantity;
  435.         return $this;
  436.     }
  437.     /**
  438.      * @return string|null
  439.      */
  440.     public function getWholesalePrice(): ?string
  441.     {
  442.         return $this->wholesale_price;
  443.     }
  444.     /**
  445.      * @param string|null $wholesale_price
  446.      * @return QuotationLine
  447.      */
  448.     public function setWholesalePrice(?string $wholesale_price): self
  449.     {
  450.         $this->wholesale_price $wholesale_price;
  451.         return $this;
  452.     }
  453.     /**
  454.      * @return string|null
  455.      */
  456.     public function getMarginRatio(): ?string
  457.     {
  458.         return $this->margin_ratio;
  459.     }
  460.     /**
  461.      * @param string|null $margin_ratio
  462.      * @return QuotationLine
  463.      */
  464.     public function setMarginRatio(?string $margin_ratio): self
  465.     {
  466.         $this->margin_ratio $margin_ratio;
  467.         return $this;
  468.     }
  469.     /**
  470.      * @param bool $calcWithPackaging
  471.      * @return float|null
  472.      */
  473.     public function getTotalMargin(bool $calcWithPackaging false): ?float
  474.     {
  475.         if ($this->wholesale_price === null) {
  476.             return 0;
  477.         }
  478.         if ($calcWithPackaging && !empty($this->options['unitQuantity'])) {
  479.             $quantity = (float) $this->options['unitQuantity'];
  480.         } else {
  481.             $quantity = (int) $this->quantity;
  482.         }
  483.         $sellPrice = (float) $this->total_amount_untaxed;
  484.         if (!empty($this->options['ecoPart']) && !empty($this->options['ecoPart']['amount'])) {
  485.             $sellPrice -= $this->options['ecoPart']['amount']  * $quantity;
  486.         }
  487.         return $sellPrice - (float) $this->wholesale_price $quantity;
  488.     }
  489.     /**
  490.      * @return string|null
  491.      */
  492.     public function getPercentageDiscountUntaxed(): ?string
  493.     {
  494.         return $this->percentage_discount_untaxed;
  495.     }
  496.     /**
  497.      * @param string|null $percentage_discount_untaxed
  498.      * @return QuotationLine
  499.      */
  500.     public function setPercentageDiscountUntaxed(?string $percentage_discount_untaxed): self
  501.     {
  502.         $this->percentage_discount_untaxed $percentage_discount_untaxed;
  503.         return $this;
  504.     }
  505.     /**
  506.      * @return string|null
  507.      */
  508.     public function getTotalDiscountUntaxed(): ?string
  509.     {
  510.         return $this->total_discount_untaxed;
  511.     }
  512.     /**
  513.      * @param string|null $total_discount_untaxed
  514.      * @return QuotationLine
  515.      */
  516.     public function setTotalDiscountUntaxed(?string $total_discount_untaxed): self
  517.     {
  518.         $this->total_discount_untaxed $total_discount_untaxed;
  519.         return $this;
  520.     }
  521.     /**
  522.      * @return string|null
  523.      */
  524.     public function getTotalDiscount(): ?string
  525.     {
  526.         return $this->totalDiscount;
  527.     }
  528.     /**
  529.      * @param string|null $totalDiscount
  530.      * @return QuotationLine
  531.      */
  532.     public function setTotalDiscount(?string $totalDiscount): QuotationLine
  533.     {
  534.         $this->totalDiscount $totalDiscount;
  535.         return $this;
  536.     }
  537.     /**
  538.      * @return string|null
  539.      */
  540.     public function getTotalAmountNoDiscountUntaxed(): ?string
  541.     {
  542.         return $this->total_amount_no_discount_untaxed;
  543.     }
  544.     /**
  545.      * @param string|null $total_amount_no_discount_untaxed
  546.      * @return QuotationLine
  547.      */
  548.     public function setTotalAmountNoDiscountUntaxed(?string $total_amount_no_discount_untaxed): self
  549.     {
  550.         $this->total_amount_no_discount_untaxed $total_amount_no_discount_untaxed;
  551.         return $this;
  552.     }
  553.     /**
  554.      * @return string|null
  555.      */
  556.     public function getTotalAmountNoDiscount(): ?string
  557.     {
  558.         return $this->totalAmountNoDiscount;
  559.     }
  560.     /**
  561.      * @param string|null $totalAmountNoDiscount
  562.      * @return QuotationLine
  563.      */
  564.     public function setTotalAmountNoDiscount(?string $totalAmountNoDiscount): QuotationLine
  565.     {
  566.         $this->totalAmountNoDiscount $totalAmountNoDiscount;
  567.         return $this;
  568.     }
  569.     /**
  570.      * @return string|null
  571.      */
  572.     public function getTotalAmountUntaxed(): ?string
  573.     {
  574.         return $this->total_amount_untaxed;
  575.     }
  576.     /**
  577.      * @param string|null $total_amount_untaxed
  578.      * @return QuotationLine
  579.      */
  580.     public function setTotalAmountUntaxed(?string $total_amount_untaxed): self
  581.     {
  582.         $this->total_amount_untaxed $total_amount_untaxed;
  583.         return $this;
  584.     }
  585.     /**
  586.      * @return string|null
  587.      */
  588.     public function getTotalTaxAmount(): ?string
  589.     {
  590.         return $this->total_tax_amount;
  591.     }
  592.     /**
  593.      * @param string|null $total_tax_amount
  594.      * @return QuotationLine
  595.      */
  596.     public function setTotalTaxAmount(?string $total_tax_amount): self
  597.     {
  598.         $this->total_tax_amount $total_tax_amount;
  599.         return $this;
  600.     }
  601.     /**
  602.      * @return string|null
  603.      */
  604.     public function getTotalAmount(): ?string
  605.     {
  606.         return $this->total_amount;
  607.     }
  608.     /**
  609.      * @param string|null $total_amount
  610.      * @return QuotationLine
  611.      */
  612.     public function setTotalAmount(?string $total_amount): self
  613.     {
  614.         $this->total_amount $total_amount;
  615.         return $this;
  616.     }
  617.     /**
  618.      * @return int|null
  619.      */
  620.     public function getPosition(): ?int
  621.     {
  622.         return $this->position;
  623.     }
  624.     /**
  625.      * @param int|null $position
  626.      * @return QuotationLine
  627.      */
  628.     public function setPosition(?int $position): self
  629.     {
  630.         $this->position $position;
  631.         return $this;
  632.     }
  633.     /**
  634.      * @return array
  635.      */
  636.     public function getOptions(): array
  637.     {
  638.         return $this->options;
  639.     }
  640.     /**
  641.      * @param array $options
  642.      * @return QuotationLine
  643.      */
  644.     public function setOptions(array $options): self
  645.     {
  646.         $this->options $options;
  647.         return $this;
  648.     }
  649.     /**
  650.      * @param array $options
  651.      * @return QuotationLine
  652.      */
  653.     public function addOptions(array $options): self
  654.     {
  655.         return $this->setOptions(array_merge($this->options$options));
  656.     }
  657.     /**
  658.      * @return Collection|QuotationLine[]
  659.      */
  660.     public function getChildrens(): Collection
  661.     {
  662.         return $this->childrens;
  663.     }
  664.     /**
  665.      * @param QuotationLine $line
  666.      * @return $this
  667.      */
  668.     public function addChildren(QuotationLine $line): self
  669.     {
  670.         if (!$this->childrens->contains($line)) {
  671.             $this->childrens[] = $line;
  672.             $line->setParent($this);
  673.             if ($line->getQuotation() !== $this->getQuotation()) {
  674.                 $line->setQuotation($this->getQuotation());
  675.             }
  676.         }
  677.         return $this;
  678.     }
  679.     /**
  680.      * @return Collection|QuotationLine[]
  681.      */
  682.     public function getPackChildrens(): Collection
  683.     {
  684.         return $this->packChildrens;
  685.     }
  686.     /**
  687.      * @param QuotationLine $line
  688.      * @return $this
  689.      */
  690.     public function addPackChildren(QuotationLine $line): self
  691.     {
  692.         if (!$this->packChildrens->contains($line)) {
  693.             $this->packChildrens[] = $line;
  694.             $line->setPackParent($this);
  695.             if ($line->getQuotation() !== $this->getQuotation()) {
  696.                 $line->setQuotation($this->getQuotation());
  697.             }
  698.         }
  699.         return $this;
  700.     }
  701.     /**
  702.      * @return Quotation|null
  703.      */
  704.     public function getDocument(): ?Quotation
  705.     {
  706.         return $this->getQuotation();
  707.     }
  708.     /**
  709.      * @return string|null
  710.      */
  711.     public function getUnitPriceWithDiscount(): ?string
  712.     {
  713.         if ($this->getDocument()->isEditPricesWithTax()) {
  714.             $unitDiscount = (float) ($this->getOptions()['unitDiscount'] ?? 0);
  715.             return (string) ((float) $this->getUnitPriceWithTax() - $unitDiscount);
  716.         } else {
  717.             $unitDiscountUntaxed = (float) ($this->getOptions()['unitDiscountUntaxed'] ?? 0);
  718.             return (string) ((float) $this->getUnitPrice() - $unitDiscountUntaxed);
  719.         }
  720.     }
  721.     /**
  722.      * @return bool
  723.      */
  724.     public function isProduct(): bool
  725.     {
  726.         $options $this->getOptions();
  727.         return !empty($options['product']) && !empty($options['product']['id']);
  728.     }
  729.     /**
  730.      * @return bool
  731.      */
  732.     public function isDeclination(): bool
  733.     {
  734.         return $this->isProduct() && !empty($this->getOptions()['product']['declination_id']);
  735.     }
  736.     /**
  737.      * @param Quotation $quotation
  738.      * @return QuotationLine
  739.      */
  740.     public function duplicate(Quotation $quotation): QuotationLine
  741.     {
  742.         if ($this->id) {
  743.             $clone = clone $this;
  744.             $clone->setId();
  745.             $clone->setQuotation($quotation);
  746.             $clone->setCreatedAt(new DateTime());
  747.             $clone->setCreatedBy(null);
  748.             $clone->setUpdatedAt(new DateTime());
  749.             $clone->setUpdatedBy(null);
  750.             $clone->childrens = new ArrayCollection();
  751.             foreach ($this->getChildrens() as $children) {
  752.                 $clone_children $children->duplicate($quotation);
  753.                 $clone->addChildren($clone_children);
  754.                 $children->packChildrens = new ArrayCollection();
  755.                 foreach ($children->getPackChildrens() as $packChildren) {
  756.                     $clone_pack_children $packChildren->duplicate($quotation);
  757.                     $children->addPackChildren($clone_pack_children);
  758.                 }
  759.             }
  760.             $clone->packChildrens = new ArrayCollection();
  761.             foreach ($this->getPackChildrens() as $packChildren) {
  762.                 $clone_pack_children $packChildren->duplicate($quotation);
  763.                 $clone->addPackChildren($clone_pack_children);
  764.             }
  765.             return $clone;
  766.         }
  767.         return $this;
  768.     }
  769.     /**
  770.      * @return Product|null
  771.      * @throws NonUniqueResultException
  772.      */
  773.     public function getProduct(): ?Product
  774.     {
  775.         if ($this->em && $this->product) {
  776.             SoftdeleteFilter::disable($this->em, [Product::class]);
  777.             $product $this->em->getRepository(Product::class)
  778.                 ->createQueryBuilder('p')
  779.                 ->where('p.id = :productId')
  780.                 ->setParameter('productId'$this->product->getId()->toBinary())
  781.                 ->getQuery()
  782.                 ->getOneOrNullResult();
  783.             SoftdeleteFilter::enable($this->em, [Product::class]);
  784.             return $product;
  785.         }
  786.         return $this->product;
  787.     }
  788.     /**
  789.      * @param Product|null $Product
  790.      * @return $this
  791.      */
  792.     public function setProduct(?Product $Product): self
  793.     {
  794.         $this->product $Product;
  795.         return $this;
  796.     }
  797.     /**
  798.      * @return Declination|null
  799.      * @throws NonUniqueResultException
  800.      */
  801.     public function getDeclination(): ?Declination
  802.     {
  803.         if ($this->em && $this->declination) {
  804.             SoftdeleteFilter::disable($this->em, [Declination::class]);
  805.             $declination $this->em->getRepository(Declination::class)
  806.                 ->createQueryBuilder('d')
  807.                 ->where('d.id = :declinationId')
  808.                 ->setParameter('declinationId'$this->declination->getId()->toBinary())
  809.                 ->getQuery()
  810.                 ->getOneOrNullResult();
  811.             SoftdeleteFilter::enable($this->em, [Declination::class]);
  812.             return $declination;
  813.         }
  814.         return $this->declination;
  815.     }
  816.     /**
  817.      * @param Declination|null $declination
  818.      * @return $this
  819.      */
  820.     public function setDeclination(?Declination $declination): self
  821.     {
  822.         $this->declination $declination;
  823.         return $this;
  824.     }
  825. }