Компонент 12. Модель данных и контракты¶
Статус: ревизия №2 по итогам аудита корпуса · 10.10.2026. Числовые факты сопровождаются ссылками; канонические перечисления — в «Модели данных», §0.
Атрибутный уровень референс-модели. Документ — вход для проектирования схемы БД и открытого API. Все идентификаторы глобально уникальны (по тенанту + сущность).
0. Канонические перечисления (единый словарь платформы)¶
Этот раздел — единственный источник правды для статусов и каналов. Все компоненты и спецификации ссылаются сюда; изменение словаря делается только здесь (ревизия №1 внешнего аудита, находки F-01…F-03, F-32).
| Сущность | Поле | Канонические значения |
|---|---|---|
| Order | status |
new → accepted → kitchen → ready → served (зал) | packed → delivered (доставка) | cancelled |
| KitchenTicket | status |
queued | cooking | ready | served |
| DeliveryAssignment | status |
assigned | picked_up | delivering | delivered | failed |
| Order | channel |
pos \| site \| app \| miniapp \| kiosk \| phone \| aggregator:{name} |
| StockMovement | type |
receive \| sale_writeoff \| production_in \| production_out \| transfer \| writeoff_spoilage \| writeoff_staff \| inventory_adjust \| return |
Правила чтения и маппинг устаревших токенов:
- Два уровня статусов не смешиваются.
Order.status— жизненный цикл заказа (единый для всех каналов);KitchenTicket.status— жизненный цикл тикета станции. «Окно экспо» (готовое блюдо ждёт выдачи) — не отдельный статус, а время междуreadyиservedзаказа; если точке нужен отдельный экран экспо, он фильтрует заказы в состоянииready. - Доставка — две сущности.
Orderдоходит доpacked, дальше работаетDeliveryAssignment(assigned → picked_up → delivering → delivered); отказ моделируется парой: записьDeliveryIncident+ статусfailedу назначения. Токенcourierиз ранних ревизий не используется: «передан курьеру» = назначение вpicked_up, заказ остаётся вpackedдоdelivered. - Подтверждение вручения —
proof_photoи/илиproof_code(сценарий «фото или код» из компонента 05 поддерживается обоими полями). - Канал
phone— заказ через колл-центр (полноценный канал приёма, компонент 02); в аналитике каналов участвует наравне с остальными. - Списание по ТТК — движением
sale_writeoffпри переводе тикета вready(а не в момент пробития чека); корректировки — движениемreturn/inventory_adjust.
Обзор связей между доменами (атрибутные схемы — в разделе ниже, полные контрактные спецификации — в спецификациях):
flowchart LR
T["Tenant и договор франшизы"] --> L["Точка и устройства"]
L --> MN["Меню, цены, модификаторы"]
MN --> RC["ТТК и полуфабрикаты"]
RC --> ING["Ингредиенты и партии"]
CHN["Каналы заказов"] --> ORD["Заказ"]
MN --> ORD
ORD --> KT["Кухонный тикет"]
KT --> SM["Движение склада: списание по ТТК"]
ING --> SM
ORD --> PAY["Оплата и фискальный чек"]
ORD --> DLV["Доставка: зона, курьер, назначение"]
ORD --> GS["Гость: профиль, лояльность"]
GS --> RV["Отзыв и NPS"]
T --> TPL["Шаблон точки и роялти"]
1. Иерархия организации¶
Tenant (тенант)¶
id, name, type (owner_brand | franchisee | independent), status, created_at
FranchiseAgreement (договор франшизы)¶
id, tenant_uk, tenant_franchisee, number, date_start, date_end, territory, royalty_pct, marketing_fee_pct, lump_sum, status
Location (точка)¶
id, tenant_id, agreement_id?, name, type (dine_in | dark_kitchen | production | hybrid), address, geo, timezone, working_hours, delivery_enabled, status
Device (устройство)¶
id, location_id, kind (kkt | kds | pos_terminal | tablet | label_printer | kiosk), model, serial, kkt_reg_number?, status
2. Меню и рецептура¶
MenuItem (блюдо/товар)¶
id, location_scope | global, category_id, name, slug, description, photos, base_price, tax_group, vat, is_alcohol, is_marked, unit, status, energy_value (БЖУ), allergens[]
PriceRule (цены по каналам/точкам)¶
id, menu_item_id, channel | location | zone, price, valid_from/to
ModifierGroup / Modifier (модификаторы)¶
group: id, name, type (single | multi | required), min, max
modifier: id, group_id, name, price_delta, qty_effect (влияние на расход)
Recipe (ТТК)¶
id, menu_item_id, version, approved_by, approved_at, portion_weight, prep_time_norm_sec, station_ids[], photo_plating, instructions_md, status
RecipeLine (строка ТТК)¶
id, recipe_id, ingredient_id | recipe_id (полуфабрикат), qty, unit, loss_cold_pct, loss_hot_pct, substitutions[]
Ingredient (ингредиент)¶
id, name, unit, category, default_shelf_life, is_tracked_lots, barcode?
3. Заказы и платежи¶
Order¶
id, location_id, channel (pos | site | app | miniapp | kiosk | phone | aggregator:{name}), guest_id?, table_id?, delivery? (address, zone_id, slot), status (new → accepted → kitchen → ready → served | packed → delivered | cancelled), created_at, promised_at, total, discount_total, source_meta (aggregator_order_id?, commission_pct?)
Перечисления
channelиstatus— канонические (§0); контракт создания заказа — в спецификации 03, источник гостя — объектguest {phone, consent}, согласие фиксируется по 152-ФЗ.
OrderItem¶
id, order_id, menu_item_id, qty, modifiers[], price, comment_to_kitchen, station_routing, course_no, status
Payment¶
id, order_id, method (cash | card | sbp_qr | bonus | deposit), amount, rrn?, status, fiscal_ref
FiscalReceipt (фискальный чек)¶
id, order_id, kkt_id, fn_number, fiscal_document_no, fiscal_sign, ffd_version, ofd_status (sent | pending | error), receipt_type (sell | refund | correction), items[] (name, qty, price, vat, marking_code?, excise?), created_at
StopList¶
id, location_id, menu_item_id | recipe_id, reason, started_by, valid_until
4. Склад и закупки¶
Lot (партия)¶
id, ingredient_id, location_id, supplier_id, qty_received, price_unit, received_at, expires_at?, marking_codes?
StockMovement (движение — единственный источник остатков)¶
id, location_id, ingredient_id | recipe_id, lot_id?, type (receive | sale_writeoff | production_in | production_out | transfer | writeoff_spoilage | writeoff_staff | inventory_adjust | return), qty, doc_ref, author_id, device_id?, created_at
Остаток = агрегация движений; каждое движение привязано к документу и автору.
PurchaseOrder / PurchaseOrderLine¶
po: id, location_id, supplier_id, status (draft → confirmed → shipped → received | rejected), expected_at, edo_doc_id?
line: id, po_id, ingredient_id, qty, price_expected, price_fact?, qty_fact?
Supplier¶
id, name, legal, categories[], delivery_days[], payment_terms, price_list_ref, status
5. Производство и кухня¶
KitchenTicket (тикет станции)¶
id, order_id, station_id, items[], status (queued | cooking | ready | served), created_at, started_at?, ready_at?
Норматив времени берётся из Recipe; факт — из статусов. Перевод в
readyтриггерит списание по ТТК (§0, правило 5).
ProductionBatch (акт производства)¶
id, location_id, recipe_id, qty_out, consumed[] (ссылки на StockMovement), yield_pct, author_id, created_at, label_printed
6. Доставка¶
DeliveryZone¶
id, location_id, polygon | radius, min_order, delivery_fee, max_promise_min, channels[]
Courier¶
id, tenant_id, employment (staff | self_employed), transport (foot | bike | car), status, rating
DeliveryAssignment¶
id, order_id, courier_id, status (assigned | picked_up | delivering | delivered | failed), assigned_at, picked_up_at, delivered_at, proof_photo?, proof_code?, failure_reason?
Статусы — канонический словарь §0 (токен
pickedранних ревизий заменён наpicked_up); отказ — параDeliveryIncident+failed.
DeliveryIncident¶
id, assignment_id, type (no_answer | wrong_address | accident | refusal), resolution, resolved_by
7. Гости и лояльность¶
Guest¶
id, phones[], email?, name, birthday?, wifi_auth?, sources[], consent_152fz_at, created_at
LoyaltyAccount¶
id, guest_id, program_id, balance, level, card_token (wallet)
LoyaltyTx¶
id, account_id, order_id?, type (earn | redeem | expire | adjust), amount, reason
Campaign / CampaignEvent¶
campaign: id, name, segment_rule (RFM/ручной), channel, offer_rule, status
event: id, campaign_id, guest_id, sent_at, opened_at?, redeemed_order_id?
Review¶
id, order_id? | visit_id?, guest_id?, rating (1..5), text, source (post_order_sms | qr_check | app | aggregator | map), tags[] (блюдо/курьер/смена — из связей заказа), sentiment?, response?, sla_deadline?
8. Персонал¶
Employee¶
id, tenant_id, location_ids[], role, rate_type (hour | shift | pct), rate_value, medbook_expires_at?, pin | card_uid, status
Shift (план) / TimesheetEntry (факт)¶
shift: id, employee_id, location_id, planned_start/end, position
timesheet: id, employee_id, fact_start/end, source (card | pin | kkt_session)
9. Франшиза и контроль¶
LocationTemplate (шаблон точки)¶
id, uk_tenant_id, version, menu_ref, recipe_pack_ref, price_policy, rights_matrix, checklist_pack_ref, norms_ref, status
RoyaltyInvoice¶
id, agreement_id, period, gross_revenue (из FiscalReceipt), royalty_amount, marketing_fee, penalties, status, edo_doc_id?
Checklist / Audit / AuditAnswer / RemediationTask¶
checklist: id, uk_tenant_id, version, items[] (question, weight, require_photo)
audit: id, checklist_id, location_id, auditor_id, scheduled | adhoc, started_at, finished_at, score
answer: id, audit_id, item_id, result, photo?, geo?, comment?
task: id, audit_id, item_id, assignee_id, deadline, status
10. Событийный слой и интеграционные контракты¶
Все сущности выше порождают события в шине. Ключевые типы:
order.created / order.status_changed / payment.completed / fiscal.receipt_sent / kitchen.ticket_status / stock.movement / delivery.status_changed / review.created / audit.finished / franchise.invoice_created
Вебхуки наружу¶
{
"event": "order.status_changed",
"tenant": "...", "location": "...",
"object_id": "ord_...", "version": 12,
"data": { "status": "ready", "at": "..." }
}
Контракт заказа внутрь (для всех каналов)¶
POST /v1/orders: location_id, channel, items[{menu_item_id, qty, modifiers[]}], guest?{phone, consent}, delivery?{address, zone_id, slot}, payment_intent?, idempotency_key
Ответ: order_id, promise_time, status_url. Полная контрактная форма с примером — в ../specs/03-api-contracts.md (источник контракта — спецификация 03; источник модели — этот документ).
Контракт меню наружу (витрины/агрегаторы)¶
GET /v1/menu?location= → категории, позиции с модификаторами, цены по каналам, стоп-лист, доступность.
11. Принципы целостности¶
- Любой остаток и любой отчёт выводятся из событий — нет «ручных остатков».
- Фискальные данные — источник выручки для роялти; других источников нет.
- Отзыв всегда ссылается на заказ/визит → на смену, станцию, курьера.
- Версии: рецепт, шаблон точки, чек-лист — версионируемые объекты с раскатом и откатом.
- ПДн гостей: согласия, хранение в РФ, выгрузка/удаление по запросу (152-ФЗ).