# Обмен данными между RateLoader и Fidelio V8 — форматы JSON

Документ описывает форматы JSON, используемые компонентами PriceExport (отправитель) и RateLoader (приёмник) для интеграции с Fidelio V8. Предназначен для команды Hotellab — как справочник при согласовании или модификации данных.

Источники и релевантные файлы

- [`features/price_export.py`](features/price_export.py:1) — генерация и отправка JSON (PriceExport).
- [`features/rate_loader_logic.py`](features/rate_loader_logic.py:1) — разбор и обработка входящего JSON (RateLoader).
- [`core/http_server.py`](core/http_server.py:1) — HTTP-эндпоинты (приём).
- [`docs/PRD.md`](docs/PRD.md:1) — требования к совместимости и примечания legacy.

1. Общая архитектура обмена

PriceExport извлекает JSON из Oracle через PL/SQL-функцию m_hlpricejson и POST'ит его на HTTP API RateLoader. Запрос выполняется с заголовками:

- Content-Type: application/json
- X-HL-Token: <token>

Подробнее — см. [`features/price_export.py`](features/price_export.py:1).

2. Общая структура JSON (raw array, batched)

**Updated 2025-11-27**: PriceExport sends raw JSON array directly (no "prices" wrapper), batched from multiple m_hlpricejson calls until ~1MB size limit or end of date range. This is the validated production format.

Array of price objects: [ ... ]

**Rationale**: Code implementation [`features/price_export.py`](features/price_export.py:438) uses `json.dumps(current_batch_data)` where `current_batch_data` is concatenated arrays from daily `m_hlpricejson` outputs. RateLoader parses direct array. Batching optimizes network/Oracle calls while preserving wire compatibility.

**Legacy note (superseded)**: Original spec assumed per-day {"prices": [...]}, evolved to batched raw array post-testing.

3. Описание объекта тарифа (элемент массива)

Каждый элемент массива — объект тарифа с полями:

- date (string) — дата действия тарифа в формате ISO-дат: "YYYY-MM-DD" (пример: "2024-01-01"). PRD требует именно этот формат при составлении PL/SQL (см. [`docs/PRD.md`](docs/PRD.md:1)).
- category (string) — код категории номера (пример: "STD").
- ratecode (string) — код тарифа (пример: "RACK").
- price (number) — числовая стоимость (может быть с дробной частью, пример: 100.50).
- guests (integer) — количество гостей (обычно 1 для одиночного базового тарифа). В legacy-логике при guests == 1 выполняется двойной вызов m_rlputrate (см. §6).
- children_rates (array) — массив объектов дочерних тарифов для детей; может отсутствовать или быть пустым.

Пример минимального элемента:

{
"date": "2024-01-01",
"category": "STD",
"ratecode": "RACK",
"price": 100.0,
"guests": 1,
"children_rates": []
}

4. Формат объектов children_rates

Каждый объект внутри children_rates содержит как минимум:

- price (number) — цена для ребёнка
- age_category (string) — категория/возрастная метка ребёнка (например: "CHD_0_2", "CHD_3_12") — legacy-функция m_rladdchild использует эту метку.

Пример:

{
"price": 20.0,
"age_category": "CHD_3_12"
}

5. Семантика особых правил (важно для корректной загрузки в Oracle)

- "double putrate" (double m_rlputrate): если price.guests == 1, система legacy вызывает m_rlputrate дважды — это закреплено в PRD и в коде обработки. При изменении поля guests поведение может отличаться; для суммирования/пересчёта важно учесть это правило. Источник: [`docs/PRD.md`](docs/PRD.md:1).

- Формат даты для PL/SQL: при построении PL/SQL используется to_date('{date}', 'yyyy-mm-dd') — значит строка должна быть в ISO формате "YYYY-MM-DD".

- Порядок полей не критичен (JSON-объекты неупорядочены), но отсутствие обязательных полей (date, category, ratecode, price) приведёт к ошибкам обработки.

6. Поведение RateLoader при приёме

RateLoader читает тело запроса, парсит JSON и передаёт массив в функцию обработки, которая строит PL/SQL-блоки:

- Для каждого price формируется вызов m_rlputrate(to_date(...), 'category', 'ratecode', price, guests);
- Для каждого child_rate формируется вызов m_rladdchild(..., 'age_category', child_price);
- Если guests == 1, вызывается m_rlputrate дважды (см. §5).

