<?php
namespace App\Entity;
use App\AbstractClass\AbstractOffer;
use App\Repository\OfferRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity(repositoryClass: OfferRepository::class)]
#[UniqueEntity("slug")]
class Offer extends AbstractOffer
{
const STATUS_IN_PROGRESS = 0;
const STATUS_ACTIVE = 1;
const STATUS_PAUSED = 2;
const STATUS_FINISHED = 3;
const STATUS_DELETED = 4;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
#[Assert\NotBlank(message:'Ce champ est obligatoire.')]
private $type;
#[ORM\Column(type: 'string', length: 255)]
#[Assert\NotBlank(message:'Ce champ est obligatoire.')]
private $title;
#[ORM\Column(type: 'string', length: 255, unique: true)]
private $slug = '';
#[ORM\Column(type: 'text', nullable: true)]
private $gibierDescription;
#[ORM\Column(type: 'text', nullable: true)]
private $teamExperienceDescription;
#[ORM\Column(type: 'text', nullable: true)]
#[Assert\NotBlank(message:'Ce champ est obligatoire.')]
private $unfoldsDay;
#[ORM\Column(type: 'text', nullable: true)]
private $huntingPlan;
#[ORM\Column(type: 'date', nullable: true)]
private $startAt;
#[ORM\Column(type: 'date', nullable: true)]
private $endAt;
#[ORM\Column(type: 'json', nullable: true)]
private $recurrence;
#[ORM\Column(type: 'float', nullable: true)]
#[Assert\Type(type: 'float', message: 'La valeur {{ value }} n\'est pas un nombre.')]
private $price;
#[ORM\Column(type: 'float', nullable: true)]
#[Assert\Type(type: 'float', message: 'La valeur {{ value }} n\'est pas un nombre.')]
private $halfActionPrice;
#[ORM\Column(type: 'integer', nullable: true)]
#[Assert\Type(type: 'integer', message: 'La valeur {{ value }} n\'est pas un entier.')]
private $halfActionNbDays;
#[ORM\Column(type: 'integer', nullable: true)]
#[Assert\Type(type: 'integer', message: 'La valeur {{ value }} n\'est pas un entier.')]
private $nbActionsAvailable;
#[ORM\Column(type: 'integer', nullable: true)]
#[Assert\Type(type: 'integer', message: 'La valeur {{ value }} n\'est pas un entier.')]
private $nbBraceletPersonMax;
#[ORM\Column(type: 'integer', nullable: true)]
#[Assert\Type(type: 'integer', message: 'La valeur {{ value }} n\'est pas un entier.')]
private $nbBraceletsAvailable;
#[ORM\Column(type: 'integer', nullable: true)]
#[Assert\Type(type: 'integer', message: 'La valeur {{ value }} n\'est pas un entier.')]
private $maxNbPersonsHuntingDay;
#[ORM\Column(type: 'integer', nullable: true)]
#[Assert\Type(type: 'integer', message: 'La valeur {{ value }} n\'est pas un entier.')]
private $nbPlacesDay;
#[ORM\Column(type: 'text', nullable: true)]
private $furtherInformation;
#[ORM\Column(type: 'boolean', nullable: true)]
private $companions;
#[ORM\Column(type: 'boolean', nullable: true)]
private $halfAction;
#[ORM\Column(type: 'integer', nullable: true)]
private $status;
#[ORM\Column(type: 'boolean', nullable: true)]
private $isPaused;
#[ORM\ManyToOne(targetEntity: Territory::class, inversedBy: 'offers')]
private $territory;
#[ORM\ManyToMany(targetEntity: HuntingMode::class)]
private $huntingMode;
#[ORM\ManyToMany(targetEntity: Weapon::class)]
private $weapons;
#[ORM\ManyToOne(targetEntity: User::class)]
#[ORM\JoinColumn(nullable: true)]
private $createdBy;
#[ORM\OneToMany(mappedBy: 'offer', targetEntity: Booking::class, orphanRemoval: true)]
private $bookings;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private $equipement;
#[ORM\Column(type: 'text', nullable: true)]
private $planningText;
#[ORM\Column(type: 'boolean', nullable: true)]
private $isTuchassouSelected;
#[ORM\OneToMany(mappedBy: 'offer', targetEntity: OfferAdditionalService::class, orphanRemoval: true, cascade: ["persist"])]
private $offerAdditionalServices;
#[ORM\Column(type: 'json', nullable: true)]
private $disponibilities = [];
#[ORM\Column(type: 'integer', nullable: true)]
#[Assert\Type(type: 'integer', message: 'La valeur {{ value }} n\'est pas un entier.')]
private $quantityBooked;
#[ORM\Column(type: 'datetime', nullable: true)]
private $createdAt;
#[ORM\ManyToMany(targetEntity: Animal::class)]
private $animals;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private $caractericGibier;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private $cover;
#[ORM\Column(type: 'float', nullable: true)]
private $fraisChasseur;
#[ORM\Column(type: 'float', nullable: true)]
private $fraisHote;
#[ORM\OneToMany(mappedBy: 'offer', targetEntity: OfferCancellationRequest::class)]
private $offerCancellationRequests;
#[ORM\OneToMany(mappedBy: 'offer', targetEntity: OfferImage::class, orphanRemoval: true, cascade: ["persist"])]
private $offerImages;
#[ORM\ManyToMany(targetEntity: Gibier::class, inversedBy: 'allOffers')]
private $gibiers;
#[ORM\Column(type: 'text', nullable: true)]
private $recurrenceException = "[]";
#[ORM\Column(type: 'boolean', nullable: true)]
private $isManagedByTuchassou;
public function __construct()
{
$this->weapons = new ArrayCollection();
$this->bookings = new ArrayCollection();
$this->offerAdditionalServices = new ArrayCollection();
$this->createdAt = new \DateTime();
$this->animals = new ArrayCollection();
$this->huntingMode = new ArrayCollection();
$this->offerCancellationRequests = new ArrayCollection();
$this->offerImages = new ArrayCollection();
$this->gibiers = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getType(): ?string
{
return $this->type;
}
public function setType(string $type): self
{
$this->type = $type;
return $this;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getGibierDescription(): ?string
{
return $this->gibierDescription;
}
public function setGibierDescription(?string $gibierDescription): self
{
$this->gibierDescription = $gibierDescription;
return $this;
}
public function getTeamExperienceDescription(): ?string
{
return $this->teamExperienceDescription;
}
public function setTeamExperienceDescription(?string $teamExperienceDescription): self
{
$this->teamExperienceDescription = $teamExperienceDescription;
return $this;
}
public function getUnfoldsDay(): ?string
{
return $this->unfoldsDay;
}
public function setUnfoldsDay(?string $unfoldsDay): self
{
$this->unfoldsDay = $unfoldsDay;
return $this;
}
public function getHuntingPlan(): ?string
{
return $this->huntingPlan;
}
public function setHuntingPlan(?string $huntingPlan): self
{
$this->huntingPlan = $huntingPlan;
return $this;
}
public function getStartAt(): ?\DateTimeInterface
{
return $this->startAt;
}
public function setStartAt(?\DateTimeInterface $startAt): self
{
$this->startAt = $startAt;
return $this;
}
public function getEndAt(): ?\DateTimeInterface
{
return $this->endAt;
}
public function setEndAt(?\DateTimeInterface $endAt): self
{
$this->endAt = $endAt;
return $this;
}
public function getRecurrence(): ?array
{
return $this->recurrence;
}
public function setRecurrence(?array $recurrence): self
{
$this->recurrence = $recurrence;
return $this;
}
public function getHotePrice(): ?float
{
if(is_null($this->price) || $this->price === 0) return null;
return $this->price/(1+(floatval((is_null($this->fraisHote)) ? $this->getHoteParameters($this->type) : $this->fraisHote)/100));
}
public function getPrice(): ?float
{
return $this->price;
}
public function getBasePrice(): ?float
{
if(is_null($this->price) || $this->price === 0) return null;
$price = $this->price;
$frais = $price*floatval((is_null($this->fraisChasseur)) ? $this->getParameters($this->type)/100 : $this->fraisChasseur/100);
$frais += $frais*0.2;
return $price+$frais;
}
public function setPrice(?float $price): self
{
$this->price = $price;
return $this;
}
public function getBaseHalfActionPrice(): ?float
{
if(is_null($this->halfActionPrice) || $this->halfActionPrice === 0) return null;
$price = $this->halfActionPrice;
$price += $price*floatval((is_null($this->fraisChasseur)) ? $this->getParameters($this->type)/100 : $this->fraisChasseur/100);
return $price;
}
public function getBaseHalfActionHotePrice(): ?float
{
if(is_null($this->halfActionPrice) || $this->halfActionPrice === 0) return null;
return $this->halfActionPrice/(1+(floatval((is_null($this->fraisHote)) ? $this->getHoteParameters($this->type) : $this->fraisHote)/100));
}
public function getHalfActionPrice(): ?float
{
return $this->halfActionPrice;
}
public function setHalfActionPrice(?float $halfActionPrice): self
{
$this->halfActionPrice = $halfActionPrice;
return $this;
}
public function getHalfActionNbDays(): ?int
{
return $this->halfActionNbDays;
}
public function setHalfActionNbDays(?int $halfActionNbDays): self
{
$this->halfActionNbDays = $halfActionNbDays;
return $this;
}
public function getNbActionsAvailable(): ?int
{
$quantityBooked = 0;
foreach ($this->getBookings() as $booking) {
if($booking->getStatus() === Booking::STATUS_ACCEPTED) {
$quantityBooked += $booking->getQuantity();
}
}
return ($this->nbActionsAvailable - $quantityBooked === 0) ? null : $this->nbActionsAvailable - $quantityBooked;
}
public function setNbActionsAvailable(?int $nbActionsAvailable): self
{
$this->nbActionsAvailable = $nbActionsAvailable;
return $this;
}
public function getNbBraceletPersonMax(): ?int
{
return $this->nbBraceletPersonMax;
}
public function setNbBraceletPersonMax(?int $nbBraceletPersonMax): self
{
$this->nbBraceletPersonMax = $nbBraceletPersonMax;
return $this;
}
public function getNbBraceletsAvailable(): ?int
{
return $this->nbBraceletsAvailable;
}
public function setNbBraceletsAvailable(?int $nbBraceletsAvailable): self
{
$this->nbBraceletsAvailable = $nbBraceletsAvailable;
return $this;
}
public function getMaxNbPersonsHuntingDay(): ?int
{
return $this->maxNbPersonsHuntingDay;
}
public function setMaxNbPersonsHuntingDay(?int $maxNbPersonsHuntingDay): self
{
$this->maxNbPersonsHuntingDay = $maxNbPersonsHuntingDay;
return $this;
}
public function getNbPlacesDay(): ?int
{
return $this->nbPlacesDay;
}
public function setNbPlacesDay(?int $nbPlacesDay): self
{
$this->nbPlacesDay = $nbPlacesDay;
return $this;
}
public function getFurtherInformation(): ?string
{
return $this->furtherInformation;
}
public function setFurtherInformation(?string $furtherInformation): self
{
$this->furtherInformation = $furtherInformation;
return $this;
}
public function getCompanions(): ?bool
{
return $this->companions;
}
public function setCompanions(?bool $companions): self
{
$this->companions = $companions;
return $this;
}
public function getHalfAction(): ?bool
{
return $this->halfAction;
}
public function setHalfAction(?bool $halfAction): self
{
$this->halfAction = $halfAction;
return $this;
}
public function getStatus(): ?int
{
if(!is_null($this->endAt)) {
$now = new \DateTime();
if($this->endAt < $now) {
return self::STATUS_FINISHED;
}
}
if(!is_null($this->disponibilities) && !empty($this->disponibilities)) {
usort($this->disponibilities, function($a, $b) {
$a = \DateTime::createFromFormat('d/m/Y', $a['date']);
$b = \DateTime::createFromFormat('d/m/Y', $b['date']);
return $a <=> $b;
});
$end = \DateTime::createFromFormat('d/m/Y', $this->disponibilities[count($this->disponibilities) - 1]['date']);
if ($end) {
$end->setTime(23, 59, 59);
}
$now = new \DateTime();
if($end < $now) {
return self::STATUS_FINISHED;
}
}
return $this->status;
}
public function setStatus(?int $status): self
{
$this->status = $status;
return $this;
}
public function getIsPaused(): ?bool
{
return $this->isPaused;
}
public function setIsPaused(?bool $isPaused): self
{
$this->isPaused = $isPaused;
return $this;
}
public function getTerritory(): ?Territory
{
return $this->territory;
}
public function setTerritory(?Territory $territory): self
{
$this->territory = $territory;
return $this;
}
/**
* @return Collection<int, Weapon>
*/
public function getWeapons(): Collection
{
return $this->weapons;
}
public function addWeapon(Weapon $weapon): self
{
if (!$this->weapons->contains($weapon)) {
$this->weapons[] = $weapon;
}
return $this;
}
public function removeWeapon(Weapon $weapon): self
{
$this->weapons->removeElement($weapon);
return $this;
}
public function getCreatedBy(): ?User
{
return $this->createdBy;
}
public function setCreatedBy(?User $createdBy): self
{
$this->createdBy = $createdBy;
return $this;
}
/**
* @return Collection<int, Booking>
*/
public function getBookings(): Collection
{
return $this->bookings;
}
public function addBooking(Booking $booking): self
{
if (!$this->bookings->contains($booking)) {
$this->bookings[] = $booking;
$booking->setOffer($this);
}
return $this;
}
public function removeBooking(Booking $booking): self
{
if ($this->bookings->removeElement($booking)) {
// set the owning side to null (unless already changed)
if ($booking->getOffer() === $this) {
$booking->setOffer(null);
}
}
return $this;
}
public function getNbReservation(): int
{
$count = 0;
foreach ($this->bookings as $booking) {
if($booking->getStatus() === Booking::STATUS_ACCEPTED) $count++;
}
return $count;
}
public function getCA(): float
{
$chiffre = 0;
foreach ($this->bookings as $booking) {
if($booking->getStatus() === Booking::STATUS_ACCEPTED) $chiffre += $booking->getTotalTtc();
}
return $chiffre;
}
public function getEquipement(): ?string
{
return $this->equipement;
}
public function setEquipement(?string $equipement): self
{
$this->equipement = $equipement;
return $this;
}
public function getPlanningText(): ?string
{
return $this->planningText;
}
public function setPlanningText(?string $planningText): self
{
$this->planningText = $planningText;
return $this;
}
public function getIsTuchassouSelected(): ?bool
{
return $this->isTuchassouSelected;
}
public function setIsTuchassouSelected(?bool $isTuchassouSelected): self
{
$this->isTuchassouSelected = $isTuchassouSelected;
return $this;
}
/**
* @return Collection<int, OfferAdditionalService>
*/
public function getOfferAdditionalServices(): Collection
{
return $this->offerAdditionalServices;
}
public function addOfferAdditionalService(OfferAdditionalService $offerAdditionalService): self
{
if (!$this->offerAdditionalServices->contains($offerAdditionalService)) {
$this->offerAdditionalServices[] = $offerAdditionalService;
$offerAdditionalService->setOffer($this);
}
return $this;
}
public function removeOfferAdditionalService(OfferAdditionalService $offerAdditionalService): self
{
if ($this->offerAdditionalServices->removeElement($offerAdditionalService)) {
// set the owning side to null (unless already changed)
if ($offerAdditionalService->getOffer() === $this) {
$offerAdditionalService->setOffer(null);
}
}
return $this;
}
public function getDisponibilities(): ?array
{
$sorted = $this->disponibilities;
sort($this->disponibilities);
return $sorted;
}
public function setDisponibilities(?array $disponibilities): self
{
$this->disponibilities = $disponibilities;
return $this;
}
public function hasShowServices(): ?bool
{
foreach ($this->offerAdditionalServices as $service)
if ($service->getIsActive()) return true;
return false;
}
public function getValidDays(): ?array
{
$dates = [];
$recurrenceExceptions = json_decode($this->getRecurrenceException());
$exceptions = [];
if ($recurrenceExceptions) {
foreach ($recurrenceExceptions as $recurrenceException) {
$exceptions[] = $recurrenceException->date;
}
}
if($this->getDisponibilities() && !empty($this->getDisponibilities())) {
foreach ($this->getDisponibilities() as $disponibility) {
if(!empty($disponibility) && !is_null($disponibility['date']) && !in_array($disponibility['date'], $exceptions))
$dates[] = \DateTime::createFromFormat('d/m/Y', $disponibility['date'])->format('Y-m-d');
}
}
if (!is_null($this->startAt) && !is_null($this->endAt)){
$period = new \DatePeriod(
$this->startAt,
new \DateInterval('P1D'),
$this->endAt->modify('+1 day')
);
if($this->getRecurrence() && !empty($this->getRecurrence())) {
$days = [
'dimanche',
'lundi',
'mardi',
'mercredi',
'jeudi',
'vendredi',
'samedi'
];
foreach ($period as $key => $value) {
if(in_array($days[$value->format('w')], $this->getRecurrence())) {
$formatted = $value->format('d/m/Y');
if (!in_array($formatted, $exceptions)) {
$dates[] = $value->format('Y-m-d');
}
}
}
} else {
foreach ($period as $key => $value) {
$dates[] = $value->format('Y-m-d');
}
}
}
return array_unique(array_values(array_diff($dates, $this->getFullBookedDays())));
}
public function getFullBookedDays(): array
{
$fullDays = [];
$booked = $this->getBookingDays();
foreach ($booked as $day => $quantity) {
if($this->getNbPlacesDay() <= $quantity)
$fullDays[] = $day;
}
return $fullDays;
}
public function getQuantityBooked(): ?int
{
return $this->quantityBooked;
}
public function setQuantityBooked(?int $quantityBooked): self
{
$this->quantityBooked = $quantityBooked;
return $this;
}
public function getNbBraceletAvaibleToBooking(): ?int
{
return $this->nbBraceletsAvailable;
}
public function getIntialPrice(): ?float
{
return $this->price;
}
public function intialHalfPrice(): ?float
{
return $this->halfActionPrice;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(?\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getBookingDays(): array
{
$result = [];
/** @var Booking $booking */
foreach ($this->bookings as $booking) {
if($booking->getStatus() !== Booking::STATUS_ACCEPTED) continue;
if($booking->getDateStart() && $booking->getDateEnd() && $booking->getDateStart()->format('Y-m-d') !== $booking->getDateEnd()->format('Y-m-d')) {
$interval = \DateInterval::createFromDateString('1 day');
$period = new \DatePeriod($booking->getDateStart(), $interval, $booking->getDateEnd());
foreach ($period as $day) {
if(array_key_exists($day->format('Y-m-d'), $result)) {
$result[$day->format('Y-m-d')] += $booking->getQuantity();
} else {
$result[$day->format('Y-m-d')] = $booking->getQuantity();
}
}
} elseif ($booking->getDateStart()) {
if(array_key_exists($booking->getDateStart()->format('Y-m-d'), $result)) {
$result[$booking->getDateStart()->format('Y-m-d')] += $booking->getQuantity();
} else {
$result[$booking->getDateStart()->format('Y-m-d')] = $booking->getQuantity();
}
}
}
return $result;
}
/**
* @return Collection<int, Animal>
*/
public function getAnimals(): Collection
{
return $this->animals;
}
public function addAnimal(Animal $animal): self
{
if (!$this->animals->contains($animal)) {
$this->animals[] = $animal;
}
return $this;
}
public function removeAnimal(Animal $animal): self
{
$this->animals->removeElement($animal);
return $this;
}
public function getCaractericGibier(): ?string
{
return $this->caractericGibier;
}
public function setCaractericGibier(?string $caractericGibier): self
{
$this->caractericGibier = $caractericGibier;
return $this;
}
public function getFraisService(): ?float
{
return $this->getPrice() - $this->getIntialPrice();
}
public function getCover(): ?string
{
return $this->cover;
}
public function setCover(?string $cover): self
{
$this->cover = $cover;
return $this;
}
public function getImages(): ?array
{
$images = [];
if($this->cover) $images[] = $this->cover;
if(!empty($this->offerImages)) {
foreach ($this->offerImages as $image) {
$images[] = $image->getPath();
}
}
return $images;
}
public function getFraisChasseur(): ?float
{
return $this->fraisChasseur;
}
public function setFraisChasseur(?float $fraisChasseur): self
{
$this->fraisChasseur = $fraisChasseur;
return $this;
}
public function getFraisHote(): ?float
{
return $this->fraisHote;
}
public function setFraisHote(?float $fraisHote): self
{
$this->fraisHote = $fraisHote;
return $this;
}
/**
* @return Collection<int, HuntingMode>
*/
public function getHuntingMode(): Collection
{
return $this->huntingMode;
}
public function addHuntingMode(HuntingMode $huntingMode): self
{
if (!$this->huntingMode->contains($huntingMode)) {
$this->huntingMode[] = $huntingMode;
}
return $this;
}
public function removeHuntingMode(HuntingMode $huntingMode): self
{
$this->huntingMode->removeElement($huntingMode);
return $this;
}
/**
* @return string|null
*/
public function getSlug(): ?string
{
return $this->slug;
}
/**
* @param string $slug
* @return slef
*/
public function setSlug(string $slug): self
{
$this->slug = $slug;
return $this;
}
public function getHigthKeyDispo(): int
{
if (empty($this->disponibilities)) return 0;
return max(array_keys($this->disponibilities))+1;
}
/**
* @return Collection<int, OfferCancellationRequest>
*/
public function getOfferCancellationRequests(): Collection
{
return $this->offerCancellationRequests;
}
public function addOfferCancellationRequest(OfferCancellationRequest $offerCancellationRequest): self
{
if (!$this->offerCancellationRequests->contains($offerCancellationRequest)) {
$this->offerCancellationRequests[] = $offerCancellationRequest;
$offerCancellationRequest->setOffer($this);
}
return $this;
}
public function removeOfferCancellationRequest(OfferCancellationRequest $offerCancellationRequest): self
{
if ($this->offerCancellationRequests->removeElement($offerCancellationRequest)) {
// set the owning side to null (unless already changed)
if ($offerCancellationRequest->getOffer() === $this) {
$offerCancellationRequest->setOffer(null);
}
}
return $this;
}
/**
* @return Collection<int, OfferImage>
*/
public function getOfferImages(): Collection
{
return $this->offerImages;
}
public function addOfferImage(OfferImage $offerImage): self
{
if (!$this->offerImages->contains($offerImage)) {
$this->offerImages[] = $offerImage;
$offerImage->setOffer($this);
}
return $this;
}
public function removeOfferImage(OfferImage $offerImage): self
{
if ($this->offerImages->removeElement($offerImage)) {
// set the owning side to null (unless already changed)
if ($offerImage->getOffer() === $this) {
$offerImage->setOffer(null);
}
}
return $this;
}
/**
* @return Collection<int, Gibier>
*/
public function getGibiers(): Collection
{
return $this->gibiers;
}
public function addGibier(Gibier $gibier): self
{
if (!$this->gibiers->contains($gibier)) {
$this->gibiers[] = $gibier;
}
return $this;
}
public function removeGibier(Gibier $gibier): self
{
$this->gibiers->removeElement($gibier);
return $this;
}
public function getRecurrenceException(): ?string
{
return $this->recurrenceException;
}
public function setRecurrenceException(?string $recurrenceException): self
{
$this->recurrenceException = $recurrenceException;
return $this;
}
public function getIsManagedByTuchassou(): ?bool
{
return $this->isManagedByTuchassou;
}
public function setIsManagedByTuchassou(?bool $isManagedByTuchassou): self
{
$this->isManagedByTuchassou = $isManagedByTuchassou;
return $this;
}
}