feat: integrate with new bpn-connect api for groups swiss feed

This commit is contained in:
Björn Fromme
2026-06-10 14:42:49 +02:00
parent 960aff0e40
commit 9b9eeff871
@@ -26,10 +26,7 @@ namespace EP\EpProducts\Service;
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use Doctrine\DBAL\Exception as DBALException;
use EP\EpProducts\Domain\Model\Contingent;
use EP\EpProducts\Domain\Model\Hotel;
use EP\EpProducts\Domain\Repository\ContingentRepository;
use EP\EpProducts\BpnConnect\ApiClient;
use EP\EpProducts\Domain\Repository\HotelRepository;
use League\Period\Period;
use Psr\Log\LoggerAwareInterface;
@@ -48,9 +45,9 @@ class GroupsSwissService implements SingletonInterface, LoggerAwareInterface
protected $hotelRepository;
/**
* @var ContingentRepository
* @var ApiClient
*/
protected $contingentRepository;
protected $apiClient;
/**
* @var HttpClient
@@ -59,12 +56,12 @@ class GroupsSwissService implements SingletonInterface, LoggerAwareInterface
/**
* @param HotelRepository $hotelRepository
* @param ContingentRepository $contingentRepository
* @param ApiClient $apiClient
*/
public function __construct(HotelRepository $hotelRepository, ContingentRepository $contingentRepository)
public function __construct(HotelRepository $hotelRepository, ApiClient $apiClient)
{
$this->hotelRepository = $hotelRepository;
$this->contingentRepository = $contingentRepository;
$this->apiClient = $apiClient;
$this->httpClient = HttpClient::create();
}
@@ -79,27 +76,31 @@ class GroupsSwissService implements SingletonInterface, LoggerAwareInterface
return false;
}
try {
$events = $this->contingentRepository->getEvents(
\DateTime::createFromImmutable($range->getStartDate()),
\DateTime::createFromImmutable($range->getEndDate()),
['hotel' => $hotel]
);
$hotelCode = trim((string) $hotel->getCode());
if ('' === $hotelCode) {
$this->logger->error(sprintf('Hotel with uid %d has no hotel code', $hotelUid));
return false;
}
catch (DBALException $e) {
$this->logger->error(sprintf('No dates found for hotel with uid %d', $hotelUid));
try {
$calendar = $this->apiClient->getCalendar(
$hotelCode,
\DateTime::createFromImmutable($range->getStartDate()),
\DateTime::createFromImmutable($range->getEndDate()->sub(new \DateInterval('P1D')))
);
$calendarRows = isset($calendar['data']) && is_array($calendar['data']) ? $calendar['data'] : [];
} catch (\Throwable $e) {
$this->logger->error(sprintf('No dates found for hotel with uid %d', $hotelUid), ['exception' => $e]);
return false;
}
$this->resetChart($range, $apiKey, $houseNumber);
if (count($events) > 0) {
$this->generateChart(
$events,
$calendarRows,
$apiKey,
$houseNumber
);
}
return true;
}
@@ -120,78 +121,72 @@ class GroupsSwissService implements SingletonInterface, LoggerAwareInterface
}
/**
* @param array $contingents
* @param array $calendarRows
* @param string $apiKey
* @param int $houseNumber
*/
protected function generateChart(array $contingents, $apiKey, $houseNumber): void
protected function generateChart(array $calendarRows, $apiKey, $houseNumber): void
{
usort($calendarRows, function (array $left, array $right): int {
return strcmp((string) ($left['date'] ?? ''), (string) ($right['date'] ?? ''));
});
$item = null;
$itemStatus = null;
$lastDate = null;
// Iterate over all contingents
foreach ($contingents as $contingent) {
/** @var Contingent $contingent */
// Get actual or 'virtual' status of current contingent
$contingentStatus = $this->getContingentStatus($contingent);
foreach ($calendarRows as $row) {
if (!isset($row['date'])) {
continue;
}
if (null === $item && Contingent::STATUS_OK !== $contingentStatus) {
// Make contingent status the current status
$itemStatus = $contingentStatus;
// Create fresh chart item
$item = [
'from' => $contingent->getBegin(),
'status' => $contingentStatus,
];
} elseif (null !== $item && $itemStatus !== $contingentStatus) {
// Set end date of current chart item to previous day when following
// status is OK
$item['to'] = Contingent::STATUS_OK !== $contingentStatus ?
$contingent->getEnd() : $contingent->getEnd()->modify('-1 day');
// Translate status code for Groups.swiss
$statusCode = Contingent::STATUS_ONREQUEST === $item['status'] ? 1 : 2;
// Post chart item to API
$this->addItem($item, $statusCode, $apiKey, $houseNumber);
// Make contingent status the current status
$itemStatus = $contingentStatus;
// Reset chart item or create fresh one depending on new status
if (Contingent::STATUS_OK === $contingentStatus) {
$currentDate = new \DateTimeImmutable($row['date']);
$currentStatus = $this->normalizeStatus((string) ($row['status'] ?? 'OK'));
if (null !== $lastDate && $currentDate > $lastDate->add(new \DateInterval('P1D'))) {
if (null !== $item) {
$this->addItem($item, $this->translateStatusToGroupsSwissState($itemStatus), $apiKey, $houseNumber);
$item = null;
} else {
$itemStatus = null;
}
}
if ('OK' === $currentStatus) {
if (null !== $item) {
$item['to'] = $lastDate;
$this->addItem($item, $this->translateStatusToGroupsSwissState($itemStatus), $apiKey, $houseNumber);
$item = null;
$itemStatus = null;
}
$lastDate = $currentDate;
continue;
}
if (null === $item) {
$item = [
'from' => $contingent->getBegin(),
'status' => $contingentStatus,
'from' => $currentDate,
'to' => $currentDate,
];
}
}
}
$itemStatus = $currentStatus;
} elseif ($itemStatus !== $currentStatus) {
$item['to'] = $lastDate;
$this->addItem($item, $this->translateStatusToGroupsSwissState($itemStatus), $apiKey, $houseNumber);
$item = [
'from' => $currentDate,
'to' => $currentDate,
];
$itemStatus = $currentStatus;
} else {
$item['to'] = $currentDate;
}
/**
* @param Contingent $contingent
* @return int
*/
protected function getContingentStatus(Contingent $contingent): int
{
$contingentStatus = $contingent->getStatus();
// All status other than OK are to be applied immediately
if (Contingent::STATUS_BLOCKED === $contingentStatus || Contingent::STATUS_ONREQUEST === $contingentStatus) {
return $contingentStatus;
$lastDate = $currentDate;
}
// Determine 'virtual' status depending on availability
$percentageAvailable = $contingent->getPercentageAvailable();
if ($percentageAvailable <= 20) {
return Contingent::STATUS_BLOCKED;
if (null !== $item) {
$item['to'] = $lastDate;
$this->addItem($item, $this->translateStatusToGroupsSwissState($itemStatus), $apiKey, $houseNumber);
}
if ($percentageAvailable <= 80) {
return $contingent::STATUS_ONREQUEST;
}
return Contingent::STATUS_OK;
}
/**
@@ -232,4 +227,41 @@ class GroupsSwissService implements SingletonInterface, LoggerAwareInterface
$this->logger->error(sprintf('A problem occured: %s', $e->getMessage()));
}
}
/**
* @param string $status
* @return string
*/
protected function normalizeStatus(string $status): string
{
$status = strtoupper(trim($status));
if ('ONREQUEST' === $status) {
return 'ON_REQUEST';
}
if (in_array($status, [ 'OK', 'ON_REQUEST', 'BLOCKED' ], true)) {
return $status;
}
return 'OK';
}
/**
* @param string $status
* @return int
*/
protected function translateStatusToGroupsSwissState(string $status): int
{
$status = $this->normalizeStatus($status);
if ('ON_REQUEST' === $status) {
return 1;
}
if ('BLOCKED' === $status) {
return 2;
}
return 0;
}
}