Атлас данных: АСБ ↔ Профкурорт

Полная карта интеграции с вендорским API (АСБ / ihmanagement): методы, поток каждого поля, схемы БД, эндпоинты и логика проверок.

live-вызов АСБ из фон-синка (локальная БД) проверка / вычисление снимок кода на 14.07.2026 · прод
Обзор потока Методы АСБ Данные: БД ← АСБ Путь ключевых полей Эндпоинты Логика проверок Фронт
14 методов АСБ — кликни, чтобы подсветить этапы, где используется
objects objtyps services catprops programms categs prices quotes calcsum setletter url_let_docs letters cancel_letter check_price_by_object
Наведи или кликни метод.
① Фоновый синк (sync_catalog) — периодически, наполняет локальную БД
objectsobjtypsservicescatprops → по каждому объекту → programmscategspricesquotes
↓ normalize_* + materialize (overrides) →
Property · child_age · hour_in/out Room · capacity · dop_capacity Program · concod Quote · concod · qty Price
② Путь пользователя — рантайм (клик по этапу раскрывает детали)

1 Поиск / каталог

Объекты из локальной БД + фильтры. Квота НЕ сверяется.
БД: Propertyфильтры: гео·профили·теги·цена
Только материализованная витрина. Живых вызовов АСБ нет. price_from — из синка.

2 Страница объекта

Фонд + наличие на даты + программы + child_age.
Room · Programналичие: Quote (room,date)child_age отдаётся
GET /properties/{id}?in&out. quota_known = есть ли записи Quote у номера; available = qty>0 на все ночи. child_age/child_start_age в ответе.

3 Выбор номера

Ёмкость + наличие номера.
взрослых ≤ capacity, всего ≤ capacity+dopналичие: Quote
capacity (осн. койки) + dop_capacity (доп. места — только детям). soldOut/unverified/fits в RoomCard.

4 Выбор программы

Цена + наличие по договору + возраст.
calcsumavailability по concodвозраст
POST /properties/{id}/price: options (calcsum live) + availability{progid} (Quote по concod). ineligibility гасит по возрасту/сроку.

5 Форма гостей

Валидация возраста и ёмкости.
возраст на заезд vs child_age + заявленныйёмкость
ageOn → guestAgeError; BirthdayPicker ограничен birthdayBounds; allValid блокирует кнопку.

6 Бронирование

Live-проверки + регистрация в АСБ.
validate: ёмкость+возрастprogrammsquotes(concod)setletterurl_let_docs
BookingService.create: validate_booking → programms → guard servcods → find_quota_shortage (live quotes) → setletter → parse (humanize) → save Order.
AsbClient — base_url = ASB_BASE_URL + «/» · auth = httpx.DigestAuth · timeout 90s
методHTTPendpointаргументыпередачаключевые поля ответа
calcsumPOSTcalcsumjs_dataform data={"js_data": json}заказ с заполненным price (итог + по клиентам)
setletterPOSTsetletterjs_dataform data={"js_data": json}ref (строкой), mes, price, paysysurl, paysysid
lettersGETlettersletcodquery {letcod}объект заказа (list→data[0] или None)
cancel_letterDELETElettersletcodquery {js_data: {"letcod":N}}{mes, ref} (ref 1=успех)
url_let_docsGETurl_let_docsletcodquery {letcod}⚠ голая JSON-строка URL (или {ref})
objectsGETobjectsобъекты (objid, objnam, objstars, hourin/out, child_age, geoobject…)
objtypsGETobjtypsobjtypid → objtypnam
servicesGETservicesservcod → servnam
catpropsGETcatpropscatpropid → catpropnam
categsGETcategsobjidquery {objid}catcod, catnam, catbednum, catdopnum, catpropids, catgalery
programmsGETprogrammsobjidquery {objid}progid, pricelistid, prognam, concod, servcods, minlength/maxlength, datein/dateout, discountprc
pricesGETpricesobjid, pricelistid, length=400querycatcod, pricelistid, datein-интервалы, price
quotesGETquotesobjid, length=400, catcod?query (catcod — если задан)catcod, resdate, concod, quoteval
check_price_by_objectGETcheck_price_by_objectobjidquery {objid}метка изменения цен (инкрементальный синк)
⚠ Грабли: cancel_letter — js_data в query-string при DELETE (иначе «wrong parametr js_data!»). url_let_docs — ответ приходит голой строкой URL, а не объектом. setletter.ref — строкой → int(float(ref)).

