625 lines
15 KiB
PHP
625 lines
15 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Entity\Groups;
|
|
|
|
use App\Entity\BlameableEntity;
|
|
use App\Entity\BlameableEntityInterface;
|
|
use App\Entity\TimestampableEntity;
|
|
use App\Entity\TimestampableEntityInterface;
|
|
use App\Entity\User;
|
|
use App\Enum\Groups\AccommodationBookingStatus;
|
|
use App\Enum\Groups\AccommodationBookingType;
|
|
use App\Enum\Groups\AdditionalServiceType;
|
|
use App\Repository\Groups\AccommodationBookingRepository;
|
|
use Doctrine\DBAL\Types\Types;
|
|
use Doctrine\ORM\Mapping as ORM;
|
|
use Symfony\Component\Uid\Uuid;
|
|
use Symfony\Component\Validator\Constraints as Assert;
|
|
|
|
#[ORM\Entity(repositoryClass: AccommodationBookingRepository::class)]
|
|
class AccommodationBooking implements BlameableEntityInterface, TimestampableEntityInterface
|
|
{
|
|
use BlameableEntity;
|
|
use TimestampableEntity;
|
|
|
|
#[ORM\Id]
|
|
#[ORM\GeneratedValue]
|
|
#[ORM\Column]
|
|
private ?int $id = null;
|
|
|
|
#[ORM\Column(length: 36, unique: true)]
|
|
private string $uuid;
|
|
|
|
#[ORM\ManyToOne]
|
|
#[ORM\JoinColumn(nullable: false)]
|
|
private ?Accommodation $accommodation = null;
|
|
|
|
#[ORM\Column(type: Types::DATE_IMMUTABLE)]
|
|
private ?\DateTimeImmutable $dateFrom = null;
|
|
|
|
#[ORM\Column(type: Types::DATE_IMMUTABLE)]
|
|
private ?\DateTimeImmutable $dateTo = null;
|
|
|
|
#[ORM\Column]
|
|
private int $paxCount = 0;
|
|
|
|
#[ORM\Column]
|
|
private int $minorsCount = 0;
|
|
|
|
#[ORM\Column]
|
|
private int $childrenCount = 0;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $boardServiceLabel = null;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
private ?int $boardServicePrice = null;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
private ?int $boardServiceOriginalId = null;
|
|
|
|
/** @var list<array{label: string, price: int, type: string, originalServiceId: int|null}> */
|
|
#[ORM\Column(type: Types::JSON)]
|
|
private array $additionalServices = [];
|
|
|
|
/** @var array<string, mixed>|null */
|
|
#[ORM\Column(type: Types::JSON, nullable: true)]
|
|
private ?array $priceBreakdown = null;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
private ?int $totalPrice = null;
|
|
|
|
#[ORM\Column(length: 3, nullable: true)]
|
|
private ?string $pricingCurrency = null;
|
|
|
|
#[ORM\Column(type: Types::SMALLINT, nullable: true)]
|
|
private ?int $pricingVersion = null;
|
|
|
|
#[ORM\Column(length: 20, enumType: AccommodationBookingStatus::class)]
|
|
private AccommodationBookingStatus $status = AccommodationBookingStatus::Draft;
|
|
|
|
/**
|
|
* What kind of record this is — an offer the customer still has to accept, or a
|
|
* directly placed, binding booking. Independent of the status: an accepted offer
|
|
* keeps its Inquiry type, which is how offer-originated bookings stay recognisable.
|
|
*/
|
|
#[ORM\Column(length: 20, enumType: AccommodationBookingType::class)]
|
|
private AccommodationBookingType $type = AccommodationBookingType::Inquiry;
|
|
|
|
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
|
|
private ?\DateTimeImmutable $accessLinkIssuedAt = null;
|
|
|
|
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
|
|
private ?\DateTimeImmutable $acceptedAt = null;
|
|
|
|
/**
|
|
* When the office validated the requested services and capacities and released the
|
|
* booking confirmation to the customer — deliberately distinct from acceptedAt, which
|
|
* only records that the customer committed.
|
|
*/
|
|
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
|
|
private ?\DateTimeImmutable $confirmedAt = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
#[Assert\NotBlank(groups: ['edit'])]
|
|
private ?string $groupName = null;
|
|
|
|
#[ORM\Column(length: 10, nullable: true)]
|
|
private ?string $salutation = null;
|
|
|
|
#[ORM\Column(length: 100, nullable: true)]
|
|
#[Assert\NotBlank(groups: ['edit'])]
|
|
private ?string $firstName = null;
|
|
|
|
#[ORM\Column(length: 100, nullable: true)]
|
|
#[Assert\NotBlank(groups: ['edit'])]
|
|
private ?string $lastName = null;
|
|
|
|
/**
|
|
* The `offer` group covers the one field a customer-facing booking cannot do without:
|
|
* the access link and every notification are addressed to it. It is enforced on
|
|
* creation as soon as the booking is not a draft, while `edit` additionally demands
|
|
* the rest of the contact data.
|
|
*/
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
#[Assert\NotBlank(groups: ['edit', 'offer'])]
|
|
#[Assert\Email(groups: ['edit', 'offer'])]
|
|
private ?string $email = null;
|
|
|
|
#[ORM\Column(length: 50, nullable: true)]
|
|
private ?string $phone = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $street = null;
|
|
|
|
#[ORM\Column(length: 20, nullable: true)]
|
|
private ?string $zip = null;
|
|
|
|
#[ORM\Column(length: 100, nullable: true)]
|
|
private ?string $city = null;
|
|
|
|
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
|
private ?string $remarks = null;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
#[Assert\Range(min: 1, max: 100)]
|
|
private ?int $accommodationDiscount = null;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
#[Assert\Range(min: 1, max: 100)]
|
|
private ?int $boardServiceDiscount = null;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
#[Assert\Range(min: 1, max: 100)]
|
|
private ?int $additionalServicesDiscount = null;
|
|
|
|
#[ORM\ManyToOne]
|
|
#[ORM\JoinColumn(onDelete: 'SET NULL')]
|
|
private ?User $managedBy = null;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->uuid = Uuid::v4()->toRfc4122();
|
|
}
|
|
|
|
public function getId(): ?int
|
|
{
|
|
return $this->id;
|
|
}
|
|
|
|
public function getUuid(): string
|
|
{
|
|
return $this->uuid;
|
|
}
|
|
|
|
public function getAccommodation(): ?Accommodation
|
|
{
|
|
return $this->accommodation;
|
|
}
|
|
|
|
public function setAccommodation(?Accommodation $accommodation): self
|
|
{
|
|
$this->accommodation = $accommodation;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getDateFrom(): ?\DateTimeImmutable
|
|
{
|
|
return $this->dateFrom;
|
|
}
|
|
|
|
public function setDateFrom(\DateTimeImmutable $dateFrom): self
|
|
{
|
|
$this->dateFrom = $dateFrom;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getDateTo(): ?\DateTimeImmutable
|
|
{
|
|
return $this->dateTo;
|
|
}
|
|
|
|
public function setDateTo(\DateTimeImmutable $dateTo): self
|
|
{
|
|
$this->dateTo = $dateTo;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getNights(): int
|
|
{
|
|
if (null === $this->dateFrom || null === $this->dateTo) {
|
|
return 0;
|
|
}
|
|
|
|
return $this->dateFrom->diff($this->dateTo)->days;
|
|
}
|
|
|
|
public function getPaxCount(): int
|
|
{
|
|
return $this->paxCount;
|
|
}
|
|
|
|
public function setPaxCount(int $paxCount): self
|
|
{
|
|
$this->paxCount = $paxCount;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getMinorsCount(): int
|
|
{
|
|
return $this->minorsCount;
|
|
}
|
|
|
|
public function setMinorsCount(int $minorsCount): self
|
|
{
|
|
$this->minorsCount = $minorsCount;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getChildrenCount(): int
|
|
{
|
|
return $this->childrenCount;
|
|
}
|
|
|
|
public function setChildrenCount(int $childrenCount): self
|
|
{
|
|
$this->childrenCount = $childrenCount;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getBoardServiceLabel(): ?string
|
|
{
|
|
return $this->boardServiceLabel;
|
|
}
|
|
|
|
public function setBoardServiceLabel(?string $boardServiceLabel): self
|
|
{
|
|
$this->boardServiceLabel = $boardServiceLabel;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getBoardServicePrice(): ?int
|
|
{
|
|
return $this->boardServicePrice;
|
|
}
|
|
|
|
public function setBoardServicePrice(?int $boardServicePrice): self
|
|
{
|
|
$this->boardServicePrice = $boardServicePrice;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getBoardServiceOriginalId(): ?int
|
|
{
|
|
return $this->boardServiceOriginalId;
|
|
}
|
|
|
|
public function setBoardServiceOriginalId(?int $boardServiceOriginalId): self
|
|
{
|
|
$this->boardServiceOriginalId = $boardServiceOriginalId;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/** @return list<array{label: string, price: int, type: string, originalServiceId: int|null}> */
|
|
public function getAdditionalServices(): array
|
|
{
|
|
return $this->additionalServices;
|
|
}
|
|
|
|
/** @param list<array{label: string, price: int, type: string, originalServiceId: int|null}> $additionalServices */
|
|
public function setAdditionalServices(array $additionalServices): self
|
|
{
|
|
$this->additionalServices = $additionalServices;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function addAdditionalServiceSnapshot(string $label, int $price, AdditionalServiceType|string $type, ?int $originalServiceId): self
|
|
{
|
|
$this->additionalServices[] = [
|
|
'label' => $label,
|
|
'price' => $price,
|
|
'type' => $type instanceof AdditionalServiceType ? $type->value : $type,
|
|
'originalServiceId' => $originalServiceId,
|
|
];
|
|
|
|
return $this;
|
|
}
|
|
|
|
/** @return array<string, mixed>|null */
|
|
public function getPriceBreakdown(): ?array
|
|
{
|
|
return $this->priceBreakdown;
|
|
}
|
|
|
|
/** @param array<string, mixed> $priceBreakdown */
|
|
public function setPriceSnapshot(array $priceBreakdown, int $totalPrice, string $currency, int $version): self
|
|
{
|
|
$this->priceBreakdown = $priceBreakdown;
|
|
$this->totalPrice = $totalPrice;
|
|
$this->pricingCurrency = $currency;
|
|
$this->pricingVersion = $version;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function clearPriceSnapshot(): self
|
|
{
|
|
$this->priceBreakdown = null;
|
|
$this->totalPrice = null;
|
|
$this->pricingCurrency = null;
|
|
$this->pricingVersion = null;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getTotalPrice(): ?int
|
|
{
|
|
return $this->totalPrice;
|
|
}
|
|
|
|
public function getPricingCurrency(): ?string
|
|
{
|
|
return $this->pricingCurrency;
|
|
}
|
|
|
|
public function getPricingVersion(): ?int
|
|
{
|
|
return $this->pricingVersion;
|
|
}
|
|
|
|
public function getStatus(): AccommodationBookingStatus
|
|
{
|
|
return $this->status;
|
|
}
|
|
|
|
public function setStatus(AccommodationBookingStatus $status): self
|
|
{
|
|
$this->status = $status;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isDraft(): bool
|
|
{
|
|
return AccommodationBookingStatus::Draft === $this->status;
|
|
}
|
|
|
|
public function isOpen(): bool
|
|
{
|
|
return AccommodationBookingStatus::Open === $this->status;
|
|
}
|
|
|
|
public function isReceived(): bool
|
|
{
|
|
return AccommodationBookingStatus::Received === $this->status;
|
|
}
|
|
|
|
public function isConfirmed(): bool
|
|
{
|
|
return AccommodationBookingStatus::Confirmed === $this->status;
|
|
}
|
|
|
|
public function isDiscarded(): bool
|
|
{
|
|
return AccommodationBookingStatus::Discarded === $this->status;
|
|
}
|
|
|
|
public function getType(): AccommodationBookingType
|
|
{
|
|
return $this->type;
|
|
}
|
|
|
|
public function setType(AccommodationBookingType $type): self
|
|
{
|
|
$this->type = $type;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isInquiry(): bool
|
|
{
|
|
return AccommodationBookingType::Inquiry === $this->type;
|
|
}
|
|
|
|
public function isBooking(): bool
|
|
{
|
|
return AccommodationBookingType::Booking === $this->type;
|
|
}
|
|
|
|
public function getAccessLinkIssuedAt(): ?\DateTimeImmutable
|
|
{
|
|
return $this->accessLinkIssuedAt;
|
|
}
|
|
|
|
public function setAccessLinkIssuedAt(?\DateTimeImmutable $accessLinkIssuedAt): self
|
|
{
|
|
$this->accessLinkIssuedAt = $accessLinkIssuedAt;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getAcceptedAt(): ?\DateTimeImmutable
|
|
{
|
|
return $this->acceptedAt;
|
|
}
|
|
|
|
public function setAcceptedAt(?\DateTimeImmutable $acceptedAt): self
|
|
{
|
|
$this->acceptedAt = $acceptedAt;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getConfirmedAt(): ?\DateTimeImmutable
|
|
{
|
|
return $this->confirmedAt;
|
|
}
|
|
|
|
public function setConfirmedAt(?\DateTimeImmutable $confirmedAt): self
|
|
{
|
|
$this->confirmedAt = $confirmedAt;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getGroupName(): ?string
|
|
{
|
|
return $this->groupName;
|
|
}
|
|
|
|
public function setGroupName(?string $groupName): self
|
|
{
|
|
$this->groupName = $groupName;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getSalutation(): ?string
|
|
{
|
|
return $this->salutation;
|
|
}
|
|
|
|
public function setSalutation(?string $salutation): self
|
|
{
|
|
$this->salutation = $salutation;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getFirstName(): ?string
|
|
{
|
|
return $this->firstName;
|
|
}
|
|
|
|
public function setFirstName(?string $firstName): self
|
|
{
|
|
$this->firstName = $firstName;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getLastName(): ?string
|
|
{
|
|
return $this->lastName;
|
|
}
|
|
|
|
public function setLastName(?string $lastName): self
|
|
{
|
|
$this->lastName = $lastName;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getEmail(): ?string
|
|
{
|
|
return $this->email;
|
|
}
|
|
|
|
public function setEmail(?string $email): self
|
|
{
|
|
$this->email = $email;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getPhone(): ?string
|
|
{
|
|
return $this->phone;
|
|
}
|
|
|
|
public function setPhone(?string $phone): self
|
|
{
|
|
$this->phone = $phone;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getStreet(): ?string
|
|
{
|
|
return $this->street;
|
|
}
|
|
|
|
public function setStreet(?string $street): self
|
|
{
|
|
$this->street = $street;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getZip(): ?string
|
|
{
|
|
return $this->zip;
|
|
}
|
|
|
|
public function setZip(?string $zip): self
|
|
{
|
|
$this->zip = $zip;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getCity(): ?string
|
|
{
|
|
return $this->city;
|
|
}
|
|
|
|
public function setCity(?string $city): self
|
|
{
|
|
$this->city = $city;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getRemarks(): ?string
|
|
{
|
|
return $this->remarks;
|
|
}
|
|
|
|
public function setRemarks(?string $remarks): self
|
|
{
|
|
$this->remarks = $remarks;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getAccommodationDiscount(): ?int
|
|
{
|
|
return $this->accommodationDiscount;
|
|
}
|
|
|
|
public function setAccommodationDiscount(?int $accommodationDiscount): self
|
|
{
|
|
$this->accommodationDiscount = $accommodationDiscount;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getBoardServiceDiscount(): ?int
|
|
{
|
|
return $this->boardServiceDiscount;
|
|
}
|
|
|
|
public function setBoardServiceDiscount(?int $boardServiceDiscount): self
|
|
{
|
|
$this->boardServiceDiscount = $boardServiceDiscount;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getAdditionalServicesDiscount(): ?int
|
|
{
|
|
return $this->additionalServicesDiscount;
|
|
}
|
|
|
|
public function setAdditionalServicesDiscount(?int $additionalServicesDiscount): self
|
|
{
|
|
$this->additionalServicesDiscount = $additionalServicesDiscount;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getManagedBy(): ?User
|
|
{
|
|
return $this->managedBy;
|
|
}
|
|
|
|
public function setManagedBy(?User $managedBy): self
|
|
{
|
|
$this->managedBy = $managedBy;
|
|
|
|
return $this;
|
|
}
|
|
}
|