Order Service — Events

(Добавлено в BR 2.1)

Публикует

order.created

Публикуется при создании нового заказа.

Topic: order.created

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "order_number": "string",
    "order_type": "takeaway",
    "status": "new",
    "created_by": "uuid"
  }
}

order.paid

Публикуется при фиксации оплаты заказа (paid_at проставлен). Не переводит в ready — изменено в BR 2.5; статус на момент события = текущий (ready / delivered).

Topic: order.paid

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "customer_id": "uuid | null",
    "order_number": "string",
    "total": "decimal",
    "payment_method": "cash | card | qr | mixed",
    "paid_amount": "decimal",
    "status": "ready | delivered"
  }
}

Консьюмеры:

  • Warehouse Service — возможно перенесён с order.completed на order.paid для более точного списания (см. открытый вопрос BR 2.5 §10.1)
  • Webhook dispatcher (Aggregator Service) — пересылает подписанным внешним POS (KOALa и т.п.)

order.cooking_started

(Добавлено в BR 2.5)

Публикуется при переходе new → accepted (кассир/официант нажал «Начать готовку»).

Topic: order.cooking_started

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "order_number": "string",
    "accepted_by": "uuid",
    "accepted_at": "datetime"
  }
}

order.ready

(Добавлено в BR 2.5; в BR 5.1 добавлен автотриггер от KDS)

Публикуется при переходе accepted → ready. Триггеры:

  1. Кассир тапнул «Готово» на POS (POST /orders/{id}/mark-ready)
  2. Автопереход — все order_items.kitchen_status = 'ready' (закрытие через KDS) — (BR 5.1)

Topic: order.ready

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "order_number": "string",
    "ready_at": "datetime",
    "trigger": "manual" | "kds_auto"
  }
}

order.item.kitchen_status_changed

(Добавлено в BR 5.1)

Публикуется при изменении kitchen_status позиции через KDS-endpoints:

  • PATCH /orders/{id}/items/{itemId}/kitchen-status — одна позиция
  • PATCH /orders/{id}/kitchen-status?station_id=... — все позиции одной станции (per item публикуется отдельным событием)

Consumers:

  • pos-bff — broadcast в WebSocket подписчикам KDS (фильтр по kitchen_station_id ∈ session.selected_station_ids)
  • (P2) analytics-service — метрики времени готовки на станцию

Topic: order.item.kitchen_status_changed

{
  "event_id": "uuid",
  "event_type": "order.item.kitchen_status_changed",
  "timestamp": "datetime",
  "version": 1,
  "source": "order-service",
  "payload": {
    "order_id": "uuid",
    "item_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "kitchen_station_id": "uuid",
    "product_id": "uuid",
    "previous_status": "pending" | "preparing" | "ready",
    "new_status": "preparing" | "ready",
    "changed_by_user_id": "uuid",
    "changed_at": "datetime"
  }
}

order.closed

(Добавлено в BR 2.5)

Публикуется при переходе → closed.

Alias order.completed

Событие order.completed продолжает публиковаться параллельно с order.closed для обратной совместимости с Customer Service. Оба события имеют одинаковый payload.

Topic: order.closed

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "customer_id": "uuid | null",
    "order_number": "string",
    "total": "decimal",
    "status": "closed"
  }
}

order.handed_over

(Добавлено в BR 2.5)

Публикуется при переходе ready → handed_over (заказ передан курьеру).

Topic: order.handed_over

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "order_number": "string",
    "courier_id": "uuid",
    "handed_over_at": "datetime"
  }
}

order.in_delivery

(Добавлено в BR 2.5)

Публикуется при переходе handed_over → in_delivery.

Topic: order.in_delivery

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "order_number": "string",
    "courier_id": "uuid",
    "in_delivery_at": "datetime"
  }
}

order.delivered

(Добавлено в BR 2.5)

Публикуется при переходе in_delivery → delivered.

Topic: order.delivered

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "customer_id": "uuid | null",
    "order_number": "string",
    "courier_id": "uuid",
    "delivered_at": "datetime"
  }
}

order.completed

Публикуется при завершении заказа (выдан клиенту).

Topic: order.completed

(Payload обогащён customer_id в BR 3.1 — используется Customer Service для точечного пересчёта dynamic групп)

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "order_number": "string",
    "total": "decimal",
    "status": "closed",
    "customer_id": "uuid | null"
  }
}

Консьюмеры:

  • Customer Service — если customer_id IS NOT NULL, триггерит пересчёт dynamic групп для клиента (правила «LTV», «days_inactive» и т.п.). Если NULL — событие игнорируется.

order.cancelled

Публикуется при отмене заказа.

Topic: order.cancelled

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "order_number": "string",
    "total": "decimal",
    "cancel_reason": "string",
    "status": "cancelled"
  }
}

order.payment_requested

(Добавлено в BR 3.3)

Публикуется когда кассир / клиент нажимает «К оплате» на заказе — сигнал Paykeeper Adapter создать инвойс в PK.

Topic: order.payment_requested

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "order_number": "string",
    "total": "decimal",
    "cart": [
      {
        "name": "string",
        "quantity": "decimal",
        "price": "decimal",
        "sum": "decimal",
        "tax": "none | vat0 | vat10 | vat20 | vat110 | vat120",
        "payment_subject": "goods | service | work | ...",
        "payment_type": "full | prepay | advance | ..."
      }
    ],
    "customer_name": "string | null",
    "customer_phone": "string | null",
    "customer_email": "string | null"
  }
}

