feat: integrate with new bpn-connect api for ical feeds

This commit is contained in:
Björn Fromme
2026-06-10 14:42:49 +02:00
parent ea0f5c6b3d
commit a5b526c5a3
@@ -1,4 +1,5 @@
<?php
namespace EP\EpProducts\Service;
/***************************************************************
@@ -26,20 +27,20 @@ namespace EP\EpProducts\Service;
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use Doctrine\DBAL\DBALException;
use EP\EpProducts\Domain\Model\Contingent;
use EP\EpProducts\BpnConnect\ApiClient;
use EP\EpProducts\Domain\Model\Hotel;
use EP\EpProducts\Domain\Repository\ContingentRepository;
use Kigkonsult\Icalcreator\Vcalendar;
use League\Period\Period;
use TYPO3\CMS\Core\SingletonInterface;
class IcalService implements SingletonInterface
{
private const FEED_HORIZON = 'P1Y';
private const SUMMARY_UNAVAILABLE = 'Unavailable';
/**
* @var ContingentRepository
* @var ApiClient
*/
protected $contingentRepository;
protected $apiClient;
/**
* @var Vcalendar
@@ -47,11 +48,11 @@ class IcalService implements SingletonInterface
protected $vcalendar;
/**
* @param ContingentRepository $contingentRepository
* @param ApiClient $apiClient
*/
public function __construct(ContingentRepository $contingentRepository)
public function __construct(ApiClient $apiClient)
{
$this->contingentRepository = $contingentRepository;
$this->apiClient = $apiClient;
}
/**
@@ -60,90 +61,73 @@ class IcalService implements SingletonInterface
*/
public function createContingentsFeed(Hotel $hotel): string
{
$now = new \DateTime('now');
try {
$events = $this->contingentRepository->getEvents(
$now,
null,
['hotel' => $hotel]
);
}
catch (DBALException $e) {
$events = [];
$calendarRows = [];
$hotelCode = trim((string) $hotel->getCode());
if ($hotelCode !== '') {
$dateFrom = new \DateTimeImmutable('today');
$dateTo = $dateFrom->add(new \DateInterval(self::FEED_HORIZON))->sub(new \DateInterval('P1D'));
try {
$calendar = $this->apiClient->getCalendar($hotelCode, $dateFrom, $dateTo);
$calendarRows = isset($calendar['data']) && is_array($calendar['data']) ? $calendar['data'] : [];
} catch (\Throwable $e) {
$calendarRows = [];
}
}
$this->createCalendarInstance();
$this->generateOccupancyItems($events);
$this->generateBlockedItems($events);
$this->generateUnavailableItems($calendarRows);
try {
$feed = $this->vcalendar->vtimezonePopulate()->createCalendar();
}
catch (\Exception $e) {
$feed = '';
}
return $feed;
}
/**
* @param array $events
*/
protected function generateOccupancyItems(array $events): void
{
$occupancy = null;
foreach ($events as $event) {
/** @var Contingent $event */
if (null === $occupancy) {
$item = [
'from' => $event->getBegin(),
'occupancy' => $event->getOccupancy(),
];
} elseif ($occupancy !== $event->getOccupancy()) {
$item['to'] = $event->getEnd();
$this->addOccupancyItem($item);
$item = [
'from' => $event->getBegin(),
'occupancy' => $event->getOccupancy(),
];
}
$occupancy = $event->getOccupancy();
}
if (isset($event) && !isset($item['to'])) {
$item['to'] = $event->getEnd();
$this->addOccupancyItem($item);
return $this->vcalendar->vtimezonePopulate()->createCalendar();
} catch (\Exception $e) {
return '';
}
}
/**
* @param array $events
* @param array $calendarRows
*/
protected function generateBlockedItems(array $events): void
protected function generateUnavailableItems(array $calendarRows): void
{
$item = null;
usort($calendarRows, function (array $left, array $right): int {
return strcmp((string) ($left['date'] ?? ''), (string) ($right['date'] ?? ''));
});
foreach ($events as $event) {
/** @var Contingent $event */
$isBlocked = Contingent::STATUS_BLOCKED === $event->getStatus() || Contingent::STATUS_ONREQUEST === $event->getStatus();
$isUnavailable = 0 === $event->getAvailable();
if (null === $item && ($isBlocked || $isUnavailable)) {
$item = [
'from' => $event->getBegin(),
];
} elseif (true === is_array($item) && Contingent::STATUS_OK === $event->getStatus()) {
$item['to'] = $event->getEnd();
$this->addBlockedItem($item);
$item = null;
$blockStart = null;
$lastDate = null;
foreach ($calendarRows as $row) {
if (!isset($row['date'])) {
continue;
}
$date = new \DateTimeImmutable($row['date']);
$lastDate = $date;
if ($this->isUnavailableStatus($row['status'] ?? 'OK')) {
if (null === $blockStart) {
$blockStart = $date;
}
continue;
}
if (null !== $blockStart) {
$this->addBlockedItem([
'from' => $blockStart,
'to' => $date,
]);
$blockStart = null;
}
}
if (isset($event) && null !== $item && !isset($item['to'])) {
$item['to'] = $event->getEnd();
$this->addBlockedItem($item);
if (null !== $blockStart && null !== $lastDate) {
$this->addBlockedItem([
'from' => $blockStart,
'to' => $lastDate->add(new \DateInterval('P1D')),
]);
}
}
@@ -157,34 +141,21 @@ class IcalService implements SingletonInterface
->newVevent()
->setDtstart($item['from'])
->setDtend($item['to'])
->setSummary('Unavailable')
->setSummary(self::SUMMARY_UNAVAILABLE)
;
}
catch (\Exception $e) {
} catch (\Exception $e) {
}
}
/**
* @param array $item
* @param mixed $status
* @return bool
*/
protected function addOccupancyItem(array $item): void
protected function isUnavailableStatus($status): bool
{
if (0 === $item['occupancy']) {
return;
}
$status = strtoupper(trim((string) $status));
$summary = sprintf('Part-occupied:%d', $item['occupancy']);
try {
$this->vcalendar
->newVevent()
->setDtstart($item['from'])
->setDtend($item['to'])
->setSummary($summary)
;
}
catch (\Exception $e) {
}
return in_array($status, [ 'BLOCKED', 'ON_REQUEST', 'ONREQUEST' ], true);
}
protected function createCalendarInstance(): void