feat: groups price calculator admin crud, booking/offer flow and api
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect;
|
||||
|
||||
use App\BpnConnect\Exception\BpnConnectException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
abstract class AbstractApiClient
|
||||
{
|
||||
public function __construct(
|
||||
protected readonly HttpClientInterface $httpClient,
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly ?string $baseUrl = null,
|
||||
protected readonly ?string $apiKey = null,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $query
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function request(string $path, array $query = []): array
|
||||
{
|
||||
$this->assertConfigured();
|
||||
|
||||
try {
|
||||
$response = $this->httpClient->request('GET', $this->baseUrl.$path, [
|
||||
'headers' => [
|
||||
'X-API-KEY' => $this->apiKey,
|
||||
],
|
||||
'query' => $query,
|
||||
]);
|
||||
|
||||
return $response->toArray();
|
||||
} catch (ExceptionInterface $e) {
|
||||
$this->logger->error('BpnConnect request failed', [
|
||||
'path' => $path,
|
||||
'query' => $query,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
throw new BpnConnectException('BpnConnect request failed: '.$e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
private function assertConfigured(): void
|
||||
{
|
||||
if (null === $this->baseUrl || null === $this->apiKey) {
|
||||
throw new BpnConnectException('BpnConnect client is not configured (missing BPN_CONNECT_BASE_URL or BPN_CONNECT_API_KEY).');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect;
|
||||
|
||||
use App\BpnConnect\Model\ContingentCalendarEntry;
|
||||
use App\BpnConnect\Model\ContingentCalendarMeta;
|
||||
use App\BpnConnect\Model\ContingentCalendarResponse;
|
||||
use App\BpnConnect\Model\ContingentMode;
|
||||
use App\BpnConnect\Model\ContingentRangeEntry;
|
||||
use App\BpnConnect\Model\ContingentRangeResponse;
|
||||
use App\BpnConnect\Model\ContingentStatus;
|
||||
|
||||
class ContingentsClient extends AbstractApiClient
|
||||
{
|
||||
private const string CALENDAR_PATH = '/api/v1/contingents/calendar';
|
||||
|
||||
public function getContingentCalendar(
|
||||
string $hotelCode,
|
||||
string $dateFrom,
|
||||
string $dateTo,
|
||||
): ContingentCalendarResponse {
|
||||
$payload = $this->request(self::CALENDAR_PATH, [
|
||||
'hotelCode' => $hotelCode,
|
||||
'dateFrom' => $dateFrom,
|
||||
'dateTo' => $dateTo,
|
||||
'mode' => ContingentMode::Days->value,
|
||||
]);
|
||||
|
||||
$entries = array_map(
|
||||
static fn (array $item) => new ContingentCalendarEntry(
|
||||
date: $item['date'],
|
||||
status: ContingentStatus::from($item['status']),
|
||||
),
|
||||
$payload['data'] ?? [],
|
||||
);
|
||||
|
||||
return new ContingentCalendarResponse($this->buildMeta($payload), $entries);
|
||||
}
|
||||
|
||||
public function getContingentRanges(
|
||||
string $hotelCode,
|
||||
string $dateFrom,
|
||||
string $dateTo,
|
||||
): ContingentRangeResponse {
|
||||
$payload = $this->request(self::CALENDAR_PATH, [
|
||||
'hotelCode' => $hotelCode,
|
||||
'dateFrom' => $dateFrom,
|
||||
'dateTo' => $dateTo,
|
||||
'mode' => ContingentMode::Ranges->value,
|
||||
]);
|
||||
|
||||
$entries = array_map(
|
||||
static fn (array $item) => new ContingentRangeEntry(
|
||||
dateFrom: $item['date_from'],
|
||||
dateTo: $item['date_to'],
|
||||
status: ContingentStatus::from($item['status']),
|
||||
),
|
||||
$payload['data'] ?? [],
|
||||
);
|
||||
|
||||
return new ContingentRangeResponse($this->buildMeta($payload), $entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $payload
|
||||
*/
|
||||
private function buildMeta(array $payload): ContingentCalendarMeta
|
||||
{
|
||||
$raw = $payload['meta'] ?? [];
|
||||
|
||||
return new ContingentCalendarMeta(
|
||||
dateFrom: $raw['date_from'] ?? '',
|
||||
dateTo: $raw['date_to'] ?? '',
|
||||
mode: $raw['mode'] ?? '',
|
||||
hotelCode: $raw['hotel_code'] ?? '',
|
||||
hotelId: (int) ($raw['hotel_id'] ?? 0),
|
||||
rowCount: (int) ($raw['row_count'] ?? 0),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Exception;
|
||||
|
||||
class BpnConnectException extends \RuntimeException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
readonly class ContingentCalendarEntry
|
||||
{
|
||||
public function __construct(
|
||||
public string $date,
|
||||
public ContingentStatus $status,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
readonly class ContingentCalendarMeta
|
||||
{
|
||||
public function __construct(
|
||||
public string $dateFrom,
|
||||
public string $dateTo,
|
||||
public string $mode,
|
||||
public string $hotelCode,
|
||||
public int $hotelId,
|
||||
public int $rowCount,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
readonly class ContingentCalendarResponse
|
||||
{
|
||||
/**
|
||||
* @param ContingentCalendarEntry[] $data
|
||||
*/
|
||||
public function __construct(
|
||||
public ContingentCalendarMeta $meta,
|
||||
public array $data,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
enum ContingentMode: string
|
||||
{
|
||||
case Days = 'days';
|
||||
case Ranges = 'ranges';
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
readonly class ContingentRangeEntry
|
||||
{
|
||||
public function __construct(
|
||||
public string $dateFrom,
|
||||
public string $dateTo,
|
||||
public ContingentStatus $status,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
readonly class ContingentRangeResponse
|
||||
{
|
||||
/**
|
||||
* @param ContingentRangeEntry[] $data
|
||||
*/
|
||||
public function __construct(
|
||||
public ContingentCalendarMeta $meta,
|
||||
public array $data,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\BpnConnect\Model;
|
||||
|
||||
enum ContingentStatus: string
|
||||
{
|
||||
case Ok = 'OK';
|
||||
case Blocked = 'BLOCKED';
|
||||
case OnRequest = 'ON_REQUEST';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return 'enum.contingent_status.'.strtolower($this->value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user