<?php
/**
* @author Quentin CHATELAIN (contact@scaledev.fr)
* @copyright 2021 - ScaleDEV SAS, 12 RUE CHARLES MORET, 10120 ST ANDRE LES VERGERS
* @license commercial
*/
declare(strict_types=1);
namespace Bluue\SalesBundle\Entity;
use DateTime;
use App\Entity\TaxRule;
use Symfony\Component\Uid\Uuid;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Bluue\SalesBundle\Repository\OrderLineRepository;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use App\DoctrineExtensions\Cancelable\Traits\UserCancelableEntity;
use App\DoctrineExtensions\Timestampable\Traits\UserTimestampableEntity;
use App\DoctrineExtensions\SoftDeleteable\Traits\UserSoftDeleteableEntity;
use Bluue\ProductsBundle\Entity\Declination;
use Bluue\ProductsBundle\Entity\Product;
/**
* @ORM\Entity(repositoryClass=OrderLineRepository::class)
* @ORM\Table(name="sales_bundle__order_line", indexes={
* @ORM\Index(name="deleted_at", columns={"deleted_at"}),
* @ORM\Index(name="created_at", columns={"created_at"}),
* @ORM\Index(name="updated_at", columns={"updated_at"}),
* @ORM\Index(name="canceled_at", columns={"canceled_at"})
* })
* @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false, hardDelete=true)
*/
class OrderLine
{
use UserTimestampableEntity;
use UserSoftDeleteableEntity;
use UserCancelableEntity;
/**
* @ORM\Id
* @ORM\Column(type="uuid")
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class=UuidGenerator::class)
*/
private ?Uuid $id = null;
/**
* @ORM\ManyToOne(targetEntity=Order::class, inversedBy="lines")
* @ORM\JoinColumn(nullable=false)
*/
private ?Order $order = null;
/**
* @ORM\ManyToOne(targetEntity=LineType::class)
*/
private ?LineType $line_type = null;
/**
* @ORM\ManyToOne(targetEntity=TaxRule::class)
*/
private ?TaxRule $tax_rule = null;
/**
* @ORM\ManyToOne(targetEntity="OrderLine", inversedBy="childrens")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id")
*/
private ?OrderLine $parent = null;
/**
* @ORM\Column(type="boolean")
*/
private bool $is_group = false;
/**
* @ORM\ManyToOne(targetEntity="OrderLine", inversedBy="packChildrens")
* @ORM\JoinColumn(name="pack_parent_id", referencedColumnName="id")
*/
private ?OrderLine $packParent = null;
/**
* @ORM\Column(type="boolean")
*/
private bool $is_pack = false;
/**
* @ORM\Column(type="string", length=128, nullable="true")
*/
private ?string $reference = null;
/**
* @ORM\Column(type="string", length=128, nullable="true")
*/
private ?string $referenceBrand = null;
/**
* @ORM\Column(type="string", length=255, nullable="true")
*/
private ?string $name = null;
/**
* @ORM\Column(type="text", nullable="true")
*/
private ?string $description = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $unit_price = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $unitPriceWithTax = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $quantity = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $quantity_delivered = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $wholesale_price = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $margin_ratio = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $percentage_discount_untaxed = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $total_discount_untaxed = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $totalDiscount = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $total_amount_no_discount_untaxed = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $totalAmountNoDiscount = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $total_amount_untaxed = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $total_tax_amount = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=6, nullable="true")
*/
private ?string $total_amount = null;
/**
* @ORM\Column(type="integer")
*/
private ?int $position = null;
/**
* @ORM\Column(type="json")
*/
private array $options = [];
/**
* @ORM\OneToMany(targetEntity="OrderLine", mappedBy="parent", cascade={"persist", "remove"}, fetch="EXTRA_LAZY")
* @ORM\OrderBy({"position" = "ASC"})
*/
private Collection $childrens;
/**
* @ORM\OneToMany(
* targetEntity="OrderLine",
* mappedBy="packParent",
* cascade={"persist", "remove"},
* fetch="EXTRA_LAZY"
* )
* @ORM\OrderBy({"position" = "ASC"})
*/
private Collection $packChildrens;
private $stockOrderLine;
private bool $stockReserved = false;
/**
* @ORM\ManyToOne(targetEntity=Product::class)
*/
private ?Product $product = null;
/**
* @ORM\ManyToOne(targetEntity=Declination::class)
*/
private ?Declination $declination = null;
public function __construct()
{
$this->childrens = new ArrayCollection();
$this->packChildrens = new ArrayCollection();
}
/**
* @return Uuid|null
*/
public function getId(): ?Uuid
{
return $this->id;
}
/**
* @return OrderLine
*/
public function setId(): self
{
$this->id = null;
return $this;
}
/**
* @return Order|null
*/
public function getOrder(): ?Order
{
return $this->order;
}
/**
* @param Order $order
* @return OrderLine
*/
public function setOrder(Order $order): self
{
$this->order = $order;
return $this;
}
/**
* @return LineType|null
*/
public function getLineType(): ?LineType
{
return $this->line_type;
}
/**
* @param LineType|null $line_type
* @return OrderLine
*/
public function setLineType(?LineType $line_type): self
{
$this->line_type = $line_type;
return $this;
}
/**
* @return TaxRule|null
*/
public function getTaxRule(): ?TaxRule
{
return $this->tax_rule;
}
/**
* @param TaxRule|null $tax_rule
* @return OrderLine
*/
public function setTaxRule(?TaxRule $tax_rule): self
{
$this->tax_rule = $tax_rule;
return $this;
}
/**
* @return OrderLine|null
*/
public function getParent(): ?OrderLine
{
return $this->parent;
}
/**
* @param OrderLine|null $parent
* @return OrderLine
*/
public function setParent(?OrderLine $parent): self
{
$this->parent = $parent;
return $this;
}
/**
* @return bool
*/
public function getIsGroup(): bool
{
return $this->is_group;
}
/**
* @param bool $is_group
* @return OrderLine
*/
public function setIsGroup(bool $is_group): self
{
$this->is_group = $is_group;
return $this;
}
/**
* @return OrderLine|null
*/
public function getPackParent(): ?OrderLine
{
return $this->packParent;
}
/**
* @param OrderLine|null $packParent
* @return OrderLine
*/
public function setPackParent(?OrderLine $packParent): self
{
$this->packParent = $packParent;
return $this;
}
/**
* @return bool
*/
public function getIsPack(): bool
{
return $this->is_pack;
}
/**
* @param bool $is_pack
* @return OrderLine
*/
public function setIsPack(bool $is_pack): self
{
$this->is_pack = $is_pack;
return $this;
}
/**
* @return string|null
*/
public function getReference(): ?string
{
return $this->reference;
}
/**
* @param string|null $reference
* @return OrderLine
*/
public function setReference(?string $reference): self
{
$this->reference = $reference;
return $this;
}
/**
* @return string|null
*/
public function getReferenceBrand(): ?string
{
return $this->referenceBrand;
}
/**
* @param string|null $referenceBrand
* @return OrderLine
*/
public function setReferenceBrand(?string $referenceBrand): self
{
$this->referenceBrand = $referenceBrand;
return $this;
}
/**
* @return string|null
*/
public function getName(): ?string
{
return $this->name;
}
/**
* @param string|null $name
* @return OrderLine
*/
public function setName(?string $name): self
{
$this->name = $name;
return $this;
}
/**
* @return string|null
*/
public function getDescription(): ?string
{
return $this->description;
}
/**
* @param string|null $description
* @return OrderLine
*/
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
/**
* @return string|null
*/
public function getUnitPrice(): ?string
{
return $this->unit_price;
}
/**
* @param string|null $unit_price
* @return OrderLine
*/
public function setUnitPrice(?string $unit_price): self
{
$this->unit_price = $unit_price;
return $this;
}
/**
* @return string|null
*/
public function getUnitPriceWithTax(): ?string
{
return $this->unitPriceWithTax;
}
/**
* @param string|null $unitPriceWithTax
* @return OrderLine
*/
public function setUnitPriceWithTax(?string $unitPriceWithTax): OrderLine
{
$this->unitPriceWithTax = $unitPriceWithTax;
return $this;
}
/**
* @return string|null
*/
public function getQuantity(): ?string
{
return $this->quantity;
}
/**
* @param string|null $quantity
* @return OrderLine
*/
public function setQuantity(?string $quantity): self
{
$this->quantity = $quantity;
return $this;
}
/**
* @return string|null
*/
public function getQuantityDelivered(): ?string
{
return $this->quantity_delivered;
}
/**
* @param string|null $quantity_delivered
* @return OrderLine
*/
public function setQuantityDelivered(?string $quantity_delivered): self
{
$this->quantity_delivered = $quantity_delivered;
return $this;
}
/**
* @param bool $virtual
* @return string|null
*/
public function getRemainingQuantity(bool $virtual = false): ?string
{
if ($this->isCanceled()) {
return '0';
}
$quantity = (int) $this->quantity - (int) $this->quantity_delivered;
if ($virtual && !empty($this->options['product']['virtual_quantity'])) {
return (string) ($quantity * $this->options['product']['virtual_quantity']);
} else {
return (string) $quantity;
}
}
/**
* @return string|null
*/
public function getWholesalePrice(): ?string
{
return $this->wholesale_price;
}
/**
* @param string|null $wholesale_price
* @return OrderLine
*/
public function setWholesalePrice(?string $wholesale_price): self
{
$this->wholesale_price = $wholesale_price;
return $this;
}
/**
* @return string|null
*/
public function getMarginRatio(): ?string
{
return $this->margin_ratio;
}
/**
* @param string|null $margin_ratio
* @return OrderLine
*/
public function setMarginRatio(?string $margin_ratio): self
{
$this->margin_ratio = $margin_ratio;
return $this;
}
/**
* @param bool $calcWithPackaging
* @return float|null
*/
public function getTotalMargin(bool $calcWithPackaging = false): ?float
{
if ($this->wholesale_price === null) {
return 0;
}
if ($calcWithPackaging && !empty($this->options['unitQuantity'])) {
$quantity = (float) $this->options['unitQuantity'];
} else {
$quantity = $this->isCanceled() ? (int) $this->quantity_delivered : (int) $this->quantity;
}
$sellPrice = (float) $this->total_amount_untaxed;
if (!empty($this->options['ecoPart']) && !empty($this->options['ecoPart']['amount'])) {
$sellPrice -= $this->options['ecoPart']['amount'] * $quantity;
}
return $sellPrice - (float) $this->wholesale_price * $quantity;
}
/**
* @return string|null
*/
public function getPercentageDiscountUntaxed(): ?string
{
return $this->percentage_discount_untaxed;
}
/**
* @param string|null $percentage_discount_untaxed
* @return OrderLine
*/
public function setPercentageDiscountUntaxed(?string $percentage_discount_untaxed): self
{
$this->percentage_discount_untaxed = $percentage_discount_untaxed;
return $this;
}
/**
* @return string|null
*/
public function getTotalDiscountUntaxed(): ?string
{
return $this->total_discount_untaxed;
}
/**
* @param string|null $total_discount_untaxed
* @return OrderLine
*/
public function setTotalDiscountUntaxed(?string $total_discount_untaxed): self
{
$this->total_discount_untaxed = $total_discount_untaxed;
return $this;
}
/**
* @return string|null
*/
public function getTotalDiscount(): ?string
{
return $this->totalDiscount;
}
/**
* @param string|null $totalDiscount
* @return OrderLine
*/
public function setTotalDiscount(?string $totalDiscount): OrderLine
{
$this->totalDiscount = $totalDiscount;
return $this;
}
/**
* @return string|null
*/
public function getTotalAmountNoDiscountUntaxed(): ?string
{
return $this->total_amount_no_discount_untaxed;
}
/**
* @param string|null $total_amount_no_discount_untaxed
* @return OrderLine
*/
public function setTotalAmountNoDiscountUntaxed(?string $total_amount_no_discount_untaxed): self
{
$this->total_amount_no_discount_untaxed = $total_amount_no_discount_untaxed;
return $this;
}
/**
* @return string|null
*/
public function getTotalAmountNoDiscount(): ?string
{
return $this->totalAmountNoDiscount;
}
/**
* @param string|null $totalAmountNoDiscount
* @return OrderLine
*/
public function setTotalAmountNoDiscount(?string $totalAmountNoDiscount): OrderLine
{
$this->totalAmountNoDiscount = $totalAmountNoDiscount;
return $this;
}
/**
* @return string|null
*/
public function getTotalAmountUntaxed(): ?string
{
return $this->total_amount_untaxed;
}
/**
* @param string|null $total_amount_untaxed
* @return OrderLine
*/
public function setTotalAmountUntaxed(?string $total_amount_untaxed): self
{
$this->total_amount_untaxed = $total_amount_untaxed;
return $this;
}
/**
* @return string|null
*/
public function getTotalTaxAmount(): ?string
{
return $this->total_tax_amount;
}
/**
* @param string|null $total_tax_amount
* @return OrderLine
*/
public function setTotalTaxAmount(?string $total_tax_amount): self
{
$this->total_tax_amount = $total_tax_amount;
return $this;
}
/**
* @return string|null
*/
public function getTotalAmount(): ?string
{
return $this->total_amount;
}
/**
* @param string|null $total_amount
* @return OrderLine
*/
public function setTotalAmount(?string $total_amount): self
{
$this->total_amount = $total_amount;
return $this;
}
/**
* @return int|null
*/
public function getPosition(): ?int
{
return $this->position;
}
/**
* @param int|null $position
* @return OrderLine
*/
public function setPosition(?int $position): self
{
$this->position = $position;
return $this;
}
/**
* @return array
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @param array $options
* @return OrderLine
*/
public function setOptions(array $options): self
{
$this->options = $options;
return $this;
}
/**
* @param array $options
* @return OrderLine
*/
public function addOptions(array $options): self
{
return $this->setOptions(array_merge($this->options, $options));
}
/**
* @return Collection|OrderLine[]
*/
public function getChildrens(): Collection
{
return $this->childrens;
}
/**
* @param OrderLine $line
* @return OrderLine
*/
public function addChildren(OrderLine $line): self
{
if (!$this->childrens->contains($line)) {
$this->childrens[] = $line;
$line->setParent($this);
if ($line->getOrder() !== $this->getOrder()) {
$line->setOrder($this->getOrder());
}
}
return $this;
}
/**
* @return Collection|OrderLine[]
*/
public function getPackChildrens(): Collection
{
return $this->packChildrens;
}
/**
* @param OrderLine $line
* @return $this
*/
public function addPackChildren(OrderLine $line): self
{
if (!$this->packChildrens->contains($line)) {
$this->packChildrens[] = $line;
$line->setPackParent($this);
if ($line->getOrder() !== $this->getOrder()) {
$line->setOrder($this->getOrder());
}
}
return $this;
}
/**
* @return Order|null
*/
public function getDocument(): ?Order
{
return $this->getOrder();
}
/**
* @param Order $order
* @return OrderLine
*/
public function duplicate(Order $order): OrderLine
{
if ($this->id) {
$clone = clone $this;
$clone->setId();
$clone->setOrder($order);
$clone->setQuantityDelivered(null);
$clone->setCreatedAt(new DateTime());
$clone->setCreatedBy(null);
$clone->setUpdatedAt(new DateTime());
$clone->setUpdatedBy(null);
$clone->childrens = new ArrayCollection();
foreach ($this->getChildrens() as $children) {
$clone_children = $children->duplicate($order);
$clone->addChildren($clone_children);
$children->packChildrens = new ArrayCollection();
foreach ($children->getPackChildrens() as $packChildren) {
$clone_pack_children = $packChildren->duplicate($order);
$children->addPackChildren($clone_pack_children);
}
}
$clone->packChildrens = new ArrayCollection();
foreach ($this->getPackChildrens() as $packChildren) {
$clone_pack_children = $packChildren->duplicate($order);
$clone->addPackChildren($clone_pack_children);
}
return $clone;
}
return $this;
}
/**
* @return false|string
*/
public function getProductOptionUniqueKey()
{
if ($this->hasProductLinked()) {
return $this->options['product']['id']
. ($this->hasDeclinationLinked() ? $this->options['product']['declination_id'] : '');
}
return false;
}
/**
* @return bool
*/
public function isProduct(): bool
{
$options = $this->getOptions();
return !empty($options['product']) && !empty($options['product']['id']);
}
/**
* @return bool
*/
public function isDeclination(): bool
{
return $this->isProduct() && !empty($this->getOptions()['product']['declination_id']);
}
/**
* @return bool
*/
public function hasProductLinked(): bool
{
if (!empty($this->options['product']) && !empty($this->options['product']['id'])) {
return true;
}
return false;
}
/**
* @return bool
*/
public function hasDeclinationLinked(): bool
{
if (!empty($this->options['product']) && !empty($this->options['product']['declination_id'])) {
return true;
}
return false;
}
/**
* @return mixed|null
*/
public function getProductId()
{
if ($this->hasProductLinked()) {
return $this->options['product']['id'];
} else {
return null;
}
}
/**
* @return mixed|null
*/
public function getDeclinationId()
{
if ($this->hasDeclinationLinked()) {
return $this->options['product']['declination_id'];
} else {
return null;
}
}
/**
* @return mixed|null
*/
public function getElementId()
{
if ($this->hasDeclinationLinked()) {
return $this->options['product']['declination_id'];
} elseif ($this->hasProductLinked()) {
return $this->options['product']['id'];
} else {
return null;
}
}
/**
* @return mixed
*/
public function getStockOrderLine()
{
return $this->stockOrderLine;
}
public function setStockOrderLine($stockOrderLine): self
{
$this->stockOrderLine = $stockOrderLine;
return $this;
}
/**
* @return bool
*/
public function isStockReserved(): bool
{
return $this->stockReserved;
}
/**
* @param bool $stockReserved
* @return OrderLine
*/
public function setStockReserved(bool $stockReserved): OrderLine
{
$this->stockReserved = $stockReserved;
return $this;
}
/**
* @return bool
*/
public function isCancelable(): bool
{
return $this->getRemainingQuantity() > 0 && !$this->isCanceled();
}
/**
* @return string|null
*/
public function getUnitPriceWithDiscount(): ?string
{
if ($this->getDocument()->isEditPricesWithTax()) {
$unitDiscount = (float) ($this->getOptions()['unitDiscount'] ?? 0);
return (string) ((float) $this->getUnitPriceWithTax() - $unitDiscount);
} else {
$unitDiscountUntaxed = (float) ($this->getOptions()['unitDiscountUntaxed'] ?? 0);
return (string) ((float) $this->getUnitPrice() - $unitDiscountUntaxed);
}
}
/**
* @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;
}
/**
* @return Declination|null
*/
public function getDeclination(): ?Declination
{
return $this->declination;
}
/**
* @param Declination|null $declination
* @return $this
*/
public function setDeclination(?Declination $declination): self
{
$this->declination = $declination;
return $this;
}
}