js_data для setletterbuild_setletter_jsdata()

{
  // верхний уровень
  "letcod": 0,                          // константа при регистрации
  "datein": "YYYY-MM-DD HH:MM:00",      // date_in + hour_in (объекта)
  "dateout": "YYYY-MM-DD HH:MM:00",     // date_out + hour_out ИЛИ hour_in
  "pricelistid": <progid 1-го гостя>,    // программа заказа
  "queryfl": 0,                         // 0 = бронь с проверкой квоты
  "price": 0,
  "rooms": [ {
    "catcod": <int>, "catplacefl": 0,
    "clients": [ {
      "clientid": 0,
      "pricelistid": <progid гостя>,     // путёвка каждого гостя
      "clinam1": фамилия, "clinam2": имя, "clinam3": отчество,
      "datebirthday": "YYYY-MM-DD",
      "sexcod": 1|2,                    // 1-М, 2-Ж
      "agecod": 1|2,                    // 1-взрослый, 2-ребёнок (по флагу child)
      "dopplacefl": 0|1,                // 1 если idx >= capacity (доп. место)
      "countintcod": 643,               // COUNTRY_RF (Россия)
      "enablefl": true, "price": 0,
      "heal":     [{"servcod": servcods[0]}],   // лечение (если есть)
      "mealType": [{"servcod": servcods[1]}]    // питание (если есть)
    } ]
  } ],
  "payer": { "lastname","firstname","middlename","email","cellphone" }  // cellphone ← phone
}
parse_setletter_response читает: ref→letcod (int(float), ≤0 → BookingError), mes→letregnr, price→int, paysysurl, paysysid. servcods берутся из programms по progid, порядок [лечение, питание].

Каждая таблица витрины (локальная БД) и откуда берётся каждое поле в АСБ. Прочерк «—» = поле не из АСБ (вычисляется/локальное).

Property ← objects · normalize_property

колонкатиписточник АСБ
idstr PKobjid
namestrobjnam
typestrobjtypid → objtyps.objtypnam
city / region / countrystrgeoobject.* (canon_place, derive_country)
starsintobjstars
descriptiontextobjnot
phonestrobjphone
hour_in / hour_outstrhourin / hourout
child_start_ageint?child_start_age
child_ageint?child_age
lat / lonfloat?geoobject.Latitude/Longitude
price_fromint?— (min по Price, синк отдельно)
city_idFK— (resolve_city_id)
Локальные (синк НЕ трогает): rating, reviews, rating_criteria, memo, how_to_get, prices_info, children_note, capacity (строка «450 мест»), canonical_path, связки treatment_profiles/amenities/media.

Room ← categs · normalize_room

колонкатиписточник АСБ
idstr PK{objid}:{catcod}
catcodintcatcod
namestrcatnam
capacityint?catbednum (осн. койки)
dop_capacityintcatdopnum (доп. места)
bedstr— (1-й feature «кроват/диван»)
featuresjsoncatpropids → catprops.catpropnam
photosjsoncatgalery (ind из href)
descriptiontextcatnot
price_fromint?— (из Price, синк отдельно)
Локальное: area.

Program ← programms · normalize_program

колонкатиписточник АСБ
idstr PK{objid}:{progid}
progidintprogid
pricelist_idintpricelistid
namestrprognam
concodstrconcod (договор)
descriptiontextprognot
min_nights / max_nightsint?minlength / maxlength
date_in / date_outstrdatein / dateout
discountjsondiscountprc (только >0)
includedjsonservcods → services.servnam
is_unionbool— (regex «профсоюзн» в prognam)
Локальные: min_nights_adult, min_nights_child, note, url_segment.

Quote ← quotes · catalog_sync (агрегация по catcod+resdate+concod)

