<?php
/**
* @author Leo BANNHOLTZER (contact@scaledev.fr)
* @copyright 2021 - ScaleDEV SAS, 12 RUE CHARLES MORET, 10120 ST ANDRE LES VERGERS
* @license commercial
*/
declare(strict_types=1);
namespace Bluue\ProductsBundle\Entity;
use App\Entity\Context;
use App\Entity\TaxRule;
use App\Entity\Currency;
use App\Entity\UnitMeasure;
use Symfony\Component\Uid\Uuid;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use App\Entity\Traits\ContextServiceEntity;
use Doctrine\Common\Collections\Collection;
use Bluue\ProductsBundle\Entity\PackProduct;
use App\Entity\Traits\TranslatorServiceEntity;
use Doctrine\Common\Collections\ArrayCollection;
use Bluue\ProductsBundle\Entity\DeclinationImage;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use Bluue\ProductsBundle\Repository\DeclinationRepository;
use App\DoctrineExtensions\Timestampable\Traits\UserTimestampableEntity;
use App\DoctrineExtensions\SoftDeleteable\Traits\UserSoftDeleteableEntity;
/**
* @ORM\Entity(repositoryClass=DeclinationRepository::class)
* @ORM\Table(name="products_bundle__declination", indexes={
* @ORM\Index(name="initial_unit", columns={"initial_unit"}),
* @ORM\Index(name="weight", columns={"weight"}),
* @ORM\Index(name="deleted_at", columns={"deleted_at"}),
* @ORM\Index(name="created_at", columns={"created_at"}),
* @ORM\Index(name="updated_at", columns={"updated_at"})
* })
* @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false, hardDelete=false)
*/
class Declination
{
use UserTimestampableEntity;
use UserSoftDeleteableEntity;
use TranslatorServiceEntity;
use ContextServiceEntity;
public const ATTRIBUTE_SEPARATOR = ':';
public const SEPARATOR_BETWEEN_NAME_AND_ATTRIBUTE = '-';
/**
* @ORM\Id
* @ORM\Column(type="uuid")
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class=UuidGenerator::class)
*/
private ?Uuid $id = null;
/**
* @ORM\ManyToOne(targetEntity=Product::class, inversedBy="declinations")
* @ORM\JoinColumn(nullable=false)
*/
private Product $product;
/**
* @ORM\OneToMany(
* targetEntity=Ean13::class,
* mappedBy="declination",
* fetch="EXTRA_LAZY",
* cascade={"persist", "remove"})
* )
*/
private Collection $ean13s;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable=true)
*/
private ?string $initial_unit;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable=true)
*/
private ?string $weight;
/**
* @ORM\ManyToOne(targetEntity=Declination::class, inversedBy="virtualDeclinations")
*/
private ?Declination $virtualParent = null;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private ?int $virtualQuantity = null;
/**
* @ORM\OneToMany(
* targetEntity=Declination::class,
* mappedBy="virtualParent",
* fetch="EXTRA_LAZY"
* )
*/
private Collection $virtualDeclinations;
/**
* @ORM\OneToMany(
* targetEntity=DeclinationAttribute::class,
* mappedBy="declination",
* cascade={"remove"},
* fetch="EXTRA_LAZY"
* )
*/
private Collection $declinationAttributes;
/**
* @ORM\ManyToOne(targetEntity=UnitMeasure::class)
*/
private ?UnitMeasure $unit_measure = null;
/**
* @ORM\OneToMany(
* targetEntity=DeclinationContext::class,
* mappedBy="declination",
* cascade={"remove"},
* fetch="EXTRA_LAZY"
* )
*/
private Collection $declinationContexts;
/**
* @ORM\OneToMany(targetEntity="DeclinationImage", mappedBy="declination", cascade={"remove"}, fetch="EXTRA_LAZY")
*/
private Collection $declinationImages;
/**
* @ORM\OneToMany(targetEntity=PackProduct::class, mappedBy="declination", cascade={"remove"}, fetch="EXTRA_LAZY")
* @ORM\OrderBy({"position" = "ASC"})
*/
private Collection $packProducts;
/**
* @ORM\OneToMany(
* targetEntity=PackProduct::class,
* mappedBy="parentDeclination",
* cascade={"remove"},
* fetch="EXTRA_LAZY"
* )
* @ORM\OrderBy({"position" = "ASC"})
*/
private Collection $packProductsWithParentDeclination;
private ?Context $context = null;
/**
* @ORM\Column(type="json")
*/
private array $options = [];
// For stock bundle
private Collection $stockProducts;
private ?object $stockProductRoot = null;
public $quantity_real = null;
public $quantity_virtual = null;
// For suppliers
private Collection $product_suppliers;
public function __construct()
{
$this->virtualDeclinations = new ArrayCollection();
$this->declinationAttributes = new ArrayCollection();
$this->declinationContexts = new ArrayCollection();
$this->declinationImages = new ArrayCollection();
$this->packProducts = new ArrayCollection();
$this->packProductsWithParentDeclination = new ArrayCollection();
$this->stockProducts = new ArrayCollection();
$this->ean13s = new ArrayCollection();
$this->product_suppliers = new ArrayCollection();
}
/**
* @return Uuid|null
*/
public function getId(): ?Uuid
{
return $this->id;
}
/**
* @return Product|null
*/
public function getProduct(): ?Product
{
return $this->product;
}
/**
* @param Product|null $product
* @return $this
*/
public function setProduct(?Product $product): self
{
$this->product = $product;
return $this;
}
/**
* Exemple : Nom Produit - Quantité : 50g
* @param ?bool $withProductName
* @param Context|null $context
* @return string|null
*/
public function getName(?bool $withProductName = true, ?Context $context = null): ?string
{
$return = $withProductName ? $this->getProduct()->getName($context) : '';
foreach ($this->getDeclinationAttributes() as $declinationAttribute) {
// ordonner par attribute groupe name
$return .= ' ' . self::SEPARATOR_BETWEEN_NAME_AND_ATTRIBUTE . ' ';
$attribute = $declinationAttribute->getAttribute();
$return .= $attribute->getAttributeGroup()->getName() . ' ' . self::ATTRIBUTE_SEPARATOR . ' ';
$return .= $declinationAttribute->getAttribute()->getName();
}
return $return;
}
/**
* @return Collection|Ean13[]
*/
public function getEan13s(): Collection
{
return $this->ean13s;
}
/**
* @param Ean13 $ean13
* @return $this
*/
public function addEan13(Ean13 $ean13): self
{
if (!$this->ean13s->contains($ean13)) {
$this->ean13s[] = $ean13;
$ean13->setDeclination($this);
}
return $this;
}
/**
* @param Ean13 $ean13
* @return $this
*/
public function removeEan13(Ean13 $ean13): self
{
$this->ean13s->removeElement($ean13);
return $this;
}
/**
* @return $this
*/
public function removeAllEan13(): self
{
$this->ean13s->slice(0);
return $this;
}
public function getDefaultEan13()
{
$ean13 = $this->ean13s->filter(function ($item) {
return $item->getIsDefault();
});
return count($ean13) ? $ean13->first() : false;
}
public function showDefaultEan13(): string
{
$default_ean13 = $this->getDefaultEan13();
return $default_ean13 && !empty($default_ean13->getValue()) ? $default_ean13->getValue() : '';
}
/**
* @return string|null
*/
public function getInitialUnit(): ?string
{
if ($this->isVirtual()) {
return (string) ((float) $this->virtualParent->getInitialUnit() * $this->virtualQuantity);
} else {
return $this->initial_unit;
}
}
/**
* @param string|null $initial_unit
* @return $this
*/
public function setInitialUnit(?string $initial_unit): self
{
$this->initial_unit = $initial_unit;
return $this;
}
/**
* @return string|null
*/
public function getWeight(): ?string
{
if ($this->isVirtual()) {
return (string) ((float) $this->virtualParent->getWeight() * $this->virtualQuantity);
} else {
return $this->weight;
}
}
/**
* @param string|null $weight
* @return $this
*/
public function setWeight(?string $weight): self
{
$this->weight = $weight;
return $this;
}
/**
* @return Collection|Declination[]
*/
public function getVirtualDeclinations(): Collection
{
return $this->virtualDeclinations;
}
/**
* @return Collection|DeclinationAttribute[]
*/
public function getDeclinationAttributes(): Collection
{
return $this->declinationAttributes;
}
/**
* @param DeclinationAttribute $declinationAttribute
* @return $this
*/
public function addDeclinationAttribute(DeclinationAttribute $declinationAttribute): self
{
if (!$this->declinationAttributes->contains($declinationAttribute)) {
$this->declinationAttributes[] = $declinationAttribute;
$declinationAttribute->setDeclination($this);
}
return $this;
}
/**
* @param DeclinationAttribute $declinationAttribute
* @return $this
*/
public function removeDeclinationAttribute(DeclinationAttribute $declinationAttribute): self
{
if ($this->declinationAttributes->removeElement($declinationAttribute)) {
if ($declinationAttribute->getDeclination() === $this) {
$declinationAttribute->setDeclination(null);
}
}
return $this;
}
/**
* @return UnitMeasure|null
*/
public function getUnitMeasure(): ?UnitMeasure
{
if ($this->isVirtual() && $this->virtualParent->getUnitMeasure()) {
return $this->virtualParent->getUnitMeasure();
} elseif (!$this->unit_measure) {
return $this->product->getUnitMeasure();
}
return $this->unit_measure;
}
/**
* @param UnitMeasure|null $unit_measure
* @return $this
*/
public function setUnitMeasure(?UnitMeasure $unit_measure): self
{
$this->unit_measure = $unit_measure;
return $this;
}
/**
* @return array
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @param array $options
* @return $this
*/
public function setOptions(array $options): self
{
$this->options = $options;
return $this;
}
/**
* @param array $options
* @return $this
*/
public function addOptions(array $options): self
{
return $this->setOptions(array_merge($this->options, $options));
}
/**
* @return Collection|DeclinationContext[]
*/
public function getDeclinationContexts(): Collection
{
return $this->declinationContexts;
}
/**
* @param DeclinationContext $declinationContext
* @return $this
*/
public function addDeclinationContext(DeclinationContext $declinationContext): self
{
if (!$this->declinationContexts->contains($declinationContext)) {
$this->declinationContexts[] = $declinationContext;
$declinationContext->setDeclination($this);
}
return $this;
}
/**
* @param DeclinationContext $declinationContext
* @return $this
*/
public function removeDeclinationContext(DeclinationContext $declinationContext): self
{
if ($this->declinationContexts->removeElement($declinationContext)) {
if ($declinationContext->getDeclination() === $this) {
$declinationContext->setDeclination(null);
}
}
return $this;
}
/**
* @param ?Context $context
* @return DeclinationContext|null
*/
public function getDeclinationContext(?Context $context = null): ?DeclinationContext
{
$value = null;
if ($this->getId()) {
if (!$this->context) {
$this->context = $context ?: $this->contextService->getActualOrDefault();
} elseif ($context && $context != $this->context) {
$this->context = $context;
}
foreach ($this->getDeclinationContexts() as $dc) {
if ($dc->getContext() == $this->context) {
$value = $dc;
}
}
}
return $value;
}
/**
* @return Collection|DeclinationImage[]
*/
public function getDeclinationImages(): Collection
{
return $this->declinationImages;
}
/**
* @param ?Context $context
* @return ProductImage|null
*/
public function getCoverImage(?Context $context = null): ?ProductImage
{
$image = null;
if ($this->getId() && $this->getProduct()) {
$image = $this->getProduct()->getCoverImageOfProductContext($context);
$decliImages = $this->getDeclinationImages($context)->getValues();
usort($decliImages, function (DeclinationImage $a, DeclinationImage $b) {
return $b->getProductImage()->getPosition() <=> $a->getProductImage()->getPosition();
});
foreach ($decliImages as $di) {
$image = $di->getProductImage();
if ($di->getProductImage()->getIsCover()) {
break;
}
}
}
return $image;
}
/**
* @return Collection|PackProduct[]
*/
public function getPackProducts(): Collection
{
return $this->packProducts;
}
/**
* @return Collection|PackProduct[]
*/
public function getPackProductsWithParentDeclination(): Collection
{
return $this->packProductsWithParentDeclination;
}
/**
* @param ?Context $context
* @return string|null
*/
public function getReference(?Context $context = null): ?string
{
if (!$this->context) {
$this->context = $context ?: $this->contextService->getActualOrDefault();
} elseif ($context && $context != $this->context) {
$this->context = $context;
}
$value = null;
foreach ($this->getDeclinationContexts() as $dc) {
if ($dc->getContext() == $this->context) {
$value = $dc->getReference();
}
}
return $value;
}
/**
* @param ?Context $context
* @return string|null
*/
public function getWholesalePrice(?Context $context = null): ?string
{
if (!$this->context) {
$this->context = $context ?: $this->contextService->getActualOrDefault();
} elseif ($context && $context != $this->context) {
$this->context = $context;
}
$value = null;
foreach ($this->getDeclinationContexts() as $dc) {
if ($dc->getContext() == $this->context) {
$value = $dc->getWholesalePrice();
}
}
return $value;
}
/**
* @param ?Context $context
* @return string|null
*/
public function getSellPrice(?Context $context = null): ?string
{
if (!$this->context) {
$this->context = $context ?: $this->contextService->getActualOrDefault();
} elseif ($context && $context != $this->context) {
$this->context = $context;
}
$value = null;
foreach ($this->getDeclinationContexts() as $dc) {
if ($dc->getContext() == $this->context) {
$value = $dc->getSellPrice();
}
}
return $value;
}
/**
* @param ?Context $context
* @return bool|null
*/
public function getIsActive(?Context $context = null): ?bool
{
if (!$this->context) {
$this->context = $context ?: $this->contextService->getActualOrDefault();
} elseif ($context && $context != $this->context) {
$this->context = $context;
}
$value = null;
foreach ($this->getDeclinationContexts() as $dc) {
if ($dc->getContext() == $this->context) {
$value = $dc->getIsActive();
}
}
return $value;
}
/**
* @param ?Context $context
* @return bool|null
*/
public function getIsDefault(?Context $context = null): ?bool
{
if (!$this->context) {
$this->context = $context ?: $this->contextService->getActualOrDefault();
} elseif ($context && $context != $this->context) {
$this->context = $context;
}
$value = null;
foreach ($this->getDeclinationContexts() as $dc) {
if ($dc->getContext() == $this->context) {
$value = $dc->getIsDefault();
}
}
return $value;
}
/**
* @param ?Context $context
* @param ?bool $withProductName
* @param bool $withRef
* @return string
*/
public function getChoiceLabel(
?Context $context = null,
?bool $withProductName = true,
bool $withRef = true
): string {
if (
!$context
&& !$this->getDeclinationContexts()->isEmpty()
&& !$this->getDeclinationContext($this->context ?: $this->contextService->getActualOrDefault())
) {
$context = $this->getDeclinationContexts()->first()->getContext();
}
$label = '';
if ($withRef && $ref = $this->getReference($context)) {
$label .= '[' . $ref . '] ';
}
$label .= $this->getName($withProductName, $context);
if (empty($this->getDeclinationAttributes()->getValues())) {
$label .= ' - ' . $this->translatorService->trans('No attributes', [], 'ProductsBundle');
}
return $label;
}
// For stock bundle
/**
* @param Uuid|null $warehouseId
* @param Context|null $context
* @return Collection
*/
public function getStockProducts(?Uuid $warehouseId = null, ?Context $context = null): Collection
{
if ($context) {
$stockProducts = $this->stockProducts->filter(function ($stockProduct) use ($context) {
return $stockProduct->getStockProductRootContexts()->filter(
function ($stockProductRootContext) use ($context) {
return $stockProductRootContext->getContext() == $context;
}
)->count() > 0;
});
} else {
$stockProducts = $this->stockProducts;
}
if ($warehouseId) {
return $stockProducts->filter(function ($stockProduct) use ($warehouseId) {
return $stockProduct->getWarehouse()->getId() == $warehouseId;
});
}
return $stockProducts;
}
/**
* @return object|null
*/
public function getStockProductRoot(): ?object
{
return $this->stockProductRoot;
}
/**
* @param Context|null $context
* @return object|null
*/
public function getStockProductRootContext(?Context $context = null): ?object
{
if (!$this->context) {
$this->context = $context ?: $this->contextService->getActualOrDefault();
} elseif ($context && $context != $this->context) {
$this->context = $context;
}
return $this->getStockProductRoot() ?
$this->getStockProductRoot()->getStockProductRootContext($this->context) : null
;
}
/**
* @param Uuid|null $warehouseId
* @return int
*/
public function getStockProductsFinalQuantity(?Uuid $warehouseId = null): int
{
$quantity = 0;
if ($warehouseId) {
foreach ($this->getStockProducts($warehouseId) as $stock_product) {
$quantity += $stock_product->getQuantityAvailable();
}
} else {
$quantity += $this->getStockProductRoot() ? $this->getStockProductRoot()->getQuantityAvailable() : 0;
}
return $quantity;
}
/**
* @param $stockProducts
* @return Declination
*/
public function setStockProducts($stockProducts): self
{
$this->stockProducts = $stockProducts;
return $this;
}
/**
* @return Declination|null
*/
public function getVirtualParent(): ?Declination
{
return $this->virtualParent;
}
/**
* @param Declination|null $virtualParent
* @return Declination
*/
public function setVirtualParent(?Declination $virtualParent): self
{
$this->virtualParent = $virtualParent;
return $this;
}
/**
* @return int|null
*/
public function getVirtualQuantity(): ?int
{
return $this->virtualQuantity;
}
/**
* @param int|null $virtualQuantity
* @return Declination
*/
public function setVirtualQuantity(?int $virtualQuantity): self
{
$this->virtualQuantity = $virtualQuantity;
return $this;
}
/**
* @return bool
*/
public function isVirtual(): bool
{
return $this->virtualQuantity && $this->virtualParent;
}
/**
* @return bool|null
*/
public function getIsPack(): ?bool
{
return count($this->getPackProductsWithParentDeclination()->getValues()) > 0;
}
/**
* @param Context|null $context
* @return Currency|null
*/
public function getCurrency(?Context $context = null): ?Currency
{
return $this->getProduct()->getCurrency($context);
}
/**
* @return TaxRule|null
*/
public function getTaxRule(): ?TaxRule
{
return $this->getProduct()->getTaxRule();
}
/**
* @return void
*/
public function removeContextFromEntity(): void
{
$this->context = null;
}
}