<?php
namespace App\Entity\Purse;
use ApiPlatform\Core\Annotation\ApiFilter;
use App\Controller\Api\PurseAvailableByUser;
use App\Controller\Api\PurseExpiringByUser;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
use App\Entity\User\User;
use App\Repository\Purse\PurseRepository;
use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiProperty;
use Symfony\Component\Serializer\Annotation\Groups;
use Ramsey\Uuid\Doctrine\UuidGenerator;
/**
* @ApiResource(
* attributes={
* "normalization_context"={"groups"={"Purse:io","Purse:output"}},
* "denormalization_context"={"groups"={"Purse:io","Purse:input"}}
* },
* collectionOperations={
* "get"={
* "method"="GET",
* "access_control"="is_granted('ROLE_USER')",
* },
* },
* itemOperations={
* "get"={
* "method"="GET",
* "access_control"="is_granted('ROLE_USER')",
* },
* "purse-available"={
* "method"="GET",
* "path"="/purse-available/user",
* "access_control"="is_granted('ROLE_USER')",
* "controller"=PurseAvailableByUser::class,
* "defaults"={"_api_receive"=false},
* },
* "purse-expiring"={
* "method"="GET",
* "path"="/purse-expiring/user",
* "access_control"="is_granted('ROLE_USER')",
* "controller"=PurseExpiringByUser::class,
* "defaults"={"_api_receive"=false},
* },
* }
* )
*
* @ApiFilter(SearchFilter::class, properties={"items.status": "exact"})
* @ORM\Entity(repositoryClass=PurseRepository::class)
*/
class Purse
{
/**
* Hook timestampable behavior
* updates createdAt, updatedAt fields
*/
use TimestampableEntity;
// ###> Purse Config ###
const DEFAULT_CONFIG_PURSE_DURATION_IN_DAYS = 365;
const DEFAULT_CONFIG_PURSE_MAX_ITEM = 30;
// ###< Purse Config ###
const STATUS_AVAILABLE = "available";
const STATUS_DONE = "done";
const STATUS_EXPIRED = "expired";
const STATUS_EXPIRING = "expiring";
/**
* @var string
* @ORM\Id
* @ORM\Column(type="uuid", unique=true)
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class=UuidGenerator::class)
* @ApiProperty(identifier=true)
* @Groups({"Purse:o"})
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity=User::class, inversedBy="purses")
* @ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* @ORM\Column(type="datetime")
* @Groups({"Purse:output"})
*/
private $dueAt;
/**
* @ORM\Column(type="integer")
* @Groups({"Purse:output"})
*/
private $maxItem;
/**
* @ORM\Column(type="float")
* @Groups({"Purse:output"})
*/
private $amount = 0;
/**
* @ORM\Column(type="string", length=30)
* @Groups({"Purse:output"})
*/
private $status = self::STATUS_AVAILABLE;
/**
* @ORM\OneToMany(targetEntity=Item::class, mappedBy="purse", orphanRemoval=true)
* @ORM\OrderBy({"createdAt" = "ASC", "percentApplied" = "ASC"})
* @Groups({"Purse:output"})
*/
private $items;
public function __construct()
{
$this->maxItem = $_ENV['CONFIG_PURSE_MAX_ITEM'] ?? self::DEFAULT_CONFIG_PURSE_MAX_ITEM;
$this->items = new ArrayCollection();
}
/**
* @return string
*/
public function getId(): string
{
return $this->id;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
public function getDueAt(): ?\DateTimeInterface
{
return $this->dueAt;
}
public function setDueAt(\DateTimeInterface $dueAt): self
{
$this->dueAt = $dueAt;
return $this;
}
public function getMaxItem(): ?int
{
return $this->maxItem;
}
public function setMaxItem(int $maxItem): self
{
$this->maxItem = $maxItem;
return $this;
}
public function getAmount(): ?float
{
if( $this->amount <= 0 ) {
return 0;
}
return $this->amount;
}
public function getAvailableAmount(): ?float
{
$availableAmount = 0;
foreach( $this->items as $item ) {
if( $item instanceof Item && in_array($item->getStatus(), [Item::STATUS_NEW, Item::STATUS_AVAILABLE]) ) {
$availableAmount += $item->getAmount();
}
}
return $availableAmount;
}
public function setAmount(float $amount): self
{
$this->amount = $amount;
return $this;
}
public function getStatus(): ?string
{
return $this->status;
}
public function setStatus(string $status): self
{
$this->status = $status;
return $this;
}
/**
* @param array $statusFilter default []
* @return Collection|Item[]
*/
public function getItems(array $statusFilter = []): Collection
{
if( !$statusFilter) {
return $this->items;
}
return $this->items->filter(function(Item $item) use ($statusFilter) {
return in_array($item->getStatus(), $statusFilter);
});
}
public function addItem(Item $item): self
{
if ($this->maxItem !== -1 && $this->items->count() >= $this->maxItem) {
return $this;
}
if (!$this->items->contains($item)) {
$this->items[] = $item;
$item->setPurse($this);
// increase purse amount
$this->amount += $item->getAmount();
}
return $this;
}
public function removeItem(Item $item): self
{
if ($this->items->removeElement($item)) {
// set the owning side to null (unless already changed)
if ($item->getPurse() === $this) {
$item->setPurse(null);
}
// decrease purse amount
$this->amount -= $item->getAmount();
}
return $this;
}
public function setItemToExpired(Item $item)
{
$today = new \DateTime();
foreach ($this->items as $i) {
if( !$i instanceof Item || $i->getId() !== $item->getId()) {
continue;
}
$dueDate = ($item->getDueDate())->modify('+6month');
if (in_array($item->getStatus(), [Item::STATUS_AVAILABLE, Item::STATUS_EXPIRING, Item::STATUS_REFUND_SENT])) {
if ($dueDate >= $today && $item->getStatus() !== Item::STATUS_REFUND_SENT) {
$item->setStatus(Item::STATUS_EXPIRING);
} else {
$item->setStatus(Item::STATUS_EXPIRED);
}
}
}
}
public function decreaseAmount($i = 1) {
$this->amount -= $i;
}
}