Current implementation
msDelivery — справочник методов доставки (name, price, weight_price, class, properties, validation_rules, free_delivery_amount). Связь с оплатой: msDeliveryMember. На заказе хранится delivery_id + delivery_cost.
Контракт провайдера: DeliveryProviderInterface с единственным методом getCost(). База Delivery / stub DefaultDelivery считают стоимость (вес, порог бесплатной доставки, %/фикс). Пример в docblock — CDEK API только для тарифа.
Checkout: draft выбирает delivery_id через OrderFieldManager (валидация + validation_rules метода). Submit проверяет пару delivery/payment (DeliveryService::getDeliveryPaymentPairError) и required address fields. После submit менеджер может менять delivery_id через ManagerOrderMutationService::update.
Fulfillment: отдельной сущности нет. «Отправлен» = статус заказа ms3_order_status_sent (seed id 4, final=1). Dedicated setting вроде ms3_status_sent в seed map статусов нет (в отличие от new/paid/canceled). События — только общие msOnBeforeChangeOrderStatus / msOnChangeOrderStatus. Отдельного msOnShip* нет.
Трекинг: колонки tracking_number / таблицы shipment нет. В vueManager OrderTabsRegistry — пример ключа tracking для plugin tab (UI hook из #166), без модели данных в core. msOrder / msOrderAddress / msOrderProduct имеют JSON properties, куда пакеты могут писать ad-hoc.
DeliveryService (ms3_delivery_service): load controller, cost, payment pairing, remove. Нет createShipment / updateTracking / webhook.
Тесты: cost, delivery/payment pair, reference CRUD. Lifecycle отгрузки / tracking / async callback — нет.
Delivery vs Shipment distinction
|
Delivery method (msDelivery) |
Shipment (сейчас) |
| Смысл |
Как покупатель получает заказ |
Конкретная отгрузка заказа |
| В core |
Да |
Нет |
| Поля |
тарификация, class, validation |
— |
| Состояние |
active справочник |
подмена через order.status_id = sent |
| Внешний id / трек |
— |
нет |
Core сейчас моделирует только Delivery method. Shipment как сущность отсутствует.
Problems
- Нет различия delivery method vs shipment: fulfillment = смена статуса заказа.
- Нет поля/модели для tracking number; плагины вынуждены класть данные в
properties или свои таблицы.
- Статус отгрузки нельзя вести отдельно от order status (in transit / delivered / returned без плодения статусов заказа).
DeliveryProviderInterface = только getCost. Нет контракта create/label/callback статуса доставки.
- Async обновления от CDEK / Почты / Яндекса / DPD в core некуда приземлять единообразно.
- Нет события «заказ отгружен» сверх общего status change;
sent ещё и final — дальше по статусам заказа обычно нельзя.
- Partial / multi-shipment в схеме не предусмотрены (order lines без shipped qty). Это не блокер для v1, но ad-hoc
properties усложнят эволюцию.
Вне scope v1: multi-warehouse, WMS, обязательные multi-parcel, реализация CDEK/DPD в core.
Связано: #568 (delivery/list discovery), #590 (payment lifecycle). Этот issue — fulfillment/shipment, не checkout list и не оплата.
Proposed Core abstraction
Минимально (имена на PR):
msShipment (1:1 с заказом в v1)
id
order_id
delivery_id # snapshot метода на момент отгрузки
status # preparing | shipped | in_transit | delivered | cancelled | returned | failed
tracking_number
external_id # id у провайдера
carrier / provider # optional string / class key
shipped_at
delivered_at
meta (json, non-secret)
ShipmentLifecycleService (DI): create (из order), transition status, setTracking, applyProviderEvent.
- Смена order status (например в
sent / custom) — policy через OrderStatusService, не прямой status_id.
DeliveryProviderInterface сохранить для cost. Опционально второй интерфейс (ShipmentProviderInterface: createShipment, handleWebhook) без ломки существующих cost-only классов.
- v1: один active shipment на order. Partial/multi — follow-up (line allocations).
Shipment lifecycle
Рекомендуемый минимум:
(none) → preparing → shipped → in_transit → delivered
↘ cancelled | failed
delivered / shipped → returned (policy)
Маппинг на order status (configurable):
shipped (или in_transit) → order sent (если ещё не final-conflict)
delivered → optional custom status или только shipment status
cancelled / failed на shipment до sent → order cancel policy
Магазины без курьерских API: менеджер создаёт shipment вручную + вводит трек, либо только статус заказа как сейчас (setting ms3_shipment_enabled / soft mode).
Tracking
tracking_number — first-class field, обновляется независимо от order status.
- События
msOnBeforeUpdateShipmentTracking / msOnUpdateShipmentTracking (имена на PR).
- Публичный cabinet/order DTO может отдавать tracking без secrets провайдера.
Partial shipment considerations
v1: целиком заказ = один shipment. Не требовать line-level shipped qty.
Зафиксировать в дизайне: будущий msShipmentItem (shipment_id, order_product_id, qty) не ломает 1:1 API (order.shipments[] длиной 1). Не реализовывать multi в этом issue.
External provider integration
Пакет (CDEK и т.д.):
DeliveryProviderInterface для тарифа на checkout (как сейчас).
- Опционально shipment provider: создать накладную, сохранить
external_id + tracking через LifecycleService.
- Webhook → core route / documented slot → verify →
applyProviderEvent.
- Credentials в
msDelivery.properties / settings, не в публичных DTO.
Core не реализует API перевозчиков.
Events
Помимо status order events:
- before/after shipment create
- before/after shipment status change
- tracking update
- (optional) provider callback applied
Плагины на msOnChangeOrderStatus остаются; для fulfillment предпочтителен shipment event, чтобы in_transit не требовал нового order status.
REST API
Backward compatibility
msDelivery, getCost, draft delivery_id, submit validation — без ломания.
- Статус
sent и ручная смена статуса менеджером работают как сейчас.
- Пока shipment выключен / не создан: поведение идентично текущему core.
- Cost-only delivery classes без shipment interface продолжают работать.
- Plugin order tabs (tracking UI) могут перейти на core fields вместо private storage.
Tests
Acceptance criteria
Дополнительный контекст
Аудит по коду beta без правок. Delivery method + cost extension уже есть. Не хватает shipment/tracking lifecycle для fulfillment-пакетов.
Current implementation
msDelivery— справочник методов доставки (name, price, weight_price, class, properties, validation_rules, free_delivery_amount). Связь с оплатой:msDeliveryMember. На заказе хранитсяdelivery_id+delivery_cost.Контракт провайдера:
DeliveryProviderInterfaceс единственным методомgetCost(). БазаDelivery/ stubDefaultDeliveryсчитают стоимость (вес, порог бесплатной доставки, %/фикс). Пример в docblock — CDEK API только для тарифа.Checkout: draft выбирает
delivery_idчерезOrderFieldManager(валидация +validation_rulesметода). Submit проверяет пару delivery/payment (DeliveryService::getDeliveryPaymentPairError) и required address fields. После submit менеджер может менятьdelivery_idчерезManagerOrderMutationService::update.Fulfillment: отдельной сущности нет. «Отправлен» = статус заказа
ms3_order_status_sent(seed id 4,final=1). Dedicated setting вродеms3_status_sentв seed map статусов нет (в отличие от new/paid/canceled). События — только общиеmsOnBeforeChangeOrderStatus/msOnChangeOrderStatus. ОтдельногоmsOnShip*нет.Трекинг: колонки
tracking_number/ таблицы shipment нет. ВvueManagerOrderTabsRegistry— пример ключаtrackingдля plugin tab (UI hook из #166), без модели данных в core.msOrder/msOrderAddress/msOrderProductимеют JSONproperties, куда пакеты могут писать ad-hoc.DeliveryService(ms3_delivery_service): load controller, cost, payment pairing, remove. Нет createShipment / updateTracking / webhook.Тесты: cost, delivery/payment pair, reference CRUD. Lifecycle отгрузки / tracking / async callback — нет.
Delivery vs Shipment distinction
msDelivery)order.status_id= sentCore сейчас моделирует только Delivery method. Shipment как сущность отсутствует.
Problems
propertiesили свои таблицы.DeliveryProviderInterface= толькоgetCost. Нет контракта create/label/callback статуса доставки.sentещё иfinal— дальше по статусам заказа обычно нельзя.propertiesусложнят эволюцию.Вне scope v1: multi-warehouse, WMS, обязательные multi-parcel, реализация CDEK/DPD в core.
Связано: #568 (delivery/list discovery), #590 (payment lifecycle). Этот issue — fulfillment/shipment, не checkout list и не оплата.
Proposed Core abstraction
Минимально (имена на PR):
ShipmentLifecycleService(DI): create (из order), transition status, setTracking, applyProviderEvent.sent/ custom) — policy черезOrderStatusService, не прямойstatus_id.DeliveryProviderInterfaceсохранить для cost. Опционально второй интерфейс (ShipmentProviderInterface: createShipment, handleWebhook) без ломки существующих cost-only классов.Shipment lifecycle
Рекомендуемый минимум:
Маппинг на order status (configurable):
shipped(илиin_transit) → ordersent(если ещё не final-conflict)delivered→ optional custom status или только shipment statuscancelled/failedна shipment до sent → order cancel policyМагазины без курьерских API: менеджер создаёт shipment вручную + вводит трек, либо только статус заказа как сейчас (setting
ms3_shipment_enabled/ soft mode).Tracking
tracking_number— first-class field, обновляется независимо от order status.msOnBeforeUpdateShipmentTracking/msOnUpdateShipmentTracking(имена на PR).Partial shipment considerations
v1: целиком заказ = один shipment. Не требовать line-level shipped qty.
Зафиксировать в дизайне: будущий
msShipmentItem (shipment_id, order_product_id, qty)не ломает 1:1 API (order.shipments[] длиной 1). Не реализовывать multi в этом issue.External provider integration
Пакет (CDEK и т.д.):
DeliveryProviderInterfaceдля тарифа на checkout (как сейчас).external_id+ tracking через LifecycleService.applyProviderEvent.msDelivery.properties/ settings, не в публичных DTO.Core не реализует API перевозчиков.
Events
Помимо status order events:
Плагины на
msOnChangeOrderStatusостаются; для fulfillment предпочтителен shipment event, чтобы in_transit не требовал нового order status.REST API
order_id.POST /api/v1/delivery/callback/{delivery_id}или addon route slot (как в Core: introduce a payment lifecycle abstraction for async providers #590 для payment).Backward compatibility
msDelivery,getCost, draftdelivery_id, submit validation — без ломания.sentи ручная смена статуса менеджером работают как сейчас.Tests
delivered)msDelivery.propertiessecretsAcceptance criteria
msDeliverymethod и от одного толькоorder.status_id.tracking_numberбез смены статуса заказа.OrderStatusServiceпо policy.DeliveryProviderInterfaceне ломается; внешний пакет может обновлять shipment async через документированный extension point.Дополнительный контекст
Аудит по коду
betaбез правок. Delivery method + cost extension уже есть. Не хватает shipment/tracking lifecycle для fulfillment-пакетов.