<?php
/**
* Created by Elements.at New Media Solutions GmbH
*
*/
namespace App\Model\Shop\Event;
use App\Ecommerce\PriceSystem\Event\PriceSystem;
use App\Model\Shop\CancelableInterface;
use App\Model\Shop\OrderItem;
use App\Model\Shop\TeaserInfoInterface;
use Carbon\Carbon;
use Elements\Bundle\RecurringDatesTypeBundle\Templating\RecurringDatesHelper;
use Elements\Bundle\TicketShopFrameworkBundle\Ecommerce\PricingManager\PriceInfo;
use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\ProductDateInterface;
use Elements\Bundle\TicketShopFrameworkBundle\Model\Shop\Product\CheckoutableInterface;
use Elements\Bundle\TicketShopFrameworkBundle\Model\Traits\ProductTrait;
use Pimcore\Bundle\EcommerceFrameworkBundle\AvailabilitySystem\AvailabilityInterface;
use Pimcore\Bundle\EcommerceFrameworkBundle\Model\ProductInterface;
use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\PriceInfoInterface;
use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\PriceInterface;
use Pimcore\Model\Asset\Image;
use Pimcore\Model\DataObject\AbstractObject;
use Pimcore\Model\DataObject\ShopEvent;
/**
* Class EventProduct
*
* @method null|ShopDynamicPrice getOrderedPriceItem()
*/
class EventProduct extends ShopEvent implements
ProductInterface,
CheckoutableInterface,
ProductDateInterface,
TeaserInfoInterface,
CancelableInterface
{
use ProductTrait;
const PRODUCT_TYPE = 'event';
public function getPriceSystemName(): ?string
{
return $this->getShopSystemName();
}
public function getAvailabilitySystemName(): ?string
{
return $this->getShopSystemName();
}
protected function getShopSystemName(): ?string
{
return self::PRODUCT_TYPE;
}
public function needsDelivery(): bool
{
return false;
}
public function isBookable(int $quantityScale = 1): bool
{
return $this->isPublished() && $this->getOSAvailabilityInfo($quantityScale)?->getAvailable();
}
public function getOSPrice($quantityScale = 1): ?PriceInterface
{
return $this->getOSPriceInfo($quantityScale)->getPrice();
}
public function getOSIsBookable($quantityScale = 1): bool
{
$price = $this->getOSPrice($quantityScale);
return $this->isBookable() && !empty($price->getGrossAmount()->asNumeric());
}
/** @phpstan-ignore-next-line */
public function getOSPriceInfo($quantityScale = 1, $products = null, Carbon $date = null): ?PriceInfoInterface
{
/** @var PriceSystem $priceSystem */
$priceSystem = $this->getPriceSystemImplementation();
return $priceSystem->getPriceInfo($this, $quantityScale, $products, $date);
}
public function getOSAvailabilityInfo($quantity = null): ?AvailabilityInterface
{
return $this->getAvailabilitySystemImplementation()->getAvailabilityInfo($this, $quantity);
}
public function getProductStartDate(): ?Carbon
{
return $this->getEventStartDate();
}
public function getTeaserPrice(): ?PriceInterface
{
/** @var PriceInfo $priceInfo */
$priceInfo = $this->getOSPriceInfo();
return $priceInfo->getBasePrice();
}
public function getTeaserTopTitle(): ?string
{
if ($category = $this->getProductCategory()) {
return $category->getName();
}
return '';
}
public function getTeaserDescription(): ?string
{
return $this->getShortDescription();
}
public function getTeaserImage(): ?Image
{
return $this->getMainImage();
}
/**
* Events got no validity date on the teaser
*
* @return array<mixed>
*/
public function getTeaserValidity(): array
{
return [];
}
/**
* @return ShopDynamicPrice[]
*/
public function getPriceObjects(): array
{
$priceObjs = [];
if ($items = $this->getPrices()) {
foreach ($items as $item) {
/** @var ShopDynamicPrice $priceObj */
$priceObj = $item->getObject();
if ($priceObj->getPrice()) {
$priceObjs[] = $priceObj;
}
}
}
return $priceObjs;
}
/**
* @return array<mixed>
*/
public function getPriceObjectGrouped(): array
{
$groupedObjs = [];
if ($items = $this->getPrices()) {
foreach ($items as $item) {
/** @var ShopDynamicPrice $priceObj */
$priceObj = $item->getObject();
if ($priceObj->getPrice()) {
$groupedObjs[$item->getGroupname()][] = $priceObj;
}
}
}
return $groupedObjs;
}
/**
* Get price group containing the given price description
*
* @param ShopDynamicPrice $priceDescription
*
* @return string
*/
public function getPriceGroupForPriceDescription(?ShopDynamicPrice $priceDescription): string
{
if ($priceDescription) {
/** @phpstan-ignore-next-line */
foreach ($this->getReference()->getPrices() as $price) {
if ($price->getObject()->getId() == $priceDescription->getId()) {
return $price->getGroupname();
}
}
}
return '';
}
/**
* @param Carbon|null $startDate
* @param ShopDynamicPrice|null $priceItem
*
* @return EventProduct
*
* @throws \Exception
*/
public function getOrCreateOrderableObject(Carbon $startDate = null, ShopDynamicPrice $priceItem = null): self
{
if ($this->getBookableOnDayOfEvent()) {
$startDate->endOfDay();
}
$key = sprintf('%s-%s-%s',
$this->getKey(),
$startDate->toDateTimeString(),
$priceItem ? $priceItem->getId() : ''
);
$list = new ShopEvent\Listing();
$list->setObjectTypes([AbstractObject::OBJECT_TYPE_VARIANT]);
$list->addConditionParam('o_parentId = ?', $this->getId());
$list->addConditionParam('ifnull(o_key,0) = ?', $key);
$list->addConditionParam('eventStartDate >= ?', $startDate->timestamp);
if ($list->count() == 0) {
// create variant for booking
$orderObject = new self();
$orderObject->setPublished(true);
$orderObject->setType(AbstractObject::OBJECT_TYPE_VARIANT);
$orderObject->setParent($this);
$orderObject->setKey($key);
$orderObject->setEventStartDate($startDate);
$orderObject->setOrderedPriceItem($priceItem);
$orderObject->save();
} else {
/** @var EventProduct $orderObject */
$orderObject = $list->current();
}
return $orderObject;
}
public function isPersonsTimeList(): bool
{
if ($prices = $this->getPrices()) {
$price = array_pop($prices);
return !empty($price->getGroupname());
}
return false;
}
public function isCancelable(OrderItem $orderItem = null): bool
{
return true;
}
public function getFirstValidValidityDate(Carbon $date): ?Carbon
{
$validityDate = null;
$firstValidityDateRange = $this->getFirstValidDateRange($date);
if (isset($firstValidityDateRange['from'])) {
/** @var Carbon $fromDateValidity */
$fromDateValidity = $firstValidityDateRange['from'];
$validityDate = $fromDateValidity->lt($date) ? $date : $fromDateValidity;
}
return $validityDate;
}
/**
* @param Carbon $date
*
* @return array<mixed>
*/
public function getFirstValidDateRange(Carbon $date): array
{
$recurringDatesHelper = new RecurringDatesHelper();
if ($validityDates = $recurringDatesHelper->getCalculatedDates($this, 'getValidityDates')) {
foreach ($validityDates as $validityDate) {
$fromDate = Carbon::parse($validityDate['fromDate']);
$toDate = Carbon::parse($validityDate['toDate']);
if (($validityDate['type'] == 'RecurringDate' || $validityDate['type'] == 'RecurringDateTime')
&& ($date->between($fromDate, $toDate) || $fromDate->gte($date))
) {
return [
'from' => $fromDate,
'to' => $toDate,
];
}
}
}
return [];
}
/**
*
* returns all valid date ranges now and in the future, if no dateformat is given, dats are returned as timestamp
*
* @param Carbon $date
*
* @return array<mixed>
*/
public function getAllValidDateRanges(?Carbon $date = null, string $dateFormat = null): array
{
$ranges = [];
if(!$date) {
$date = Carbon::now()->startOfDay();
}
if ($validDates = $this->getValidityDates()) {
if ($definitions = $validDates->getDefinitionArray()) {
foreach ($definitions as $definition) {
if(!isset($definition['values']['fromDate']) && isset($definition['values']['date'])) {
$fromDate = Carbon::parse($definition['values']['date']);
$toDate = clone $fromDate;
} else {
$fromDate = Carbon::parse($definition['values']['fromDate']);
$toDate = Carbon::parse($definition['values']['toDate']);
}
if (($definition['type'] == 'FixedDate' || $definition['type'] == 'RecurringDate' || $definition['type'] == 'RecurringDateTime')
&& ($date->between($fromDate, $toDate) || $fromDate->gte($date) || $fromDate->getTimestamp()==$date->getTimestamp())
) {
$ranges[] = [
'from' => $fromDate,
'to' => $toDate,
];
}
}
}
}
return $ranges;
}
}