Консьюмеры:

  • Paykeeper Adapter (paykeeper-adapter-invoice) — создаёт инвойс в PK.

order.refund_requested

(Добавлено в BR 3.3)

Публикуется при инициации возврата из админки / POS — сигнал Paykeeper Adapter вызвать POST /change/payment/reverse/ в PK.

Topic: order.refund_requested

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "store_id": "uuid",
    "pk_payment_id": "string",
    "amount": "decimal",
    "is_full_refund": "boolean",
    "refund_cart": [
      {
        "name": "string",
        "quantity": "decimal",
        "price": "decimal",
        "sum": "decimal",
        "tax": "string"
      }
    ],
    "reason": "string",
    "initiated_by": "admin | pos",
    "cashier_id": "uuid | null"
  }
}

Консьюмеры:

  • Paykeeper Adapter (paykeeper-adapter-refund) — вызов PK reverse.

order.refunded

Публикуется при создании записи возврата (refund_records) через POST /internal/orders/{id}/refund. Одна продажа может породить несколько order.refunded (частичные возвраты).

Topic: order.refunded

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "refund_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "order_number": "string",
    "order_total": "decimal",
    "refund_amount": "decimal",
    "refund_total_so_far": "decimal",
    "is_full_refund": "boolean",
    "type": "refund | reversal",
    "payment_method": "cash | card",
    "rrn": "string | null",
    "fiscal_doc_number": "string | null",
    "initiated_by": "pos | admin",
    "cashier_id": "uuid"
  }
}

order.status.changed

Публикуется при любом переходе статуса заказа (new → in_progress → ready → closed/cancelled) а также при переходах агрегаторского flow (accepted → ready → handed_over / rejected). Консьюмируется Store Service для синхронизации статуса столов (zal_tables).

Topic: order.status.changed

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "order_id": "uuid",
    "store_id": "uuid",
    "franchise_id": "uuid",
    "order_number": "string",
    "previous_status": "string",
    "status": "string",
    "reason": "string | null"
  }
}

Консьюмеры:

  • Store Service (store-service-tables) — обновляет zal_tables.status при закрытии/отмене заказа
  • Aggregator Service (StatusChangedConsumer) — пушит статус в агрегатор (Я.Еда / МД)

pos.shift.opened

Публикуется при открытии кассовой смены ФЯ на POS-терминале. Источник — POS BFF (POST /internal/shifts/open). Order Service не хранит shift persistence — только транслирует событие для консьюмеров (админка, аналитика).

Topic: pos.shift.opened

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "store_id": "uuid",
    "franchise_id": "uuid",
    "cashier_id": "uuid",
    "fiscal_cycle": "integer",
    "opened_at": "datetime (ISO-8601)"
  }
}

pos.shift.closed

Публикуется при закрытии кассовой смены (Z-отчёт). Payload содержит итоги смены от ФЯ.

Topic: pos.shift.closed

{
  "event_id": "uuid",
  "timestamp": "datetime",
  "version": 1,
  "payload": {
    "store_id": "uuid",
    "franchise_id": "uuid",
    "cashier_id": "uuid",
    "fiscal_cycle": "integer",
    "closed_at": "datetime (ISO-8601)",
    "z_report_doc_number": "integer | null",
    "sales_total": "decimal",
    "refunds_total": "decimal",
    "cash_total": "decimal",
    "card_total": "decimal",
    "sales_count": "integer",
    "refunds_count": "integer"
  }
}

Потребляет

EventTopicConsumer groupЗачем
aggregator.order.receivedaggregator.order.receivedorder-service-aggregatorAggregatorOrderConsumer принимает заказ из Aggregator Service, создаёт Order с channel=aggregator, aggregator_order_id, aggregator_channel (yandex/delivery_club)
paykeeper.invoice.createdpaykeeper.invoice.createdorder-service-pk-invoiceСохраняет orders.pk_invoice_id, orders.pk_invoice_url после создания инвойса в PK. (BR 3.3)
paykeeper.payment.receivedpaykeeper.payment.receivedorder-service-pk-paymentФиксация оплаты: orders.paid_at, orders.pk_payment_id, orders.payment_method (маппинг из payment_system_id PK), эмит собственного order.paid. (BR 3.3)
paykeeper.payment.refundedpaykeeper.payment.refundedorder-service-pk-refundСоздание/обновление RefundRecord.status=done, эмит order.refunded. (BR 3.3)
paykeeper.receipt.fiscalizedpaykeeper.receipt.fiscalizedorder-service-pk-receiptЗаполнение orders.fiscal_data (JSONB с ФН/ФД/ФП/смена), orders.pk_fop_receipt_key. (BR 3.3)
paykeeper.receipt.failedpaykeeper.receipt.failedorder-service-pk-receipt-failСтавит orders.fiscal_failed=true — визуальная метка в UI. (BR 3.3)
paykeeper.refund.failedpaykeeper.refund.failedorder-service-pk-refund-failОбновляет RefundRecord.status=failed + сохраняет error_message. (BR 3.3)

Phase 2+

Дополнительно возможно:

  • catalog.product.updated — обновление кэша товаров
  • warehouse.stock.depleted — автоматический стоп-лист при нулевых остатках