колонкатиписточник АСБ
room_idstr{objid}:{catcod}
datedateresdate
concodstrconcod (договор)
qtyintΣ quoteval по ключу
Уникальный ключ: (room_id, date, concod). До фикса агрегировалось без concod — отсюда была «Quota is empty».

Price ← prices · expand_price_intervals

колонкаисточник
room_id{objid}:{catcod}
pricelist_idpricelistid
dateпо-дневно из datein-интервала
priceprice (заглушка 77777 исключается)

Order ← запрос BookingCreate + ответ setletter

колонкаисточник
tokenuuid4().hex
letcod / letregnr / price / paysys_urlответ setletter (parse)
objid / catcod / date_in / date_outзапрос (body)
payer / guestsзапрос (снапшот json)
docs_urlurl_let_docs (при оплате)
status / paid_at«booked»→«paid»
Путь ключевого поля: сырой АСБ → колонка БД → API-ответ → фронт. Кликни строку.
полеполный путь данных
Ёмкость (осн.)
catbednumRoom.capacityRoomItem.capacityRoomCard.fits / validate_booking
Доп. места
catdopnumRoom.dop_capacityRoomItem.dop_capacityfits (только детям)
Договор (квота)
concod (programms + quotes)Program.concod · Quote.concodavailability{progid}RoomGuestPrograms.avail
Квота (места)
quotevalQuote.qty (по date,concod)available / quota_known · availabilitysoldOut · программа гаснет
Возраст ребёнка
child_age / child_start_ageProperty.child_agePropertyDetail.child_ageBookingFlow.childAge → guestAgeError
Время заезда/выезда
hourin / houroutProperty.hour_in/out(в create)js_data datein/dateout
Программа (путёвка)
progid · servcodsProgram.progid · includedprogram_prices · optionsRoomGuestPrograms picked → js_data pricelistid+heal/mealType
Цена
calcsum (live)PriceResponse.total/optionsRoomGuestPrograms итог · BookingFlow price
Кликни строку, чтобы увидеть, что происходит на каждом шаге.

Эндпоинты букинга и цены

метод + путьтело запросаответошибки
POST /bookingsBookingCreate: objid, dateIn, dateOut, rooms[], payerBookingCreated: token, price, letregnr, payUrl422 {code,message}; 404 not_found
GET /bookings/{token}BookingStatus: status, price, letregnr, payUrl, docsUrl404 order_not_found
POST /bookings/{token}/payBookingStatus (status→paid)404 stub_disabled / not_found
POST /properties/{id}/pricePriceRequest: catcod, datein, dateout, guests[]PriceResponse: total, guests, options, availability404 room_not_found
GET /properties/{id}?in&outPropertyDetail + RoomItem.available/quota_known404 property_not_found

Схемы DTO (поля)

схемаполя
GuestInlastname, firstname, middlename="", birthday, sex(1|2), child=false, age?, progid
RoomIncatcod, guests[GuestIn] (min 1)
PayerInlastname, firstname, middlename="", phone, email
BookingCreateobjid, dateIn→date_in, dateOut→date_out, rooms[RoomIn], payer
PriceRequestcatcod, datein, dateout, guests[{child, age, progid?}]
PriceResponsetotal, guests[{progid, price}], options{gi:{progid:price}}, availability{progid:bool}
RoomItem… capacity, dop_capacity, program_prices{progid:price}, available, quota_known
BFF-прокси (Next): /api/booking/create|status|pay/api/v1/bookings*; /api/booking/price достаёт objid из тела → /api/v1/properties/{objid}/price. Наличие/цена в списке: fetchRoomAvailabilityClientGET /properties/{id} (rooms[].available/quota_known); fetchRoomsPriceClient/properties/{id}/rooms-price.

BookingService.create() — порядок (всё ДО и вокруг setletter)

