src/Entity/Voucher.php line 26

Open in your IDE?
  1. <?php
  2. namespace App\Entity;
  3. use App\Constants\ActiveConstants;
  4. use App\Constants\VoucherConstants;
  5. use App\Repository\VoucherRepository;
  6. use App\Traits\TimeTrackTrait;
  7. use Doctrine\Common\Collections\ArrayCollection;
  8. use Doctrine\Common\Collections\Collection;
  9. use Doctrine\ORM\Mapping as ORM;
  10. use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
  11. use Symfony\Component\Validator\Constraints as Assert;
  12. use Symfony\Component\Validator\Context\ExecutionContextInterface;
  13. use Symfony\Contracts\Translation\TranslatorInterface;
  14. use App\Validator\Constraints as AppAssert;
  15. #[ORM\Entity(repositoryClassVoucherRepository::class)]
  16. #[ORM\Table(name'voucher')]
  17. #[ORM\HasLifecycleCallbacks]
  18. #[ORM\EntityListeners(["App\EventListener\VoucherCodeGeneratorListener"])]
  19. #[UniqueEntity(
  20.     fields: ['code'],
  21.     message'Такий код вже використовується.'
  22. )]
  23. class Voucher
  24. {
  25.     use TimeTrackTrait;
  26.     #[ORM\Id]
  27.     #[ORM\GeneratedValue]
  28.     #[ORM\Column]
  29.     private ?int $id null;
  30.     #[ORM\Column(length255)]
  31.     #[Assert\NotBlank(message'Поле "Назва" є обов’язковим.')]
  32.     private string $name;
  33.     #[ORM\Column(length50nullabletrueuniquetrueoptions: ['collation' => 'utf8mb4_bin'])]
  34.     #[AppAssert\UniqueVoucherCode]
  35.     private ?string $code null;
  36.     #[ORM\Column(length50)]
  37.     #[Assert\NotBlank(message'Оберіть тип знижки.')]
  38.     private string $type;
  39.     #[ORM\Column(length50name"voucher_type")]
  40.     private string $voucherType VoucherConstants::VOUCHER_SINGLE;
  41.     #[ORM\Column(type"decimal"precision10scale2)]
  42.     #[Assert\NotBlank(message'Вкажіть суму знижки.')]
  43.     #[Assert\Positive(message'Сума знижки повинна бути більшою за 0.')]
  44.     private float $value;
  45.     #[ORM\Column(type"datetime"nullabletruename"start_at")]
  46.     private ?\DateTimeInterface $startAt null;
  47.     #[ORM\Column(type"datetime"nullabletruename"expires_at")]
  48.     #[Assert\Expression(
  49.         "this.getExpiresAt() === null or this.getStartAt() === null or this.getExpiresAt() > this.getStartAt()",
  50.         message"Дата закінчення повинна бути пізнішою за дату початку."
  51.     )]
  52.     private ?\DateTimeInterface $expiresAt null;
  53.     #[ORM\Column(type"boolean")]
  54.     private bool $active true;
  55.     #[ORM\Column(type"integer"nullabletruename"usage_per_user")]
  56.     private ?int $usagePerUser null;
  57.     #[ORM\Column(type"text"nullabletrue)]
  58.     private ?string $description null;
  59.     #[ORM\OneToMany(mappedBy"voucher"targetEntityVoucherCode::class, cascade: ["persist""remove"], orphanRemovaltrue)]
  60.     private Collection $codes;
  61.     #[ORM\OneToMany(mappedBy'voucher'targetEntityOrder::class)]
  62.     private Collection $orders;
  63.     #[ORM\ManyToMany(targetEntityCompany::class)]
  64.     #[ORM\JoinTable(
  65.         name'voucher_company',
  66.         joinColumns: [new ORM\JoinColumn(name'voucher_id'referencedColumnName'id'onDelete'CASCADE')],
  67.         inverseJoinColumns: [new ORM\JoinColumn(name'company_id'referencedColumnName'id'onDelete'CASCADE')]
  68.     )]
  69.     private Collection $companies;
  70.     #[ORM\ManyToMany(targetEntityTariff::class)]
  71.     #[ORM\JoinTable(
  72.         name'voucher_tariff',
  73.         joinColumns: [new ORM\JoinColumn(name'voucher_id'referencedColumnName'id'onDelete'CASCADE')],
  74.         inverseJoinColumns: [new ORM\JoinColumn(name'tariff_id'referencedColumnName'id'onDelete'CASCADE')]
  75.     )]
  76.     private Collection $tariffs;
  77.     private ?int $generatedCount null;
  78.     private ?int $generatedLength null;
  79.     public function __construct()
  80.     {
  81.         $this->companies = new ArrayCollection();
  82.         $this->tariffs = new ArrayCollection();
  83.         $this->orders = new ArrayCollection();
  84.         $this->codes = new ArrayCollection();
  85.     }
  86.     /**
  87.      * @return Collection<int, Order>
  88.      */
  89.     public function getOrders(): Collection
  90.     {
  91.         return $this->orders;
  92.     }
  93.     public function addOrder(Order $order): static
  94.     {
  95.         if (!$this->orders->contains($order)) {
  96.             $this->orders->add($order);
  97.             $order->setVoucher($this);
  98.         }
  99.         return $this;
  100.     }
  101.     public function removeOrder(Order $order): static
  102.     {
  103.         if ($this->orders->removeElement($order)) {
  104.             if ($order->getVoucher() === $this) {
  105.                 $order->setVoucher(null);
  106.             }
  107.         }
  108.         return $this;
  109.     }
  110.     public function getId(): ?int
  111.     {
  112.         return $this->id;
  113.     }
  114.     public function getName(): string
  115.     {
  116.         return $this->name;
  117.     }
  118.     public function setName(string $name): self
  119.     {
  120.         $this->name $name;
  121.         return $this;
  122.     }
  123.     public function getCode(): ?string
  124.     {
  125.         return $this->code;
  126.     }
  127.     public function setCode(?string $code): self
  128.     {
  129.         if ($this->voucherType !== VoucherConstants::VOUCHER_GENERATED) {
  130.             $this->code $code;
  131.         }
  132.         return $this;
  133.     }
  134.     public function getType(): string
  135.     {
  136.         return $this->type;
  137.     }
  138.     public function setType(string $type): self
  139.     {
  140.         $this->type $type;
  141.         return $this;
  142.     }
  143.     public function getVoucherType(): string
  144.     {
  145.         return $this->voucherType;
  146.     }
  147.     public function setVoucherType(string $voucherType): self
  148.     {
  149.         $this->voucherType $voucherType;
  150.         return $this;
  151.     }
  152.     public function getValue(): float
  153.     {
  154.         return $this->value;
  155.     }
  156.     public function setValue(float $value): self
  157.     {
  158.         $this->value $value;
  159.         return $this;
  160.     }
  161.     public function getStartAt(): ?\DateTimeInterface
  162.     {
  163.         return $this->startAt;
  164.     }
  165.     public function setStartAt(?\DateTimeInterface $startAt): self
  166.     {
  167.         $this->startAt $startAt;
  168.         return $this;
  169.     }
  170.     public function getExpiresAt(): ?\DateTimeInterface
  171.     {
  172.         return $this->expiresAt;
  173.     }
  174.     public function setExpiresAt(?\DateTimeInterface $expiresAt): self
  175.     {
  176.         $this->expiresAt $expiresAt;
  177.         return $this;
  178.     }
  179.     public function isActive(): bool
  180.     {
  181.         return $this->active;
  182.     }
  183.     public function setActive(bool $active): self
  184.     {
  185.         $this->active $active;
  186.         return $this;
  187.     }
  188.     public function getActiveLabel(): string
  189.     {
  190.         return $this->active ActiveConstants::LABEL_YES ActiveConstants::LABEL_NO;
  191.     }
  192.     public function getTypeLabel(): string
  193.     {
  194.         $types VoucherConstants::loadTypes();
  195.         return $types[$this->type] ?? $this->type;
  196.     }
  197.     public function getVoucherTypeLabel(): string
  198.     {
  199.         $types VoucherConstants::loadVoucherTypes();
  200.         return $types[$this->voucherType] ?? $this->voucherType;
  201.     }
  202.     public function getUsagePerUser(): ?int
  203.     {
  204.         return $this->usagePerUser;
  205.     }
  206.     public function setUsagePerUser(?int $usagePerUser): self
  207.     {
  208.         $this->usagePerUser $usagePerUser;
  209.         return $this;
  210.     }
  211.     public function getDescription(): ?string
  212.     {
  213.         return $this->description;
  214.     }
  215.     public function setDescription(?string $description): self
  216.     {
  217.         $this->description $description;
  218.         return $this;
  219.     }
  220.     /**
  221.      * @return Collection<int, VoucherCode>
  222.      */
  223.     public function getCodes(): Collection
  224.     {
  225.         return $this->codes;
  226.     }
  227.     public function addCode(VoucherCode $code): self
  228.     {
  229.         if (!$this->codes->contains($code)) {
  230.             $this->codes[] = $code;
  231.             $code->setVoucher($this);
  232.         }
  233.         return $this;
  234.     }
  235.     public function removeCode(VoucherCode $code): self
  236.     {
  237.         if ($this->codes->removeElement($code)) {
  238.             if ($code->getVoucher() === $this) {
  239.                 $code->setVoucher(null);
  240.             }
  241.         }
  242.         return $this;
  243.     }
  244.     /**
  245.      * Set createdAt.
  246.      *
  247.      * @param \DateTime|null $createdAt
  248.      *
  249.      * @return Voucher
  250.      */
  251.     public function setCreatedAt($createdAt null)
  252.     {
  253.         $this->createdAt $createdAt;
  254.         return $this;
  255.     }
  256.     /**
  257.      * Get createdAt.
  258.      *
  259.      * @return \DateTime|null
  260.      */
  261.     public function getCreatedAt()
  262.     {
  263.         return $this->createdAt;
  264.     }
  265.     /**
  266.      * Set updatedAt.
  267.      *
  268.      * @param \DateTime|null $updatedAt
  269.      *
  270.      * @return Voucher
  271.      */
  272.     public function setUpdatedAt($updatedAt null)
  273.     {
  274.         $this->updatedAt $updatedAt;
  275.         return $this;
  276.     }
  277.     /**
  278.      * Get updatedAt.
  279.      *
  280.      * @return \DateTime|null
  281.      */
  282.     public function getUpdatedAt()
  283.     {
  284.         return $this->updatedAt;
  285.     }
  286.     public function getCompanies(): Collection
  287.     {
  288.         return $this->companies;
  289.     }
  290.     public function addCompany(Company $company): self
  291.     {
  292.         if (!$this->companies->contains($company)) {
  293.             $this->companies->add($company);
  294.         }
  295.         return $this;
  296.     }
  297.     public function removeCompany(Company $company): self
  298.     {
  299.         $this->companies->removeElement($company);
  300.         return $this;
  301.     }
  302.     public function getTariffs(): Collection
  303.     {
  304.         return $this->tariffs;
  305.     }
  306.     public function addTariff(Tariff $tariff): self
  307.     {
  308.         if (!$this->tariffs->contains($tariff)) {
  309.             $this->tariffs->add($tariff);
  310.         }
  311.         return $this;
  312.     }
  313.     public function removeTariff(Tariff $tariff): self
  314.     {
  315.         $this->tariffs->removeElement($tariff);
  316.         return $this;
  317.     }
  318.     public function getGeneratedCount(): ?int
  319.     {
  320.         return $this->generatedCount;
  321.     }
  322.     public function setGeneratedCount(?int $generatedCount): self
  323.     {
  324.         $this->generatedCount $generatedCount;
  325.         return $this;
  326.     }
  327.     public function getGeneratedLength(): ?int
  328.     {
  329.         return $this->generatedLength;
  330.     }
  331.     public function setGeneratedLength(?int $generatedLength): self
  332.     {
  333.         $this->generatedLength $generatedLength;
  334.         return $this;
  335.     }
  336.     #[Assert\Callback]
  337.     public function validateSingleVoucherFields(ExecutionContextInterface $context): void
  338.     {
  339.         if ($this->voucherType === VoucherConstants::VOUCHER_SINGLE) {
  340.             if (empty($this->code)) {
  341.                 $context->buildViolation('Поле "Код" є обов’язковим для Єдиного промокоду.')
  342.                     ->atPath('code')
  343.                     ->addViolation();
  344.             }
  345.             if (empty($this->usagePerUser) || $this->usagePerUser <= 0) {
  346.                 $context->buildViolation('Поле "Кількість використань на користувача" має бути більше 0 для Єдиного промокоду.')
  347.                     ->atPath('usagePerUser')
  348.                     ->addViolation();
  349.             }
  350.         }
  351.         if ($this->voucherType === VoucherConstants::VOUCHER_GENERATED) {
  352.             if (empty($this->generatedCount) || $this->generatedCount <= 0) {
  353.                 $context->buildViolation('Вкажіть кількість кодів для генерації.')
  354.                     ->atPath('generatedCount')
  355.                     ->addViolation();
  356.             }
  357.             if (empty($this->generatedLength) || $this->generatedLength <= 0) {
  358.                 $context->buildViolation('Вкажіть довжину промокоду для генерації.')
  359.                     ->atPath('generatedLength')
  360.                     ->addViolation();
  361.             }
  362.         }
  363.     }
  364.     public function applyTo(float $amountTranslatorInterface $translator): array
  365.     {
  366.         $original round($amount2PHP_ROUND_HALF_DOWN);
  367.         $discount 0;
  368.         if (!$this->isUsable()) {
  369.             return [
  370.                 'status' => 'error',
  371.                 'errors' => ['voucher' => $translator->trans('voucher_not_found', [], 'validators')],
  372.             ];
  373.         }
  374.         if ($this->getType() === VoucherConstants::TYPE_PERCENT) {
  375.             $discount round($original * ($this->getValue() / 100.0), 2PHP_ROUND_HALF_DOWN);
  376.         } elseif ($this->getType() === VoucherConstants::TYPE_FIXED) {
  377.             $discount round($this->getValue(), 2PHP_ROUND_HALF_DOWN);
  378.         }
  379.         $final round($original $discount2,PHP_ROUND_HALF_DOWN);
  380.         if ($discount >= $original || $final 1) {
  381.             return [
  382.                 'status' => 'error',
  383.                 'errors' => ['voucher' => $translator->trans('voucher_not_found', [], 'validators')],
  384.             ];
  385.         }
  386.         return [
  387.             'status'        => 'ok',
  388.             'discount'      => $discount,
  389.             'name'          => $this->getName(),
  390.             'discount_type' => $this->getType(),
  391.             'price'         => $final,
  392.         ];
  393.     }
  394.     public function isExpired(): bool
  395.     {
  396.         return $this->expiresAt !== null && $this->expiresAt < new \DateTimeImmutable('now');
  397.     }
  398.     public function isNotStarted(): bool
  399.     {
  400.         return $this->startAt !== null && $this->startAt > new \DateTimeImmutable('now');
  401.     }
  402.     public function isUsable(): bool
  403.     {
  404.         return $this->isActive() && !$this->isExpired() && !$this->isNotStarted();
  405.     }
  406. }