<?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 App\Entity\Traits\EntityManagerServiceEntity;
use App\Services\SoftdeleteFilter;
use Bluue\SalesBundle\DoctrineExtensions\EditPricesWithTaxEntity;
use DateTime;
use App\Entity\User;
use App\Entity\Context;
use App\Entity\Currency;
use Doctrine\ORM\NonUniqueResultException;
use Symfony\Component\Uid\Uuid;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\Common\Collections\Criteria;
use App\DoctrineExtensions\ArchivedEntity;
use Bluue\CustomersBundle\Entity\Customer;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Bluue\SalesBundle\Repository\QuotationRepository;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
use App\DoctrineExtensions\Validatable\Traits\UserValidatableEntity;
use App\DoctrineExtensions\Timestampable\Traits\UserTimestampableEntity;
use App\DoctrineExtensions\SoftDeleteable\Traits\UserSoftDeleteableEntity;
use App\Entity\Establishment;
use DateTimeInterface;
/**
* @ORM\Entity(repositoryClass=QuotationRepository::class)
* @ORM\Table(name="sales_bundle__quotation", indexes={
* @ORM\Index(name="internal_name", columns={"internal_name"}),
* @ORM\Index(name="reference", columns={"reference"}),
* @ORM\Index(name="total_amount_untaxed", columns={"total_amount_untaxed"}),
* @ORM\Index(name="total_amount", columns={"total_amount"}),
* @ORM\Index(name="validated_at", columns={"validated_at"}),
* @ORM\Index(name="archived", columns={"archived"}),
* @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 Quotation
{
use UserValidatableEntity;
use UserTimestampableEntity;
use UserSoftDeleteableEntity;
use ArchivedEntity;
use EditPricesWithTaxEntity;
use EntityManagerServiceEntity;
/**
* @ORM\Id
* @ORM\Column(type="uuid")
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class=UuidGenerator::class)
*/
private ?Uuid $id = null;
/**
* @ORM\ManyToOne(targetEntity=Context::class)
*/
private ?Context $context = null;
/**
* @ORM\ManyToOne(targetEntity=Customer::class)
* @ORM\JoinColumn(nullable=false)
*/
private ?Customer $customer = null;
/**
* @ORM\ManyToOne(targetEntity=User::class)
*/
private ?User $commercial = null;
/**
* @ORM\ManyToOne(targetEntity=Customer::class)
*/
private ?Customer $invoice_address = null;
/**
* @ORM\ManyToOne(targetEntity=Customer::class)
*/
private ?Customer $delivery_address = null;
/**
* @ORM\ManyToOne(targetEntity=Customer::class)
*/
private ?Customer $invoiceContact = null;
/**
* @ORM\ManyToOne(targetEntity=Customer::class)
*/
private ?Customer $deliveryContact = null;
/**
* @ORM\ManyToOne(targetEntity=Currency::class)
* @ORM\JoinColumn(nullable=false)
*/
private ?Currency $currency = null;
/**
* @ORM\Column(type="decimal", precision=20, scale=12)
*/
private ?string $currency_change_rate = null;
/**
* @ORM\ManyToOne(targetEntity=PaymentMethod::class)
*/
private ?PaymentMethod $payment_method = null;
/**
* @ORM\Column(type="integer", nullable="true")
*/
private ?int $validity_in_days = 30;
/**
* @ORM\Column(type="string", length=128, nullable="true")
*/
private ?string $reference = null;
/**
* @ORM\Column(type="string", length=255, nullable="true")
*/
private ?string $internal_name = null;
/**
* @ORM\Column(type="string", length=255, nullable="true")
*/
private ?string $external_name = null;
/**
* @ORM\Column(type="boolean")
*/
private bool $reduced_vat = false;
/**
* @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="json")
*/
private array $options = [];
/**
* @ORM\Column(type="boolean", options={"default":"0"})
*/
private bool $lost = false;
/**
* @ORM\Column(type="boolean", options={"default":"0"})
*/
private bool $closed = false;
/**
* @ORM\Column(type="boolean", options={"default":"0"})
*/
private bool $sent = false;
/**
* Virtual
* @var bool
*/
private bool $proforma = false;
/**
* Virtual
* @var array
*/
private array $pdfOptions = [];
/**
* @ORM\OneToMany(
* targetEntity=QuotationLine::class,
* mappedBy="quotation",
* cascade={"persist", "remove"},
* fetch="EXTRA_LAZY"
* )
* @ORM\OrderBy({"position" = "ASC"})
*/
private Collection $lines;
/**
* @ORM\OneToMany(targetEntity=Invoice::class, mappedBy="quotation", fetch="EXTRA_LAZY")
*/
private Collection $invoices;
/**
* @ORM\ManyToOne(
* targetEntity=Order::class,
* inversedBy="quotations",
* fetch="EXTRA_LAZY"
* )
*/
private ?Order $order = null;
/**
* @ORM\ManyToOne(targetEntity=Establishment::class)
*/
private ?Establishment $establishment = null;
public function __construct()
{
$this->lines = new ArrayCollection();
$this->invoices = new ArrayCollection();
}
/**
* @return Uuid|null
*/
public function getId(): ?Uuid
{
return $this->id;
}
/**
* @return Quotation
*/
public function setId(): self
{
$this->id = null;
return $this;
}
/**
* @return Context|null
*/
public function getContext(): ?Context
{
return $this->context;
}
/**
* @param Context|null $context
* @return Quotation
*/
public function setContext(?Context $context): self
{
$this->context = $context;
return $this;
}
/**
* @return Customer|null
* @throws NonUniqueResultException
*/
public function getCustomer(): ?Customer
{
SoftdeleteFilter::disable($this->em, [Customer::class]);
if (!$this->customer) {
return null;
}
if ($this->getId()) {
$customer = $this->em->createQueryBuilder()
->select('c')
->from(Customer::class, 'c')
->where('c.id = :customer')
->setParameter('customer', $this->customer->getId()->toBinary())
->getQuery()
->getOneOrNullResult();
SoftdeleteFilter::enable($this->em, [Customer::class]);
return $customer;
}
return $this->customer;
}
/**
* @param Customer $customer
* @return Quotation
*/
public function setCustomer(Customer $customer): self
{
if ($this->customer && $this->customer->getId() !== $customer->getId()) {
$this->setInvoiceContact(null)->setDeliveryContact(null);
}
$this->customer = $customer;
$this->setDeliveryAddress(null);
return $this->setInvoiceAddress($customer);
}
/**
* @return User|null
* @throws NonUniqueResultException
*/
public function getCommercial(): ?User
{
SoftdeleteFilter::disable($this->em, [Customer::class]);
if (!$this->commercial) {
return null;
}
if ($this->getId()) {
$commercial = $this->em->createQueryBuilder()
->select('c')
->from(Customer::class, 'c')
->where('c.id = :commercial')
->setParameter('commercial', $this->commercial->getId()->toBinary())
->getQuery()
->getOneOrNullResult();
SoftdeleteFilter::enable($this->em, [Customer::class]);
return $commercial;
}
return $this->commercial;
}
/**
* @param User|null $commercial
* @return $this
*/
public function setCommercial(?User $commercial): self
{
$this->commercial = $commercial;
return $this;
}
/**
* @return Customer|null
*/
public function getInvoiceAddress(): ?Customer
{
return $this->invoice_address;
}
/**
* @param Customer|null $invoice_address
* @return Quotation
*/
public function setInvoiceAddress(?Customer $invoice_address): self
{
$this->invoice_address = $invoice_address;
return $this;
}
/**
* @return Customer|null
*/
public function getDeliveryAddress(): ?Customer
{
return $this->delivery_address;
}
/**
* @param Customer|null $delivery_address
* @return Quotation
*/
public function setDeliveryAddress(?Customer $delivery_address): self
{
$this->delivery_address = $delivery_address;
return $this;
}
/**
* @return Customer|null
*/
public function getInvoiceContact(): ?Customer
{
return $this->invoiceContact;
}
/**
* @param Customer|null $invoiceContact
* @return Quotation
*/
public function setInvoiceContact(?Customer $invoiceContact): Quotation
{
$this->invoiceContact = $invoiceContact;
return $this;
}
/**
* @return Customer|null
*/
public function getDeliveryContact(): ?Customer
{
return $this->deliveryContact;
}
/**
* @param Customer|null $deliveryContact
* @return Quotation
*/
public function setDeliveryContact(?Customer $deliveryContact): Quotation
{
$this->deliveryContact = $deliveryContact;
return $this;
}
/**
* @return Currency|null
*/
public function getCurrency(): ?Currency
{
return $this->currency;
}
/**
* @param Currency $currency
* @return Quotation
*/
public function setCurrency(Currency $currency): self
{
$this->currency = $currency;
return $this->setCurrencyChangeRate($currency->getChangeRate());
}
/**
* @return string|null
*/
public function getCurrencyChangeRate(): ?string
{
return $this->currency_change_rate;
}
/**
* @param string $currency_change_rate
* @return $this
*/
public function setCurrencyChangeRate(string $currency_change_rate): self
{
$this->currency_change_rate = $currency_change_rate;
return $this;
}
/**
* @return PaymentMethod|null
*/
public function getPaymentMethod(): ?PaymentMethod
{
return $this->payment_method;
}
/**
* @param PaymentMethod|null $payment_method
* @return Quotation
*/
public function setPaymentMethod(?PaymentMethod $payment_method): Quotation
{
$this->payment_method = $payment_method;
return $this;
}
/**
* @return int|null
*/
public function getValidityInDays(): ?int
{
return $this->validity_in_days;
}
/**
* @param int|null $validity_in_days
* @return Quotation
*/
public function setValidityInDays(?int $validity_in_days): Quotation
{
$this->validity_in_days = $validity_in_days;
return $this;
}
/**
* @return DateTimeInterface|null
*/
public function getValidityDate(): ?DateTimeInterface
{
if ($this->validity_in_days) {
return $this->createdAt->modify('+' . $this->validity_in_days . ' day');
} else {
return null;
}
}
/**
* @return string|null
*/
public function getReference(): ?string
{
return $this->reference;
}
/**
* @param string|null $reference
* @return Quotation
*/
public function setReference(?string $reference): self
{
$this->reference = $reference;
return $this;
}
/**
* @return string|null
*/
public function getInternalName(): ?string
{
return $this->internal_name;
}
/**
* @param string|null $internal_name
* @return Quotation
*/
public function setInternalName(?string $internal_name): self
{
$this->internal_name = $internal_name;
return $this;
}
/**
* @return string|null
*/
public function getExternalName(): ?string
{
return $this->external_name;
}
/**
* @param string|null $external_name
* @return Quotation
*/
public function setExternalName(?string $external_name): self
{
$this->external_name = $external_name;
return $this;
}
/**
* @return bool
*/
public function isReducedVat(): bool
{
return $this->reduced_vat;
}
/**
* @param bool $reduced_vat
* @return Quotation
*/
public function setReducedVat(bool $reduced_vat): self
{
$this->reduced_vat = $reduced_vat;
return $this;
}
/**
* @return string|null
*/
public function getTotalDiscountUntaxed(): ?string
{
return $this->total_discount_untaxed;
}
/**
* @param string|null $total_discount_untaxed
* @return Quotation
*/
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 Quotation
*/
public function setTotalDiscount(?string $totalDiscount): Quotation
{
$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 Quotation
*/
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 Quotation
*/
public function setTotalAmountNoDiscount(?string $totalAmountNoDiscount): Quotation
{
$this->totalAmountNoDiscount = $totalAmountNoDiscount;
return $this;
}
/**
* @return string|null
*/
public function getTotalAmountUntaxed(): ?string
{
return $this->total_amount_untaxed;
}
/**
* @param string|null $total_amount_untaxed
* @return Quotation
*/
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 Quotation
*/
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 Quotation
*/
public function setTotalAmount(?string $total_amount): self
{
$this->total_amount = $total_amount;
return $this;
}
/**
* @return array
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @param array $options
* @return Quotation
*/
public function setOptions(array $options): self
{
$this->options = $options;
return $this;
}
/**
* @param array $options
* @return Quotation
*/
public function addOptions(array $options): self
{
return $this->setOptions(array_merge($this->options, $options));
}
/**
* @return bool
*/
public function isLost(): bool
{
return $this->lost;
}
/**
* @param bool $lost
* @return Quotation
*/
public function setLost(bool $lost): Quotation
{
$this->lost = $lost;
return $this;
}
/**
* @return bool
*/
public function isClosed(): bool
{
return $this->closed;
}
/**
* @param bool $closed
* @return Quotation
*/
public function setClosed(bool $closed): Quotation
{
$this->closed = $closed;
return $this;
}
/**
* @return bool
*/
public function isSent(): bool
{
return $this->sent;
}
/**
* @param bool $sent
* @return Quotation
*/
public function setSent(bool $sent): Quotation
{
$this->sent = $sent;
return $this;
}
/**
* @return bool
*/
public function isProforma(): bool
{
return $this->proforma;
}
/**
* @param bool $proforma
* @return Quotation
*/
public function setProforma(bool $proforma): Quotation
{
$this->proforma = $proforma;
return $this;
}
/**
* @return array
*/
public function getPdfOptions(): array
{
return $this->pdfOptions;
}
/**
* @param array $pdfOptions
* @return Quotation
*/
public function setPdfOptions(array $pdfOptions): Quotation
{
$this->pdfOptions = $pdfOptions;
return $this;
}
/**
* @return Collection|QuotationLine[]
*/
public function getLines(): Collection
{
return $this->getFilterLines();
}
/**
* @param QuotationLine $line
* @return $this
*/
public function addLine(QuotationLine $line): self
{
if (!$this->lines->contains($line)) {
$this->lines[] = $line;
if ($line->getQuotation() !== $this) {
$line->setQuotation($this);
}
}
return $this;
}
/**
* @param QuotationLine $line
* @return $this
*/
public function removeLine(QuotationLine $line): self
{
$this->lines->removeElement($line);
return $this;
}
/**
* @param bool $withGroups
* @param bool $all
* @param bool $withPacks
* @return Collection|QuotationLine[]
*/
public function getFilterLines(bool $withGroups = true, bool $all = false, bool $withPacks = true): Collection
{
if ($all) {
$lines = $this->lines;
$goodLines = [];
foreach ($lines as $line) {
if (!$line->getParent() && !$line->getPackParent()) {
$goodLines[] = $line;
foreach ($line->getChildrens() as $child) {
$goodLines[] = $child;
foreach ($child->getPackChildrens() as $grandChild) {
$goodLines[] = $grandChild;
}
}
foreach ($line->getPackChildrens() as $child) {
$goodLines[] = $child;
}
}
}
return new ArrayCollection($goodLines);
}
$criteria = Criteria::create();
if ($withGroups) {
$criteria
->where(Criteria::expr()->eq('parent', null))
->andWhere(Criteria::expr()->eq('packParent', null));
} else {
if ($withPacks) {
$criteria->andWhere(Criteria::expr()->eq('is_pack', true));
} else {
$criteria->andWhere(Criteria::expr()->eq('is_pack', false));
}
$criteria->where(Criteria::expr()->eq('is_group', false));
}
return $this->lines->matching($criteria);
}
/**
* @return Collection|Invoice[]
*/
public function getInvoices(): Collection
{
return $this->invoices;
}
/**
* @return Invoice|null
*/
public function getMainInvoice(): ?Invoice
{
$collection = $this->invoices->filter(function (Invoice $invoice) {
return !$invoice->getIsDeposit() && !$invoice->isCanceled();
});
return $collection->count() ? $collection->first() : null;
}
/**
* @return Collection|Invoice[]
*/
public function getDepositInvoices(): Collection
{
return $this->invoices->filter(function (Invoice $invoice) {
return $invoice->getIsDeposit() && !$invoice->isCanceled();
});
}
/**
* @return float
*/
public function getResidual(): float
{
$residual = (float) $this->getTotalAmount();
foreach ($this->getDepositInvoices() as $depositInvoice) {
$residual -= $depositInvoice->getTotalAmount();
}
return $residual;
}
/**
* @return Order|null
*/
public function getOrder(): ?Order
{
return $this->order;
}
/**
* @return bool
*/
public function isDepositInvoicable(): bool
{
return $this->getValidatedAt() &&
!$this->getMainInvoice() &&
(!$this->getOrder() || !$this->getOrder()->isValidated());
}
/**
* @return bool
*/
public function isInvoicable(): bool
{
return $this->getValidatedAt() && !$this->getMainInvoice() && !$this->getOrder();
}
/**
* @return bool
*/
public function isOrderable(): bool
{
return $this->getValidatedAt() && !$this->getOrder() && !$this->getMainInvoice();
}
/**
* @return Quotation
*/
public function duplicate(): Quotation
{
if ($this->id) {
$clone = clone $this;
$clone->setId();
$clone->setCreatedAt(new DateTime());
$clone->setCreatedBy(null);
$clone->setUpdatedAt(new DateTime());
$clone->setUpdatedBy(null);
$clone->setValidatedAt(null);
$clone->setValidatedBy(null);
$clone->setArchived(false);
$clone->setClosed(false);
$clone->setLost(false);
$clone->setContext(null);
$clone->setEstablishment(null);
$clone->setReference(null);
$clone->setOrder(null);
$clone->addOptions([
'invoice_address' => null,
'delivery_address' => null
]);
$clone->lines = new ArrayCollection();
foreach ($this->getFilterLines() as $line) {
$clone_line = $line->duplicate($clone);
$clone->addLine($clone_line);
}
return $clone;
}
return $this;
}
/**
* @param Order|null $order
* @return $this
*/
public function setOrder(?Order $order): self
{
$this->order = $order;
return $this;
}
/**
* @return Establishment|null
*/
public function getEstablishment(): ?Establishment
{
return $this->establishment;
}
/**
* @param Establishment $establishment
* @return $this
*/
public function setEstablishment(?Establishment $establishment): self
{
$this->establishment = $establishment;
return $this;
}
}