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 <?php
namespace EP\EpProducts\Service; namespace EP\EpProducts\Service;
/*************************************************************** /***************************************************************
@@ -26,20 +27,20 @@ namespace EP\EpProducts\Service;
* This copyright notice MUST APPEAR in all copies of the script! * This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/ ***************************************************************/
use Doctrine\DBAL\DBALException; use EP\EpProducts\BpnConnect\ApiClient;
use EP\EpProducts\Domain\Model\Contingent;
use EP\EpProducts\Domain\Model\Hotel; use EP\EpProducts\Domain\Model\Hotel;
use EP\EpProducts\Domain\Repository\ContingentRepository;
use Kigkonsult\Icalcreator\Vcalendar; use Kigkonsult\Icalcreator\Vcalendar;
use League\Period\Period;
use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\SingletonInterface;
class IcalService implements 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 * @var Vcalendar
@@ -47,11 +48,11 @@ class IcalService implements SingletonInterface
protected $vcalendar; 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 public function createContingentsFeed(Hotel $hotel): string
{ {
$now = new \DateTime('now'); $calendarRows = [];
try { $hotelCode = trim((string) $hotel->getCode());
$events = $this->contingentRepository->getEvents(
$now, if ($hotelCode !== '') {
null, $dateFrom = new \DateTimeImmutable('today');
['hotel' => $hotel] $dateTo = $dateFrom->add(new \DateInterval(self::FEED_HORIZON))->sub(new \DateInterval('P1D'));
);
} try {
catch (DBALException $e) { $calendar = $this->apiClient->getCalendar($hotelCode, $dateFrom, $dateTo);
$events = []; $calendarRows = isset($calendar['data']) && is_array($calendar['data']) ? $calendar['data'] : [];
} catch (\Throwable $e) {
$calendarRows = [];
}
} }
$this->createCalendarInstance(); $this->createCalendarInstance();
$this->generateUnavailableItems($calendarRows);
$this->generateOccupancyItems($events);
$this->generateBlockedItems($events);
try { try {
$feed = $this->vcalendar->vtimezonePopulate()->createCalendar(); return $this->vcalendar->vtimezonePopulate()->createCalendar();
} } catch (\Exception $e) {
catch (\Exception $e) { return '';
$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);
} }
} }
/** /**
* @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) { $blockStart = null;
/** @var Contingent $event */ $lastDate = null;
$isBlocked = Contingent::STATUS_BLOCKED === $event->getStatus() || Contingent::STATUS_ONREQUEST === $event->getStatus();
$isUnavailable = 0 === $event->getAvailable(); foreach ($calendarRows as $row) {
if (null === $item && ($isBlocked || $isUnavailable)) { if (!isset($row['date'])) {
$item = [ continue;
'from' => $event->getBegin(), }
];
} elseif (true === is_array($item) && Contingent::STATUS_OK === $event->getStatus()) { $date = new \DateTimeImmutable($row['date']);
$item['to'] = $event->getEnd(); $lastDate = $date;
$this->addBlockedItem($item);
$item = null; 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'])) { if (null !== $blockStart && null !== $lastDate) {
$item['to'] = $event->getEnd(); $this->addBlockedItem([
$this->addBlockedItem($item); 'from' => $blockStart,
'to' => $lastDate->add(new \DateInterval('P1D')),
]);
} }
} }
@@ -157,34 +141,21 @@ class IcalService implements SingletonInterface
->newVevent() ->newVevent()
->setDtstart($item['from']) ->setDtstart($item['from'])
->setDtend($item['to']) ->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']) { $status = strtoupper(trim((string) $status));
return;
}
$summary = sprintf('Part-occupied:%d', $item['occupancy']); return in_array($status, [ 'BLOCKED', 'ON_REQUEST', 'ONREQUEST' ], true);
try {
$this->vcalendar
->newVevent()
->setDtstart($item['from'])
->setDtend($item['to'])
->setSummary($summary)
;
}
catch (\Exception $e) {
}
} }
protected function createCalendarInstance(): void protected function createCalendarInstance(): void