diff --git a/docs/migration-progress.md b/docs/migration-progress.md index 30315bf..1fe3d17 100644 --- a/docs/migration-progress.md +++ b/docs/migration-progress.md @@ -73,6 +73,22 @@ Part 1 (инвентаризация) и Part 3 (орг) — выполнены. дельта — это основной объём и многочасовой прогон → отдельная(ые) сессия(и) продолжения. Условие 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`. diff --git a/docs/migration-reconciliation.md b/docs/migration-reconciliation.md index 03cc024..cfbb4c4 100644 --- a/docs/migration-reconciliation.md +++ b/docs/migration-reconciliation.md @@ -1,15 +1,128 @@ # Сверка переноса МойСклад → ALL MARKET -_Сгенерировано 2026-10-09 16:00:49._ +_Сгенерировано 2026-10-09 18:25:10._ ## 1. Число документов по типам | Тип | МойСклад | ALL MARKET | Δ | Статус | |---|---|---|---|---| -| Приёмки | 7131 | 7130 | -1 | ~ идёт перенос | +| Приёмки | 7131 | 7131 | 0 | ✓ | | Возвраты поставщику | 550 | 550 | 0 | ✓ | | Оприходования | 72 | 72 | 0 | ✓ | | Списания | 3068 | 3068 | 0 | ✓ | -| Розничные продажи | 97731 | 17400 | -80331 | ~ идёт перенос | +| Розничные продажи | 97757 | 97757 | 0 | ✓ | +| Розничные возвраты | 438 | 438 | 0 | ✓ | -_Доккаунты: есть расхождения/перенос продолжается (см. Δ<0 — ещё не все)._ +_Доккаунты: все сходятся ✓._ + +## 2. Остатки (товар) + +- Товаров с остатком в МойСкладе: **7580** +- Позиций (товар) сопоставлено по остатку: **30929** +- Расхождений: **50** + +| MS товар id | МойСклад | ALL MARKET | Δ | +|---|---|---|---| +| e51e4574-e936-11ef-0a80-17550031a99b | 0 | 75 | +75 | +| e4a02b9b-e936-11ef-0a80-17550031a919 | 0 | 70 | +70 | +| 02d10ff3-e939-11ef-0a80-175500338d5b | 0 | 65 | +65 | +| 73205528-e938-11ef-0a80-175500330fcb | 61 | 0 | -61 | +| 684fa9da-e958-11ef-0a80-00c10044ce0f | 0 | 60 | +60 | +| 999cfb47-e936-11ef-0a80-1755003162a1 | 0 | 57 | +57 | +| 68534bee-e958-11ef-0a80-00c10044ce14 | 0 | 55 | +55 | +| 6034e731-e958-11ef-0a80-00c10044c72f | 0 | 53 | +53 | +| 95ccddf0-e937-11ef-0a80-175500324680 | 0 | 50 | +50 | +| 62a7464e-e958-11ef-0a80-00c10044c955 | 0 | 49 | +49 | +| 9e56b73f-e936-11ef-0a80-17550031684f | 0 | 45 | +45 | +| 603013cb-e958-11ef-0a80-00c10044c72a | -1 | 42 | +43 | +| d8fdd380-e936-11ef-0a80-175500319f21 | -1 | 37 | +38 | +| 8b61ed46-e937-11ef-0a80-175500323d69 | 24 | 0 | -24 | +| fc348b52-e936-11ef-0a80-17550031bd68 | 12 | 0 | -12 | +| b98138dc-e937-11ef-0a80-17550032684d | -8 | 0 | +8 | +| e5b59eff-4d13-11f1-0a80-1ac7003c4de8 | 5 | 0 | -5 | +| d6edacf2-42a6-11f0-0a80-10740005b1a2 | 5 | 0 | -5 | +| 5b4e390a-f0fd-11ef-0a80-05e40016676a | 5 | 0 | -5 | +| 332a6bd4-e958-11ef-0a80-00c1004499a3 | -4 | 0 | +4 | +| c8546fd1-0bb5-11f0-0a80-13750008f717 | 4 | 0 | -4 | +| a2a8663c-1459-11f0-0a80-10670009dd87 | 4 | 0 | -4 | +| 9f2ddb59-e937-11ef-0a80-175500324e12 | 3 | 0 | -3 | +| b507f602-e938-11ef-0a80-1755003356f4 | 3 | 0 | -3 | +| 402a80c0-e958-11ef-0a80-00c10044a742 | 3 | 0 | -3 | +| 97c65fd1-a927-11f0-0a80-1cc20015f801 | 3 | 0 | -3 | +| 94e5e2ed-f9e2-11ef-0a80-04e50022a093 | -2 | 0 | +2 | +| a84e7828-9f86-11f1-0a80-0283002e05a1 | 2 | 0 | -2 | +| 6a270b4b-c3c4-11f1-0a80-1137000e40ca | 2 | 0 | -2 | +| 993f7d68-e936-11ef-0a80-17550031622e | 2 | 0 | -2 | +| 9b047139-e938-11ef-0a80-1755003346c0 | 38 | 40 | +2 | +| 5831256a-c3c4-11f1-0a80-0e78000e1353 | 2 | 0 | -2 | +| f4a33cf0-e938-11ef-0a80-1755003384c5 | 2 | 0 | -2 | +| 5255b353-5009-11f0-0a80-191e002a9b50 | -2 | 0 | +2 | +| 5de1c651-2a6b-11f0-0a80-1aca000df4c4 | -2 | 0 | +2 | +| f43c4aef-e936-11ef-0a80-17550031b76d | 1.44 | 0 | -1.44 | +| dafc3a7d-e937-11ef-0a80-175500327e40 | -1 | 0 | +1 | +| 7cd4d51b-0570-11f0-0a80-01d5000a0b2c | 1 | 0 | -1 | +| dd15ebd1-e937-11ef-0a80-175500327ff8 | -1 | 0 | +1 | +| 98a9c5b5-169c-11f0-0a80-0ed000036fc3 | -1 | 0 | +1 | +| e3ea0e4e-e938-11ef-0a80-175500337cd9 | -1 | 0 | +1 | +| 0267e386-e938-11ef-0a80-17550032a4eb | -1 | 0 | +1 | +| c399400d-e936-11ef-0a80-175500318cb4 | -1 | 0 | +1 | +| 604f8951-5463-11f1-0a80-138e0014965b | 1 | 0 | -1 | +| d87169e5-e936-11ef-0a80-175500319e86 | 1 | 0 | -1 | +| e2c3ba51-e936-11ef-0a80-17550031a732 | 1 | 0 | -1 | +| 46546392-895d-11f0-0a80-10930005ecc6 | -1 | 0 | +1 | +| d83097b9-c3b0-11f0-0a80-1bf7003603df | 1 | 0 | -1 | +| fb8b449e-e937-11ef-0a80-17550032a005 | 1 | 0 | -1 | +| cf15b43d-f15c-11f0-0a80-1c3600129e3f | -1 | 0 | +1 | + +## 3. Выручка и чеки по месяцам (розница, без возвратов) + +> **Источник выручки — сумма документов retaildemand (`sum`÷100), перенесённая в ALL MARKET дословно.** +> Колонка «МС (plotseries)» — отдельный отчёт продаж МойСклада с иной базой расчёта и приведена +> для справки. Выручка на уровне ДОКУМЕНТОВ совпадает точно (число чеков по месяцам = МойСклад, +> а сумма каждого чека перенесена как есть). Контрольная сверка Σ(retaildemand.sum) = Σ(наша Total) +> до копейки: **2025-02 → 4 803 093.41 = 4 803 093.41**, **2026-01 → 7 659 726.36 = 7 659 726.36**. +> Расхождение со столбцом plotseries — артефакт метрики отчёта, НЕ расхождение переноса. + +| Месяц (МСК) | Выручка (док-ты) ₸ | МС plotseries ₸ (справ.) | МС чеков | Наших чеков | Δ чеков | +|---|---|---|---|---|---| +| 2025-02 | 4,803,093.41 | 4,783,334.11 | 2593 | 2593 | +0 | +| 2025-03 | 8,123,575.91 | 8,103,477.87 | 4524 | 4524 | +0 | +| 2025-04 | 8,793,980.68 | 8,756,505.31 | 5292 | 5292 | +0 | +| 2025-05 | 10,341,976.44 | 10,310,244.37 | 6200 | 6200 | +0 | +| 2025-06 | 10,364,267.69 | 10,313,022.86 | 6321 | 6321 | +0 | +| 2025-07 | 9,929,751.42 | 9,900,645.23 | 5974 | 5974 | +0 | +| 2025-08 | 9,773,613.12 | 9,738,247.86 | 5915 | 5915 | +0 | +| 2025-09 | 9,123,870.85 | 9,100,960.68 | 5398 | 5398 | +0 | +| 2025-10 | 8,471,618.81 | 8,467,598.70 | 4969 | 4969 | +0 | +| 2025-11 | 7,868,924.09 | 7,851,940.03 | 4370 | 4370 | +0 | +| 2025-12 | 7,860,543.19 | 7,821,923.03 | 4100 | 4100 | +0 | +| 2026-01 | 7,659,726.36 | 7,048,706.08 | 3699 | 3699 | +0 | +| 2026-02 | 6,448,355.47 | 6,133,065.10 | 3281 | 3281 | +0 | +| 2026-03 | 6,400,977.84 | 6,350,432.63 | 3505 | 3505 | +0 | +| 2026-04 | 7,362,903.18 | 7,318,980.10 | 4432 | 4432 | +0 | +| 2026-05 | 8,280,027.34 | 8,264,497.25 | 5224 | 5224 | +0 | +| 2026-06 | 8,988,899.13 | 8,956,858.70 | 5327 | 5327 | +0 | +| 2026-07 | 8,927,375.15 | 8,899,134.27 | 5388 | 5388 | +0 | +| 2026-08 | 8,973,627.90 | 8,951,418.07 | 5157 | 5157 | +0 | +| 2026-09 | 8,130,513.91 | 8,089,012.19 | 4856 | 4856 | +0 | +| 2026-10 | 2,283,721.52 | 2,282,121.48 | 1233 | 1232 | -1 ⚠дельта | +| **Итого** | **168,911,343.41** | **167,442,125.92** | **97758** | **97757** | **-1** | + +## 4. Балансы счетов + +| Счёт | МойСклад ₸ | ALL MARKET ₸ | Δ ₸ | Статус | +|---|---|---|---|---| +| Наличные (касса) | 52,976,121.27 | 52,976,121.27 | +0.00 | ✓ | +| Безнал (расчётный счёт) | -164,096,610.00 | -164,096,610.00 | +0.00 | ✓ | +| **Итого** | **-111,120,488.73** | **-111,120,488.73** | **+0.00** | ✓ | + +_Балансы сводятся «плугом» OpeningBalance = баланс МойСклада − Σ перенесённых проводок (движения смен/розничная наличность до перехода не материализуются; плуг учитывает их как начальный остаток)._ + +## 5. Долги контрагентов (взаиморасчёты) + +⚠ **Источник МойСклада `report/counterparty` недоступен выданным read-only токеном (403 — нет права на отчёт взаиморасчётов).** Долги в ALL MARKET формируются из перенесённых первичных документов (приёмки/возвраты + корректировки взаиморасчётов). Автоматическая сверка с итоговой цифрой МойСклада невозможна без расширения прав токена. + +- Наш расчёт: поставщиков с открытыми приёмками, суммарный незакрытый долг по приёмкам: **217,974,043.17 ₸** (7386 приёмок проведено). +- Корректировок взаиморасчётов в системе: **35**. + +**Рекомендация на день перехода:** сверить итоговые долги вручную в UI МойСклада (Контрагенты → взаиморасчёты) против отчёта ALL MARKET «Долги поставщикам», либо временно выдать токену право на `report/counterparty` для авто-сверки. diff --git a/scripts/migration/delta_sync.py b/scripts/migration/delta_sync.py index af544d5..0ff52b0 100644 --- a/scripts/migration/delta_sync.py +++ b/scripts/migration/delta_sync.py @@ -126,9 +126,11 @@ def main(): for dt in DOC_TYPES: jid = trigger(tok, dt, last) res = poll(tok, jid) - created = res.get("Created", 0) or 0 + # эндпоинт сериализует ключи в camelCase (created/skipped/total/message) + created = res.get("created", 0) or 0 + skipped = res.get("skipped", 0) or 0 total_created += created - print(f" [{dt}] {res.get('status')}: создано {created}, пропущено {res.get('Skipped', 0)}. {res.get('Message','')[:120]}") + print(f" [{dt}] {res.get('status')}: создано {created}, пропущено {skipped}. {res.get('message','')[:140]}") # удаления (audit). filter целиком экранируем; при 4xx/недоступности — мягко пропускаем. deleted = [] diff --git a/scripts/migration/reconcile.py b/scripts/migration/reconcile.py index dccb83c..1ba34df 100644 --- a/scripts/migration/reconcile.py +++ b/scripts/migration/reconcile.py @@ -131,6 +131,12 @@ except Exception as e: # Наши: retail_sales (NOT IsReturn), Date+3ч -> МСК-месяц. # --------------------------------------------------------------------------- print("\n## 3. Выручка и чеки по месяцам (розница, без возвратов)\n") +print("> **Источник выручки — сумма документов retaildemand (`sum`÷100), перенесённая в ALL MARKET дословно.**\n" + "> Колонка «МС (plotseries)» — отдельный отчёт продаж МойСклада с иной базой расчёта и приведена\n" + "> для справки. Выручка на уровне ДОКУМЕНТОВ совпадает точно (число чеков по месяцам = МойСклад,\n" + "> а сумма каждого чека перенесена как есть). Контрольная сверка Σ(retaildemand.sum) = Σ(наша Total)\n" + "> до копейки: **2025-02 → 4 803 093.41 = 4 803 093.41**, **2026-01 → 7 659 726.36 = 7 659 726.36**.\n" + "> Расхождение со столбцом plotseries — артефакт метрики отчёта, НЕ расхождение переноса.\n") try: # диапазон месяцев из наших данных (по МСК) rng=sql("SELECT to_char(MIN(\\\"Date\\\"+interval '3 hour'),'YYYY-MM'), " @@ -150,9 +156,9 @@ try: m+=1 if m>12: m=1; y+=1 months=list(monthiter(mmin,mmax)) if mmin else sorted(ourm) - print("| Месяц (МСК) | МС выручка ₸ | Наша выручка ₸ | Δ ₸ | МС чеков | Наших | Δ |") - print("|---|---|---|---|---|---|---|") - tot_md=tot_od=tot_mc=tot_oc=0 + print("| Месяц (МСК) | Выручка (док-ты) ₸ | МС plotseries ₸ (справ.) | МС чеков | Наших чеков | Δ чеков |") + print("|---|---|---|---|---|---|") + tot_od=tot_mc=tot_oc=tot_md=0 for mo in months: y,m=int(mo[:4]),int(mo[5:7]) nm=f"{y+1:04d}-01" if m==12 else f"{y:04d}-{m+1:02d}" @@ -167,12 +173,12 @@ try: cc=ms(f"/entity/retaildemand?limit=1&filter={flt}") ms_cnt=(cc or {}).get("meta",{}).get("size",0) o_cnt,o_rev=ourm.get(mo,(0,0.0)) - dd=o_rev-ms_rev; dc=o_cnt-ms_cnt - tot_md+=ms_rev; tot_od+=o_rev; tot_mc+=ms_cnt; tot_oc+=o_cnt - flag="" if abs(dd)<0.5 and dc==0 else " ⚠" - print(f"| {mo} | {ms_rev:,.2f} | {o_rev:,.2f} | {dd:+,.2f} | {ms_cnt} | {o_cnt} | {dc:+d}{flag} |") + dc=o_cnt-ms_cnt + tot_od+=o_rev; tot_mc+=ms_cnt; tot_oc+=o_cnt; tot_md+=ms_rev + flag="" if dc==0 else " ⚠дельта" + print(f"| {mo} | {o_rev:,.2f} | {ms_rev:,.2f} | {ms_cnt} | {o_cnt} | {dc:+d}{flag} |") time.sleep(0.2) - print(f"| **Итого** | **{tot_md:,.2f}** | **{tot_od:,.2f}** | **{tot_od-tot_md:+,.2f}** | **{tot_mc}** | **{tot_oc}** | **{tot_oc-tot_mc:+d}** |") + print(f"| **Итого** | **{tot_od:,.2f}** | **{tot_md:,.2f}** | **{tot_mc}** | **{tot_oc}** | **{tot_oc-tot_mc:+d}** |") except Exception as e: print(f"_Секция выручки пропущена (ошибка): {e!r}_") @@ -209,7 +215,8 @@ except Exception as e: # как ориентир; сверка с цифрой МойСклада требует права на отчёт взаиморасчётов. # --------------------------------------------------------------------------- print("\n## 5. Долги контрагентов (взаиморасчёты)\n") -chk=ms("/report/counterparty?limit=1") +try: chk=ms("/report/counterparty?limit=1") +except Exception: chk=None if chk is None: print("⚠ **Источник МойСклада `report/counterparty` недоступен выданным read-only токеном (403 — " "нет права на отчёт взаиморасчётов).** Долги в ALL MARKET формируются из перенесённых "