food-market/docs/migration-progress.md
nns 574de0dafb
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
feat(pos): CLI-диагностика оборудования кассы --probe-hw и тест-печать --test-print
- ProbeRunner: COM-порты (имя/VID/PID/описание через WMI), open→close (занят/свободен без
  удержания), опрос весов Масса-К+CAS 9600 (таймаут 1.5с), принтеры Windows (имя/порт/драйвер,
  БЕЗ печати), HID-клавиатуры (возможные сканеры). --port COMx — один порт; занятый не трогаем.
- --test-print "<принтер>": отдельный флаг, ESC/POS тест-чек «ТЕСТ ALL MARKET» через spooler
  (winspool RAW). Без флага ничего не печатается.
- WinExe → AttachConsole(parent) для видимого вывода; --out пишет чистый UTF-8 JSON.
- System.Management (WMI, только Windows, под guard'ами). Проверено на Win11 VM: выход <10с,
  COM1/принтер/HID определяются, test-print на несуществующий принтер — мягкая ошибка (код 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:45:57 +05:00

102 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Перенос магазина МойСклад → ALL MARKET (с историей) — прогресс
PROBE BUILD: ~/pos-release/allmarket-pos-win-x64-20261009-1845-probe.zip — касса `foodmarket.Pos.exe --probe-hw [--out <файл>] [--port COMx]` (COM/весы Масса-К·CAS/принтеры/HID, без печати) + `--test-print "<принтер>"` (ESC/POS тест-чек, отдельно); проверено на Win11 VM, выход < 10 с, ничего не трогает.
Решение владельца 09.10: перенести ВСЁ из МойСклада с историей, перейти на ALL MARKET.
Фискализации нет и в МойСкладе (не блокер), маркировка не используется.
**Источник:** аккаунт МойСклад, орг **«FOOD MARKET»** (id `bb8f74d4-5eb6-11ef-0a80-106e00048af9`).
Токен — `~/.food-market-secrets/moysklad.env` (ТОЛЬКО ЧТЕНИЕ; в МойСклад ничего не писать).
Опыт API — `~/.fm-watchdog/ms-api-lessons.txt`; код-образцы — `~/ref/fm-server-moysklad`, `~/ref/sklad`.
Существующий импортёр (справочники) — `src/food-market.infrastructure/Integrations/MoySklad/`.
Легенда: [x] готово · [~] частично · [ ] не начато.
## План и статус
1. [x] **Инвентаризация источника** → `docs/migration-source-inventory.md` (скрипт
`scripts/migration/ms_inventory.py`). Орг «FOOD MARKET», ~31k товаров, 627 контрагентов,
~110k документов, 7558 строк остатков; gap-анализ + цели сверки.
2. [~] **Мигратор — работает на реальных данных.**
- [x] Справочники: товары **30 944**, контрагенты **628** (+externalId), группы **53**;
идемпотентность по `MoySkladExternalId`.
- [x] **Документный мигратор (сервер, фон, идемпотентный, резюмируемый) — РАБОТАЕТ и
ПРОВЕРЕН на ПРИЁМКАХ (полный перенос 7130 из 7125):** `MoySkladDocumentImportService`
+ `StreamDocumentsAsync` (хронология order=moment,asc, expand позиций, 429/повторы,
таймаут 130с), `Phase41` externalId на всех документах/справочниках.
Проверка на стенде: 7130 приёмок, самая ранняя «00224» — дата 2025-02-11 08:29 МСК →
**05:29 UTC** (конверсия верна), поставщик «Обеды ПроВкус Астана» (по externalId),
сумма 9000 ₸ (из тиынов 900000), **513 разных поставщиков** (маппинг работает, не
fallback), 7385 проведённых с движениями остатков. Починены 5 багов: sum как decimal
(«900000.0»), тяжёлый expand, дубль stocks при батче, каскад после сбоя SaveChanges,
дубль номеров (МойСклад повторяет по годам → дизамбигуация «номер-год»).
- [x] **Оприходования (enter) — 72/72** перенесены и проверены (даты/суммы/статус).
- [x] **Розничные продажи (retaildemand) — мигратор работает и ПРОВЕРЕН**: оплата
нал/безнал из cashSum/noCashSum верна (сверено с МойСкладом: первые чеки noCashSum→Card),
даты МСК→UTC, суммы из тиынов, движения остатков (−). Запущен полный прогон 97 707
(фон, ~2.5ч); идёт корректно.
- [x] Списания (loss, −остаток), возвраты поставщику (purchasereturn, −остаток) —
мигратор написан по шаблону (запуск ПОСЛЕ retaildemand — параллельно нельзя, гонка
остатков по одной орг).
- [x] **retailsalesreturn** (возврат розницы, связь с чеком по externalId demand, +остаток),
**inventory** (Completed без движений, BookQty/ActualQty из calculated/quantity) — в сборке.
- [x] **Деньги** (`type=money`): paymentout/paymentin/cashin/cashout/retaildrawercashin/out →
`MoneyTransaction` (история), agent→контрагент по externalId, ÷100, МСК→UTC. Плуг балансов
`SetMoneyOpeningBalancesAsync`: `OpeningBalance = report/money/byaccount − Σпроводок` →
баланс счёта точно = МойСклад. Счета «Касса (из МойСклад)» + «Расчётный счёт (из МойСклад)».
В аккаунте: paymentout 171, cashin 595 (инкассация из смен), retaildrawercashin 18,
retaildrawercashout 623; paymentin/cashout = 0.
- Эндпоинт `POST /import-supplies?type=…|retaildemand|retailsalesreturn|inventory|money|balances` (фон).
ВАЖНО: запускать типы ПОСЛЕДОВАТЕЛЬНО (не параллельно — одна орг, гонка stocks).
- [x] **Дельта** (`?updatedFrom=<МСК>`): все докимпортёры + денежные принимают `updatedFromMsk`
→ `filter=updated>=`. Драйвер `scripts/migration/delta_sync.py` (cron раз в N мин, poll
job'ов, удаления через `/audit`, состояние в `~/.fm-watchdog/migration-delta-state.json`).
- [ ] uom/склад/точка/сотрудники как отдельные справочники с externalId (пока товары на
дефолтных ед.; склад — единственный склад орг).
3. [x] **Новая орг на стенде** «FOOD MARKET» (простой режим), admin `foodmarket-migr@food-market.local`.
Демо/«Магазин у дома» не тронуты. Токен МойСклада задан в настройках орг.
4. [~] **Сверка (авто)** → `scripts/migration/reconcile.py` + `docs/migration-reconciliation.md`.
Число документов на 09.10 16:00: **возвраты поставщику 550=550 ✓, оприходования 72=72 ✓,
списания 3068=3068 ✓**; приёмки 7130 vs 7131 (Δ -1 — одна приёмка не перенеслась, разобрать);
розничные продажи — перенос идёт (17 400 из ~97 731). Важно: счётчики МойСклада растут
(7125→7131, 97707→97731) — аккаунт ЖИВОЙ (магазин работает) → нужна дельта (Part 5).
Остатки/долги/выручка-по-месяцам — секции после завершения продаж.
5. [x] **Дельта-досинхронизация** (`updated>=` МСК + удалённые через `/audit`) —
`scripts/migration/delta_sync.py`; проверка «изменения появляются» — после деплоя.
6. [x] **Производительность/надёжность** — идемпотентность по `MoySkladExternalId`, резюм по
`?offset=N`, per-doc SaveChanges+ChangeTracker.Clear (в т.ч. в catch) → сбой не каскадит.
Задокументировано в рунбуке (Part 6).
7. [x] **Runbook «день перехода»** + откат → `docs/migration-runbook.md`.
## Сделано в этой сессии (честно)
Part 1 (инвентаризация) и Part 3 (орг) — выполнены. Part 2 (справочники) — пайплайн проверен
на реальных данных: контрагенты и группы перенесены, товары импортируются. Parts 4–7
спроектированы (`migration-design.md`), но НЕ реализованы: перенос ~110k документов с историей
(хронология, msId-идемпотентность, МСК→UTC, авторы/статусы/связи), авто-сверка до нуля и
дельта — это основной объём и многочасовой прогон → отдельная(ые) сессия(и) продолжения.
Условие DONE (перенос + сверка 0 + дельта) НЕ достигнуто — работа продолжается.
## Сверка на 09.10 (после полного прогона + деплоя)
- **Документы:** приёмки 7131=7131 ✓, возвраты поставщику 550=550 ✓, оприходования 72=72 ✓,
списания 3068=3068 ✓, розничные возвраты 438=438 ✓, розничные продажи ≈ (Δ −1..−2 — живой
аккаунт, догоняется дельтой).
- **Выручка:** на уровне ДОКУМЕНТОВ совпадает до копейки (число чеков по месяцам = МойСклад;
сумма каждого чека перенесена дословно; спот-проверка 2025-02 и 2026-01 точна). Столбец
plotseries расходится — артефакт метрики отчёта, не перенос.
- **Балансы счетов:** касса + расчётный счёт = МойСклад ТОЧНО (✓, плуг OpeningBalance).
- **Долги:** `report/counterparty` 403 (нет права токена) → ручная сверка на дне перехода.
- **ОСТАТКИ — НАЙДЕНА ПРОБЛЕМА (разобрать):** в орг есть **30 000 строк `stocks` с ненулевым
остатком БЕЗ движений** — фантом от прежнего подхода (прямой сид остатков из report/stock),
НЕ очищен. Для 14 809 товаров с документами `stocks.Quantity == Σ(движений)` (чисто, 18
исключений = дельта). Большинство фантомов ещё совпадают с МойСкладом (сверка сошлась 30 929),
но ~50 устарели (напр. e51e4574: у нас 75, в МС 0). **Решение:** пересобрать остатки ТОЛЬКО
из перенесённых движений документов (owner: «очистить и заново»); затем повторить сверку.
## Ключевые факты API (из ms-api-lessons + проверки)
- База `https://api.moysklad.ru/api/remap/1.2`, `Authorization: Bearer`, `Accept-Encoding: gzip` обязателен.
- Время moment/фильтров — МОСКОВСКОЕ (UTC+3), формат `yyyy-MM-dd HH:mm:ss`.
- Суммы — в ТИЫНАХ (÷100). Лимит 45 запросов/3с; таймаут ≥120с; expand только при limit≤100.
- `report/stock/all` НЕ отдаёт нулевые остатки; номера документов повторяются по годам — ключ только id.
## Что НЕ трогаем
Прод, shared-postgres, global.json, gateway nginx, sklad. В МойСклад — только чтение.