Сервер отвечает быстро и синхронно (старое поведение) текстовой строкой — legacy ожидает 200 и plain text "POST request for /load" (см. тесты в проекте и [`docs/PRD.md`](docs/PRD.md:1)).

7. Примеры полных JSON-пакетов

Пример 1 — пакет для одного дня, один тариф, без детей (raw array):

[
{
"date": "2024-01-01",
"category": "STD",
"ratecode": "RACK",
"price": 100.50,
"guests": 1,
"children_rates": []
}
]

Пример 2 — батч нескольких дней/тарифов (raw batched array):

[
{
"date": "2024-02-01",
"category": "FAM",
"ratecode": "FAM_PROMO",
"price": 180.0,
"guests": 2,
"children_rates": [
{"price": 20.0, "age_category": "CHD_0_2"},
{"price": 40.0, "age_category": "CHD_3_12"}
]
},
{
"date": "2024-02-01",
"category": "STD",
"ratecode": "RACK",
"price": 110.0,
"guests": 1,
"children_rates": []
},
{
"date": "2024-02-02",
"category": "DELUXE",
"ratecode": "RACK",
"price": 150.0,
"guests": 1,
"children_rates": []
}
]

8. Частые причины несоответствия и рекомендации по выравниванию данных

- Неверный формат даты — проверьте "YYYY-MM-DD" в m_hlpricejson output.

- Пустой массив [] — валиден, но проверьте Oracle function.

- Поля с null/пустыми строками — обработчик ожидает значения; numeric (price, guests) как числа.

- Различия в именах полей (e.g., "children" vs "children_rates") — используйте "children_rates".

Рекомендации:

- В PriceExport добавить валидацию перед POST: проверять наличие ключей date, category, ratecode, price, guests и принудительно приводить date к "YYYY-MM-DD".

- Логгировать полные тела запросов/ответов (Deeper logging) для отладки — в проекте уже есть поддержка сохранения запросов/ответов в папку логов (см. [`features/price_export.py`](features/price_export.py:1)).

9. Обработка ошибок и откаты

- PriceExport ведёт счётчики ошибок (oracle_errors, json_errors, http_errors, network_errors) — при разрыве сети повторять попытки или отправлять уведомление (в проекте это логируется).

- RateLoader должен валидировать вход перед формированием PL/SQL; при некорректных данных возвращать 4xx и логировать подробности. Legacy-сервер может возвращать 200 текстом, но для внутренних клиентов рекомендуется 4xx с описанием ошибки.

10. Таблица соответствия полей (mapping)

- array[0].date → PL/SQL: to_date('{date}', 'yyyy-mm-dd')
- array[0].category → PL/SQL: category
- array[0].ratecode → PL/SQL: ratecode
- array[0].price → PL/SQL: price (NUMBER)
- array[0].guests → PL/SQL: guests (NUMBER)
- array[0].children_rates[] → PL/SQL: m_rladdchild(..., age_category, price)

11. Контрольные точки при внедрении

- Убедиться, что тестовая среда Fidelio принимает date в формате "YYYY-MM-DD".
- Провести тестовую отправку одного дня с парой тарифов и пустыми children_rates.
- Сверить логи RateLoader и Oracle — убедиться, что генерируются запросы m_rlputrate и m_rladdchild.

12. Полезные ссылки (в репозитории)

- Код отправки JSON: [`features/price_export.py`](features/price_export.py:1)
- Код парсинга/обработки: [`features/rate_loader_logic.py`](features/rate_loader_logic.py:1)
- HTTP-сервер: [`core/http_server.py`](core/http_server.py:1)
- PRD и совместимость: [`docs/PRD.md`](docs/PRD.md:1)

13. Приложение: контрольный пример запроса c HTTP-заголовками

POST /load HTTP/1.1
Content-Type: application/json
X-HL-Token: <token>

<JSON-тело — см. §7>

14. Заключение

Документ даёт однозначную спецификацию для выравнивания форматов между PriceExport и RateLoader/Fidelio V8. Перед внесением изменений в PL/SQL-логику или в формат JSON необходимо согласовать поведение по "double putrate" и правилам обработки children_rates с командой интеграции Fidelio.

Автор: команда разработки RateLoader / Hotellab
