diff --git a/docs/migration-progress.md b/docs/migration-progress.md index 32cbb30..5837f42 100644 --- a/docs/migration-progress.md +++ b/docs/migration-progress.md @@ -84,12 +84,13 @@ Part 1 (инвентаризация) и Part 3 (орг) — выполнены. plotseries расходится — артефакт метрики отчёта, не перенос. - **Балансы счетов:** касса + расчётный счёт = МойСклад ТОЧНО (✓, плуг OpeningBalance). - **Долги:** `report/counterparty` 403 (нет права токена) → ручная сверка на дне перехода. -- **ОСТАТКИ — НАЙДЕНА ПРОБЛЕМА (разобрать):** в орг есть **30 000 строк `stocks` с ненулевым - остатком БЕЗ движений** — фантом от прежнего подхода (прямой сид остатков из report/stock), - НЕ очищен. Для 14 809 товаров с документами `stocks.Quantity == Σ(движений)` (чисто, 18 - исключений = дельта). Большинство фантомов ещё совпадают с МойСкладом (сверка сошлась 30 929), - но ~50 устарели (напр. e51e4574: у нас 75, в МС 0). **Решение:** пересобрать остатки ТОЛЬКО - из перенесённых движений документов (owner: «очистить и заново»); затем повторить сверку. +- **ОСТАТКИ — РАЗОБРАНО:** орг «FOOD MARKET» чистая (30 944 товара = 30 944 msId, БЕЗ дублей; + `stocks == Σ(движений)`). «Фантом 30 000» оказался БАГОМ СВЕРКИ: SQL не фильтровал по орг, а + Демо-магазин тоже засеян из МойСклада теми же msId → суммировался. После скоупа по орг + (`ORG=eb5db1ae-…`) расхождений остатков **42**: **35** — товары МС с ДУБЛЕМ ШК (в МС несколько + карточек на один штрихкод, часто весовые/хлеб «0…»); у нас ШК уникален → слиты (`MergeByBarcode`), + msId «лишней» карточки нет в каталоге (нет аналога в модели) — остаток мал; **7** — живой аккаунт + (±1–4, догоняются дельтой). Все 42 объяснены построчно в `migration-reconciliation.md`. ## Ключевые факты API (из ms-api-lessons + проверки) - База `https://api.moysklad.ru/api/remap/1.2`, `Authorization: Bearer`, `Accept-Encoding: gzip` обязателен. diff --git a/docs/migration-reconciliation.md b/docs/migration-reconciliation.md index cfbb4c4..77a6930 100644 --- a/docs/migration-reconciliation.md +++ b/docs/migration-reconciliation.md @@ -1,6 +1,6 @@ # Сверка переноса МойСклад → ALL MARKET -_Сгенерировано 2026-10-09 18:25:10._ +_Сгенерировано 2026-10-09 18:55:15._ ## 1. Число документов по типам @@ -10,69 +10,71 @@ _Сгенерировано 2026-10-09 18:25:10._ | Возвраты поставщику | 550 | 550 | 0 | ✓ | | Оприходования | 72 | 72 | 0 | ✓ | | Списания | 3068 | 3068 | 0 | ✓ | -| Розничные продажи | 97757 | 97757 | 0 | ✓ | +| Розничные продажи | 97761 | 97757 | -4 | ~ дельта | | Розничные возвраты | 438 | 438 | 0 | ✓ | -_Доккаунты: все сходятся ✓._ +_Доккаунты: Δ<0 — догоняется дельтой (живой аккаунт рос во время переноса); Δ>0 — разобрать._ ## 2. Остатки (товар) -- Товаров с остатком в МойСкладе: **7580** -- Позиций (товар) сопоставлено по остатку: **30929** -- Расхождений: **50** +- Товаров с остатком в МойСкладе: **7579** +- Позиций (товар) сопоставлено по остатку: **30937** +- Расхождений: **42** (из них нет в каталоге переноса: **35**, в каталоге: **7**) + +> **Объяснение расхождений.** Почти все — товары МойСклада с ДУБЛИРУЮЩИМ штрихкодом (в МС несколько карточек на один ШК, часто весовые/хлеб с «0…»). В ALL MARKET штрихкод уникален, поэтому такие карточки слиты в одну (`MergeByBarcode`), а msId «лишней» карточки в каталог не попал → её небольшой остаток не сопоставляется (нет аналога в нашей модели). Единичные расхождения по товарам, которые ЕСТЬ в каталоге — живой аккаунт (продажи во время сверки, догоняются дельтой). **Решение:** для чистого перехода устранить дубли ШК в МойСкладе (задать уникальные коды) ЛИБО принять слияние — суммарный остаток затронутых позиций мал. + + +_Нет в каталоге переноса (дубль ШК в МойСкладе → слияние, нет аналога) (35):_ | 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 | +| e5b59eff-4d13-11f1-0a80-1ac7003c4de8 | 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 | +| c8546fd1-0bb5-11f0-0a80-13750008f717 | 4 | 0 | -4 | | 402a80c0-e958-11ef-0a80-00c10044a742 | 3 | 0 | -3 | +| b507f602-e938-11ef-0a80-1755003356f4 | 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 | +| 9f2ddb59-e937-11ef-0a80-175500324e12 | 3 | 0 | -3 | | 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 | +| 6a270b4b-c3c4-11f1-0a80-1137000e40ca | 2 | 0 | -2 | +| a84e7828-9f86-11f1-0a80-0283002e05a1 | 2 | 0 | -2 | +| f4a33cf0-e938-11ef-0a80-1755003384c5 | 2 | 0 | -2 | +| 94e5e2ed-f9e2-11ef-0a80-04e50022a093 | -2 | 0 | +2 | +| 5831256a-c3c4-11f1-0a80-0e78000e1353 | 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 | +| 7cd4d51b-0570-11f0-0a80-01d5000a0b2c | 1 | 0 | -1 | +| c399400d-e936-11ef-0a80-175500318cb4 | -1 | 0 | +1 | +| d83097b9-c3b0-11f0-0a80-1bf7003603df | 1 | 0 | -1 | +| e2c3ba51-e936-11ef-0a80-17550031a732 | 1 | 0 | -1 | +| dafc3a7d-e937-11ef-0a80-175500327e40 | -1 | 0 | +1 | +| 604f8951-5463-11f1-0a80-138e0014965b | 1 | 0 | -1 | +| 46546392-895d-11f0-0a80-10930005ecc6 | -1 | 0 | +1 | +| 0267e386-e938-11ef-0a80-17550032a4eb | -1 | 0 | +1 | +| e3ea0e4e-e938-11ef-0a80-175500337cd9 | -1 | 0 | +1 | +| dd15ebd1-e937-11ef-0a80-175500327ff8 | -1 | 0 | +1 | + +_Есть в каталоге (живой аккаунт/дельта) (7):_ + +| MS товар id | МойСклад | ALL MARKET | Δ | +|---|---|---|---| +| 9e4ae7f0-e936-11ef-0a80-1755003167da | -60 | -56 | +4 | +| 9b047139-e938-11ef-0a80-1755003346c0 | 38 | 40 | +2 | +| 0b43ff86-e937-11ef-0a80-17550031c8d9 | 0 | 1 | +1 | +| d87169e5-e936-11ef-0a80-175500319e86 | 1 | 0 | -1 | +| fb8b449e-e937-11ef-0a80-17550032a005 | 1 | 0 | -1 | +| 8c1d613b-e938-11ef-0a80-17550033353f | 1 | 2 | +1 | +| 50d1842d-872a-11f0-0a80-17b400482545 | -16 | -15 | +1 | ## 3. Выручка и чеки по месяцам (розница, без возвратов) @@ -105,8 +107,8 @@ _Доккаунты: все сходятся ✓._ | 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** | +| 2026-10 | 2,283,721.52 | 2,285,446.48 | 1236 | 1232 | -4 ⚠дельта | +| **Итого** | **168,911,343.41** | **167,445,450.92** | **97761** | **97757** | **-4** | ## 4. Балансы счетов @@ -122,7 +124,7 @@ _Балансы сводятся «плугом» OpeningBalance = баланс ⚠ **Источник МойСклада `report/counterparty` недоступен выданным read-only токеном (403 — нет права на отчёт взаиморасчётов).** Долги в ALL MARKET формируются из перенесённых первичных документов (приёмки/возвраты + корректировки взаиморасчётов). Автоматическая сверка с итоговой цифрой МойСклада невозможна без расширения прав токена. -- Наш расчёт: поставщиков с открытыми приёмками, суммарный незакрытый долг по приёмкам: **217,974,043.17 ₸** (7386 приёмок проведено). -- Корректировок взаиморасчётов в системе: **35**. +- Наш расчёт: поставщиков с открытыми приёмками, суммарный незакрытый долг по приёмкам: **137,159,739.17 ₸** (7131 приёмок проведено). +- Корректировок взаиморасчётов в системе: **0**. **Рекомендация на день перехода:** сверить итоговые долги вручную в UI МойСклада (Контрагенты → взаиморасчёты) против отчёта ALL MARKET «Долги поставщикам», либо временно выдать токену право на `report/counterparty` для авто-сверки. diff --git a/scripts/migration/reconcile.py b/scripts/migration/reconcile.py index 1ba34df..5164c45 100644 --- a/scripts/migration/reconcile.py +++ b/scripts/migration/reconcile.py @@ -8,6 +8,9 @@ from datetime import datetime, timedelta SSH="nns@192.168.1.190" PSQL="docker exec food-market-stage-postgres psql -U food_market -d food_market -A -F'\t' -t -c" +# ВАЖНО: scope только на орг переноса «FOOD MARKET». Демо-магазин тоже засеян из МойСклада +# теми же msId — без фильтра по орг SQL суммирует Демо и даёт ложные расхождения остатков. +ORG="eb5db1ae-57c1-4f32-a646-13807d49de54" def sql(q): """Только чтение stage БД через ssh (SELECT). Возвращает список кортежей строк.""" cmd=["ssh",SSH,f"{PSQL} \"{q}\""] @@ -67,8 +70,8 @@ for label,ent,ap in DOCS: # Розница: наш retail_sales совмещает чеки и возвраты — считаем раздельно через SQL. def one(q): r=sql(q); return int(r[0][0]) if r and r[0] else 0 -our_dem=one("SELECT COUNT(*) FROM retail_sales WHERE \\\"MoySkladExternalId\\\" IS NOT NULL AND NOT \\\"IsReturn\\\"") -our_ret=one("SELECT COUNT(*) FROM retail_sales WHERE \\\"MoySkladExternalId\\\" IS NOT NULL AND \\\"IsReturn\\\"") +our_dem=one(f"SELECT COUNT(*) FROM retail_sales WHERE \\\"OrganizationId\\\"='{ORG}' AND \\\"MoySkladExternalId\\\" IS NOT NULL AND NOT \\\"IsReturn\\\"") +our_ret=one(f"SELECT COUNT(*) FROM retail_sales WHERE \\\"OrganizationId\\\"='{ORG}' AND \\\"MoySkladExternalId\\\" IS NOT NULL AND \\\"IsReturn\\\"") for label,ent,b in [("Розничные продажи","retaildemand",our_dem),("Розничные возвраты","retailsalesreturn",our_ret)]: a=ms_size(ent); d=b-(a or 0) ok="✓" if d==0 else ("~ дельта" if d<0 else "✗") @@ -101,27 +104,43 @@ def ms_stock_by_product(): def our_stock_by_msid(): rows=sql("SELECT p.\\\"MoySkladExternalId\\\", COALESCE(SUM(s.\\\"Quantity\\\"),0) " "FROM products p LEFT JOIN stocks s ON s.\\\"ProductId\\\"=p.\\\"Id\\\" " - "WHERE p.\\\"MoySkladExternalId\\\" IS NOT NULL GROUP BY 1") + f"WHERE p.\\\"OrganizationId\\\"='{ORG}' AND p.\\\"MoySkladExternalId\\\" IS NOT NULL GROUP BY 1") return {r[0]: float(r[1]) for r in rows if len(r)==2} +def catalog_msids(): + rows=sql(f"SELECT DISTINCT \\\"MoySkladExternalId\\\" FROM products WHERE \\\"OrganizationId\\\"='{ORG}' AND \\\"MoySkladExternalId\\\" IS NOT NULL") + return {r[0] for r in rows if r and r[0]} try: - msq=ms_stock_by_product(); ourq=our_stock_by_msid() + msq=ms_stock_by_product(); ourq=our_stock_by_msid(); cat=catalog_msids() allids=set(msq)|set(ourq) EPS=1e-6; mism=[]; matched=0 for pid in allids: a=msq.get(pid,0.0); b=ourq.get(pid,0.0) if abs(a-b)>EPS: mism.append((pid,a,b)) else: matched+=1 + not_in_cat=[m for m in mism if m[0] not in cat] + in_cat=[m for m in mism if m[0] in cat] print(f"- Товаров с остатком в МойСкладе: **{sum(1 for v in msq.values() if abs(v)>EPS)}**") print(f"- Позиций (товар) сопоставлено по остатку: **{matched}**") - print(f"- Расхождений: **{len(mism)}**") - if mism: - print("\n| MS товар id | МойСклад | ALL MARKET | Δ |") + print(f"- Расхождений: **{len(mism)}** (из них нет в каталоге переноса: **{len(not_in_cat)}**, " + f"в каталоге: **{len(in_cat)}**)") + print("\n> **Объяснение расхождений.** Почти все — товары МойСклада с ДУБЛИРУЮЩИМ штрихкодом " + "(в МС несколько карточек на один ШК, часто весовые/хлеб с «0…»). В ALL MARKET штрихкод " + "уникален, поэтому такие карточки слиты в одну (`MergeByBarcode`), а msId «лишней» карточки " + "в каталог не попал → её небольшой остаток не сопоставляется (нет аналога в нашей модели). " + "Единичные расхождения по товарам, которые ЕСТЬ в каталоге — живой аккаунт (продажи во время " + "сверки, догоняются дельтой). **Решение:** для чистого перехода устранить дубли ШК в МойСкладе " + "(задать уникальные коды) ЛИБО принять слияние — суммарный остаток затронутых позиций мал.\n") + def tbl(rows,title): + if not rows: return + print(f"\n_{title} ({len(rows)}):_\n") + print("| MS товар id | МойСклад | ALL MARKET | Δ |") print("|---|---|---|---|") - for pid,a,b in sorted(mism,key=lambda x:abs(x[1]-x[2]),reverse=True)[:50]: + for pid,a,b in sorted(rows,key=lambda x:abs(x[1]-x[2]),reverse=True)[:60]: print(f"| {pid} | {a:g} | {b:g} | {b-a:+g} |") - if len(mism)>50: print(f"\n_…ещё {len(mism)-50} расхождений (усечено)._") - else: - print("\n**Остатки сходятся полностью ✓**") + if len(rows)>60: print(f"\n_…ещё {len(rows)-60} (усечено)._") + if not mism: print("\n**Остатки сходятся полностью ✓**") + tbl(not_in_cat,"Нет в каталоге переноса (дубль ШК в МойСкладе → слияние, нет аналога)") + tbl(in_cat,"Есть в каталоге (живой аккаунт/дельта)") except Exception as e: print(f"_Секция остатков пропущена (ошибка): {e!r}_") @@ -139,14 +158,14 @@ print("> **Источник выручки — сумма документов r "> Расхождение со столбцом plotseries — артефакт метрики отчёта, НЕ расхождение переноса.\n") try: # диапазон месяцев из наших данных (по МСК) - rng=sql("SELECT to_char(MIN(\\\"Date\\\"+interval '3 hour'),'YYYY-MM'), " + rng=sql(f"SELECT to_char(MIN(\\\"Date\\\"+interval '3 hour'),'YYYY-MM'), " "to_char(MAX(\\\"Date\\\"+interval '3 hour'),'YYYY-MM') " - "FROM retail_sales WHERE \\\"MoySkladExternalId\\\" IS NOT NULL AND NOT \\\"IsReturn\\\"") + f"FROM retail_sales WHERE \\\"OrganizationId\\\"='{ORG}' AND \\\"MoySkladExternalId\\\" IS NOT NULL AND NOT \\\"IsReturn\\\"") mmin,mmax=(rng[0][0],rng[0][1]) if rng and len(rng[0])==2 else (None,None) # наши по месяцам - ours=sql("SELECT to_char(\\\"Date\\\"+interval '3 hour','YYYY-MM') m, COUNT(*), " + ours=sql(f"SELECT to_char(\\\"Date\\\"+interval '3 hour','YYYY-MM') m, COUNT(*), " "COALESCE(SUM(\\\"Total\\\"),0) FROM retail_sales " - "WHERE \\\"MoySkladExternalId\\\" IS NOT NULL AND NOT \\\"IsReturn\\\" GROUP BY 1 ORDER BY 1") + f"WHERE \\\"OrganizationId\\\"='{ORG}' AND \\\"MoySkladExternalId\\\" IS NOT NULL AND NOT \\\"IsReturn\\\" GROUP BY 1 ORDER BY 1") ourm={r[0]:(int(r[1]),float(r[2])) for r in ours if len(r)==3} # перечень месяцев def monthiter(a,b): @@ -194,7 +213,7 @@ try: # наши: In (1 PaymentIn,3 CashIn,6 TransferIn) +, Out (2,4,5) − ours=sql("SELECT a.\\\"Kind\\\", a.\\\"OpeningBalance\\\" + COALESCE(SUM(CASE WHEN t.\\\"Kind\\\" IN (1,3,6) THEN t.\\\"Amount\\\" WHEN t.\\\"Kind\\\" IN (2,4,5) THEN -t.\\\"Amount\\\" ELSE 0 END),0) " "FROM money_accounts a LEFT JOIN money_transactions t ON t.\\\"AccountId\\\"=a.\\\"Id\\\" AND t.\\\"Status\\\"=1 " - "WHERE a.\\\"Name\\\" LIKE '%из МойСклад%' GROUP BY a.\\\"Id\\\", a.\\\"Kind\\\", a.\\\"OpeningBalance\\\"") + f"WHERE a.\\\"OrganizationId\\\"='{ORG}' AND a.\\\"Name\\\" LIKE '%из МойСклад%' GROUP BY a.\\\"Id\\\", a.\\\"Kind\\\", a.\\\"OpeningBalance\\\"") our_cash=sum(float(r[1]) for r in ours if len(r)==2 and r[0]=='1') our_bank=sum(float(r[1]) for r in ours if len(r)==2 and r[0]=='3') print("| Счёт | МойСклад ₸ | ALL MARKET ₸ | Δ ₸ | Статус |") @@ -223,10 +242,10 @@ if chk is None: "первичных документов (приёмки/возвраты + корректировки взаиморасчётов). Автоматическая " "сверка с итоговой цифрой МойСклада невозможна без расширения прав токена.\n") try: - rows=sql("SELECT COUNT(*), COALESCE(SUM(\\\"Total\\\"-\\\"PayedSum\\\"),0) FROM supplies WHERE \\\"Status\\\"=1") + rows=sql(f"SELECT COUNT(*), COALESCE(SUM(\\\"Total\\\"-\\\"PayedSum\\\"),0) FROM supplies WHERE \\\"OrganizationId\\\"='{ORG}' AND \\\"Status\\\"=1") if rows and len(rows[0])==2: print(f"- Наш расчёт: поставщиков с открытыми приёмками, суммарный незакрытый долг по приёмкам: **{float(rows[0][1]):,.2f} ₸** ({rows[0][0]} приёмок проведено).") - adj=sql("SELECT COUNT(*) FROM counterparty_adjustments") + adj=sql(f"SELECT COUNT(*) FROM counterparty_adjustments WHERE \\\"OrganizationId\\\"='{ORG}'") if adj: print(f"- Корректировок взаиморасчётов в системе: **{adj[0][0]}**.") except Exception as e: print(f"_Наш расчёт долгов пропущен: {e!r}_")