6 KiB
МойСклад: синхронизация заказов покупателей
Дата: 2026-03-20
Приложение: picking_app
Цель
Расширить существующую интеграцию с МойСкладом: добавить синхронизацию заказов покупателей (customerorder) в очередь комплектации наравне с перемещениями (move).
Контекст
В picking_app уже реализована синхронизация перемещений (sync_picking_list()), хранение токена в Picking Settings, пагинация через nextHref. Заказы должны попасть в ту же очередь с минимальными изменениями в UI и логике комплектации.
Модель данных
Picking Settings — новое поле
| Поле | Тип | Значение по умолчанию |
|---|---|---|
ms_order_state |
Data | "Подтверждён" |
Поле помечается reqd: 1 и default: "Подтверждён" в DocType, чтобы пустое значение было исключено на уровне схемы. Дополнительно в коде: если значение пустое — использовать "Подтверждён" как runtime-fallback.
Picking List — новые поля
| Поле | Тип | Значение |
|---|---|---|
source_type |
Select | "Move" / "Order" |
Уникальность ms_id: текущий unique: 1 на поле ms_id снять. Вместо этого хранить ms_id с префиксом типа:
- Перемещения:
move:{uuid} - Заказы:
order:{uuid}
Это исключает коллизии UUID между разными типами сущностей МС.
Логика синхронизации
sync_picking_list() — изменения
- При создании новой записи проставлять
source_type = "Move"иms_id = "move:{uuid}" - При обновлении существующей записи — также проставлять
source_type = "Move"если поле пустое (backfill)
sync_picking_orders()
- Читает
ms_order_stateизPicking Settings; если пусто — использует"Подтверждён" - Тянет
customerorderиз МС:GET /entity/customerorder expand=state,store,positions.assortment,positions.assortment.uom limit=100 order=moment,descstoreобязательно включён в expand — иначе объект склада придёт как stub безname/id. - Фильтрует на клиенте:
order["state"]["name"] == ms_order_state to_warehouseберётся из объектаstoreчерез существующий_extract_store_id();from_warehouse— пустойms_idсохраняется с префиксом"order:{uuid}"- При создании —
source_type = "Order". При обновлении —source_typeвсегда перезаписывается значением"Order"(поле не является иммутабельным).
Производительность: фильтрация по статусу происходит на клиенте после получения всех страниц (МС API не поддерживает фильтр по state.name напрямую без UUID состояния). Лимит: не более 1000 заказов за одну синхронизацию (10 страниц по 100) — это осознанный компромисс.
sync_all() — новая публичная функция (@frappe.whitelist)
Вызывает последовательно sync_picking_list() и sync_picking_orders(), возвращает суммарный результат:
{"status": "ok", "moves": {"created": N, "updated": N}, "orders": {"created": N, "updated": N}}
Исправление существующего бага
В add_items_from_picking_list() строка 462 содержит ссылку на неопределённую переменную added_rows вместо added_row_names. Исправить в рамках этой задачи, иначе функция упадёт с NameError при первом же вызове.
API: get_picking_list_items()
Добавить опциональный параметр source_type:
source_type=None— вернуть все записи (включая NULL из legacy)source_type="Move"/"Order"— фильтровать по значению; NULL-записи при"Move"включаются (обратная совместимость)
Добавить source_type в список возвращаемых полей (fields), чтобы UI мог рендерить колонку "Тип".
UI
В попап-диалоге выбора позиций:
- Добавить колонку Тип: отображает "Перемещение" (Move и NULL) / "Заказ" (Order)
- Добавить фильтр по типу: табы или кнопки Все / Перемещения / Заказы
- Логика добавления позиций в
Picking Document— без изменений
Прочее
- Поле
ms_dateв UI показывает "Дата перемещения" — для заказов это несточное название. Принимаем как допустимое упрощение: переименование метки выходит за рамки задачи. - Статусная машина Picking List (Draft → Partial → Added) — без изменений
- Структура
Picking List Item— без изменений