#шагчто делает / ошибка
1Lookup Propertyнет → ValueError property_not_found (404)
2cap/dop/name по Roomcatcod не найден → ValueError room_not_found
3validate_bookingёмкость + возраст (ниже) → BookingError 422
4programms (live)servcods_by_progid + concod по каждой программе
5guard servcodsprogid без servcods → «Выбранная путёвка недоступна…»
6quotes (live) + find_quota_shortageнет мест по (catcod,ночи,concod) → «…нет свободных мест»
7build js_datarooms[].clients[] (см. Методы АСБ)
8setletter (live)регистрация; ref<0 = ошибка
9parse + humanizeref≤0 → BookingError (человеческий текст, raw в лог)
10save Ordertoken, status=booked, commit

Ёмкость validate_booking · доп. места только детям

cap = capacity or 0 ; dop = dop_capacity or 0 ; adults = гости без child
если cap > 0:
  len(adults) > cap  → «В номер «{name}» помещается не более {cap} взросл.
                        (доп. места — только для детей)…»
  len(guests) > cap + dop → «В номер «{name}» помещается до {cap+dop}
                        гостей ({cap} осн. + {dop} доп.).»
если cap == 0 → ёмкость НЕ проверяется (вместимость АСБ не задана)

Возраст validate_booking + фронт guestAgeError · на дату заезда

age = полных лет от birthday до date_in (_age_on)
age < 0                          → «Дата рождения не может быть позже даты заезда»
child & age != заявленный(g.age) → «Возраст ребёнка на заезд — {age}, а указан {claimed}…»
child & age > child_age          → «…уже не ребёнок (до {child_age} лет). Оформите взрослым»
взрослый & age <= child_age      → «…детский возраст (до {child_age}). Оформите ребёнком»
child_age берётся из Property (из АСБ). Фронт ещё и ограничивает пикер (birthdayBounds).

Квота find_quota_shortage · по договору concod

avail[(catcod, date, concod)] = Σ quoteval
nights = [date_in .. date_out-1]
для каждой пары (catcod, concod) заказа с числом гостей count:
  если для любой ночи  avail[(catcod, ночь, concod)] < count  → нехватка
  → «На выбранные даты в номере «{room}» по программе «{prog}» нет свободных мест»
availability в /price: то же, но per-progid; нет данных по concod → true («по запросу»)

Человеческие ошибки АСБ humanize_asb_error(mes)

подстрока в mesпоказываем пользователю
quota is emptyНа выбранные даты нет свободных мест…
not available forПрограмма недоступна к бронированию на выбранные даты…
maximum/minimum number of daysНе подходит / слишком короткий срок — измените даты
not enough moneyБронь временно недоступна по техническим причинам…
wrong parametr · check-in/out time · js_dataТехническая ошибка, мы уже разбираемся
прочее (fallback)Не удалось оформить на выбранных условиях…
Сырой английский текст АСБ уходит в лог booking.rejected, пользователю — только человеческий. Полного справочника кодов у АСБ нет.

Компонент → какие поля читает → что делает

компонентполяназначение
BookingBlockfilters.rooms[], RoomItem, avail(available/quota_known), roomPricesстроит roomGroups; клиентские fetch наличия+цены; накладывает avail на фонд
RoomsList / RoomCardcapacity, dop_capacity, available, quota_known, program_prices, price_fromsoldOut/unverified; fits (ёмкость); собирает booking_draft → /booking
RoomGuestProgramsprogram_prices, options, availability{progid}, min/max_nights, date_in/out, nameцена (calcsum); гасит по наличию (concod), возрасту (ADULT_AGE=35, ageRange из названия), сроку
BookingFlowdraft, forms, property.childAge, catNames, progNamesageOn/guestAgeError (возраст на заезд); allValid; submit → /api/booking/create; поллинг статуса
BirthdayPickerminDate, maxDateограничивает выбор даты рождения под возраст гостя
booking/page.tsxprop.child_age, child_start_age, rooms, programsфетчит /properties/{id}; прокидывает childAge, catNames, progNames
Черновик брони (мультикомната) хранится в sessionStorage.booking_draft; введённые ФИО — в booking_form; токены заказов — в localStorage.my_bookings. Payload create: rooms[].guests[] с lastname/firstname/middlename/birthday/sex/child/age/progid + payer.