# Ваша база

API показывает вашему отделу **только ваше**: ваши брони, ваших детей и семьи этих детей. Брони,
дети и семьи других отделов не видны и не меняются. Корпоративные брони (группы от организаций) не
видны никому через API.

Запрос к чужой записи отвечает `404 not_found` — так же, как к несуществующей. По ответу нельзя
понять, есть ли такая запись у другого отдела.

## Чей ребёнок

Ребёнка «ведёт» отдел, который оформил ему бронь **на самую позднюю по датам смену**. В расчёт идут:

- действующие брони;
- брони, снятые из-за неоплаты (место снято, но ребёнок остаётся у вас — чтобы перезвонить).

Отменённая вручную бронь ребёнка не удерживает. Борьбы за детей нет: переход между отделами
свободный, лагерь его не запрещает.

**Пример.** Тимура в прошлом году возил ваш отдел — он ваш. В этом году другой отдел оформил ему
бронь на июльскую смену, а у вас брони на этот год нет — Тимур переходит к другому отделу:

- у вас приходит событие `child.removed`, Тимур пропадает из `GET /children` и из семьи;
- **ваши прошлые брони Тимура остаются** в ваших списках и отчётах — это ваши продажи, — но
  **без контактов семьи**: в брони `contacts_hidden: true`, телефоны родителя и плательщика —
  `null`, менять их нельзя;
- у другого отдела приходит `child.added`.

Если потом вы оформите Тимуру бронь на смену позже июльской — он вернётся к вам.

## Чья семья

Семью вы видите, если у вас есть хотя бы один её ребёнок — или ваша бронь на эту семью без карточки
ребёнка. В карточке семьи ([`GET /families/{id}`](/api/семьи)) — **только ваши дети и ваши брони**:
брат или сестра, которых ведёт другой отдел, не показываются.

Один телефон — одна семья во всей системе лагеря. Основной телефон семьи (`phone`, в виде
`+7XXXXXXXXXX`) — ключ, по которому её узнают: через API он не меняется. Второй номер добавляйте в
`extra_phones`.

### Общая семья (`shared: true`)

Если в семье есть дети и вашего, и другого отдела, семья **общая**. Её общие данные вам не
показываются и не меняются — чтобы отделы не переписывали данные друг друга:

- `full_name` — как **вы** записали родителя в своей брони;
- вторых номеров (`extra_phones`) и города нет;
- `PATCH /families/{id}` с ФИО, городом, согласием на рассылки или вторыми номерами — отказ
  `403 family_shared`.

Свои `external_id` и `metadata` ставить можно всегда.

## Найти семью или ребёнка

| Нужно                                         | Запрос                                                            |
| --------------------------------------------- | ----------------------------------------------------------------- |
| Семья по телефону                             | `GET /families?phone=87010000001` — любая запись телефона, ищется по основному и вторым номерам |
| Семья по ФИО                                  | `GET /families?name=Тестов` — часть ФИО родителя или вашего ребёнка, от 2 символов |
| Семьи, изменившиеся с момента                 | `GET /families?updated_since=2027-05-14T00:00:00Z`                |
| Семья по номеру вашей CRM                     | `GET /families/by-external-id/{external_id}`                      |
| Все ваши дети                                 | `GET /children`                                                   |
| Кто был в сезоне                              | `GET /children?season_id_visited=2`                               |
| Кого не было в этом сезоне (для обзвона)      | `GET /children?season_id_not_visited=3`                           |
| Дети по году рождения                         | `GET /children?birth_year_from=2014&birth_year_to=2016`           |
| Один ребёнок                                  | `GET /children/{id}`                                              |
| Брони ребёнка или семьи                       | `GET /bookings?child_id=40871`, `GET /bookings?parent_id=22014`   |
| Бронь по номеру вашей CRM                     | `GET /bookings/by-external-id/{external_id}`                      |

Пример ответа `GET /families/22014`:

```json
{
  "id": "22014",
  "version": "2027-05-14T09:31:07.482913Z",
  "full_name": "Тестова Айгерим Болатовна",
  "phone": "+77010000001",
  "extra_phones": ["+77020000002"],
  "city": { "id": "1", "name": "Астана" },
  "marketing_opt_in": true,
  "shared": false,
  "merged_from": [],
  "children": [
    {
      "id": "40871",
      "family_id": "22014",
      "full_name": "Тестов Тимур Аскарович",
      "birth_date": "2016-04-12",
      "external_id": "child-551",
      "metadata": {}
    }
  ],
  "bookings": [],
  "external_id": "client-2207",
  "metadata": { "segment": "vip" }
}
```

В `bookings` — краткие сведения о ваших бронях детей семьи, свежие первыми (в примере пусто для
краткости).

## Изменить семью и ребёнка

Нужно право `bookings`.

**Семья** — `PATCH /families/{id}`, можно с `If-Match: <version>`:

```sh
curl -X PATCH https://partners.zvezdnyy-burabai.edu.kz/ext/v1/families/22014 \
  -H "Authorization: Bearer $ZB_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "extra_phones": ["8 702 000 00 02", "8 705 000 00 03"], "external_id": "client-2207" }'
```

| Поле               | Как                                                                          |
| ------------------ | ---------------------------------------------------------------------------- |
| `full_name`        | ФИО родителя                                                                 |
| `city_id`          | Город; `null` — стереть                                                      |
| `marketing_opt_in` | Согласие на рассылки                                                         |
| `extra_phones`     | Вторые номера — **заменяют список целиком**. Номер другой семьи не принимается (`409 phone_taken`) |
| `external_id`      | Не передано — не менять; строка — заменить; `null` — стереть                 |
| `metadata`         | Заменяет метки целиком                                                       |

Отказы: `403 family_shared` — общая семья (см. выше); `400 bad_city` — города нет в справочнике;
`412 version_mismatch` — семью изменили после вашего чтения.

**Ребёнок** — `PATCH /children/{id}`: только ваши `external_id` и `metadata`. ФИО и дата рождения
ребёнка меняются правкой брони (`PATCH /bookings/{id}`).

## Свои номера и метки

У брони, семьи и ребёнка можно хранить:

- `external_id` — номер записи в вашей CRM (до 255 знаков). Уникален внутри вашего отдела: две
  брони с одним `external_id` не бывает (`409 external_id_taken`);
- `metadata` — до 20 пар «ключ — значение». Ключ — от 1 до 40 знаков; значение — строка до 500
  знаков, число, `true`/`false` или `null`. Вложенные объекты нельзя (`400 bad_metadata`).

Лагерь их не разбирает — только хранит и возвращает. Другие отделы ваших номеров и меток не видят.

:::tip{title="Используйте external_id как защиту от дублей"}

Передавайте номер сделки вашей CRM в `external_id` при оформлении. Если ваша CRM по ошибке попробует
оформить ту же сделку второй раз (даже с другим `Idempotency-Key`), API ответит
`409 external_id_taken` и назовёт номер уже оформленной брони.

:::

## Слияние дублей

Лагерь объединяет дубли карточек — например, если одну семью завели под двумя телефонами:

- `child.merged` — две карточки одного ребёнка объединены. `data.into_id` — номер оставшейся.
  Старый номер больше не отвечает; ваши `external_id` и метки перенесены на оставшуюся (если на ней
  уже был ваш номер — остаётся он). Замените у себя старый номер на `into_id`.
- `client.merged` — то же для семьи. `data.into_id` — номер оставшейся семьи. Запрос по старому
  номеру отвечает оставшейся семьёй (её номер — в `id`, старые — в `merged_from`).

## Сверка раз в сутки

[`GET /reconcile/bookings`](/api/отчеты) — все брони вашего отдела (и отменённые): номер, статус
и версия, большими порциями (до 5000 за раз). Раз в сутки сравните со своей базой: чего у вас нет
или где `version` другая — перечитайте эти брони. Так находится всё, что разошлось, даже если
какие-то события потерялись у вас.

```json
{
  "items": [
    { "id": "15230", "status": "active", "version": "2027-05-14T10:02:45.918201Z" },
    { "id": "15231", "status": "cancelled", "version": "2027-05-15T09:40:11.003118Z" }
  ],
  "next_cursor": "c15231"
}
```

Платёж версию брони не меняет — чтобы сверить деньги, сравнивайте и платежи:
`GET /payments?updated_since=…`.
