| | | 1 | | namespace Services.AiAssistant; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// Сжатая база знаний BitrixStock для промпта LLM (этапы 1–5, правила остатков и цен). |
| | | 5 | | /// Источник: AppRepository/BitrixStockServices/README.md, UI /Studio2/BitrixStock?tab=sync |
| | | 6 | | /// </summary> |
| | | 7 | | public static class BitrixStockSyncAiKnowledge |
| | | 8 | | { |
| | | 9 | | public static string GetPromptSection() => |
| | 8 | 10 | | """ |
| | 8 | 11 | | ═══ BITRIXSTOCK — СИНХРОНИЗАЦИЯ OZON И 1С (/Studio2/BitrixStock?tab=sync) ═══ |
| | 8 | 12 | | |
| | 8 | 13 | | Цепочка этапов (PresetStageSettings в пресете): |
| | 8 | 14 | | • Этап 1 — отчёт: сравнение остатков 1С (WarehouseStockSnapshot) и Ozon → строки с Action. |
| | 8 | 15 | | • Этап 2 — накат остатков в Ozon (POST /v2/products/stocks) по строкам Push/Decrease/Clear. |
| | 8 | 16 | | • Этап 3 — очистка лишних FBS-складов (CleanupExtra): обнуление складов Ozon, не входящих в «защищённые». |
| | 8 | 17 | | • Этап 4 — накат цен: PriceOptimizationManager → ImportProductPrices (пакеты до 1000). |
| | 8 | 18 | | • Этап 5 — сироты и партии [N]: фаза 5a (batch из ozon_products) → фаза 5b (сироты non-batch). |
| | 8 | 19 | | |
| | 8 | 20 | | Правила Action на этапе 1 (определяют целевой остаток для этапа 2): |
| | 8 | 21 | | • Push — остаток Ozon < 1С, цель = OneCQty (с учётом резерва ReservedQty и суммы LinkStores B1+B2→B12). |
| | 8 | 22 | | • Decrease — Ozon > 1С, цель = OneCQty. |
| | 8 | 23 | | • Clear — 1С=0, обнулить Ozon. |
| | 8 | 24 | | • Match — уже совпадает, накат не нужен. |
| | 8 | 25 | | • NotFound / FbsNoWarehouse / NotForSale — особые случаи (нет в Ozon, нет маппинга склада, снят с продажи). |
| | 8 | 26 | | • CleanupExtra — этап 3: лишний FBS-склад обнулён. |
| | 8 | 27 | | • BatchVariant / BatchVariantDisabledLink — этап 5a: партия [N], floor(1С/N) упаковок; disabled-связь → 0. |
| | 8 | 28 | | |
| | 8 | 29 | | Маппинг складов: WarehouseMapping связывает склад 1С (OneCStore) с OzonWarehouseName. |
| | 8 | 30 | | Коллизия B1+B2→один Ozon: остатки 1С СУММИРУЮТСЯ (GetLinkedOneCQuantity). |
| | 8 | 31 | | FBS-fallback (SDS): если Ozon не отдаёт разбивку по складам — остаток на все маппинги клиента. |
| | 8 | 32 | | |
| | 8 | 33 | | Партии [N] в OfferId (ARTICLE=BRAND[N], N≥2): |
| | 8 | 34 | | • На Ozon продаётся упаковка из N шт.; в 1С — штучный остаток. |
| | 8 | 35 | | • Целевой остаток на Ozon = floor(OneCQty / N). |
| | 8 | 36 | | • Цена партии = winnerPrice × N (PriceOptimizationManager, этап 5a или 2 с BatchMultiplier). |
| | 8 | 37 | | • [1] не считается партией. |
| | 8 | 38 | | |
| | 8 | 39 | | Этап 4 — цены: |
| | 8 | 40 | | • Снимок цен Ozon сохраняется на этапе 1 (BitrixStockSyncReportOzonPrices). |
| | 8 | 41 | | • Победитель: PriceOptimizationManager.Calculate (C0 из 1С, комиссии, индекс цены). |
| | 8 | 42 | | • Unchanged — если отличие < 1 ₽; Error — ошибка Ozon после VAT-ретрая. |
| | 8 | 43 | | • Индексы: SUPER/GREEN (выгодный), YELLOW (умеренный), RED (невыгодный), WITHOUT_INDEX. |
| | 8 | 44 | | |
| | 8 | 45 | | Этап 5 — сироты (5b): |
| | 8 | 46 | | • Сирота = остаток на Ozon (offer_id+склад) без покрытия в отчёте/снэпшоте → OzonZero. |
| | 8 | 47 | | • Сопоставление: exact offer_id, normalized [N], артикул+склад. |
| | 8 | 48 | | |
| | 8 | 49 | | Мульти-клиент K1/K2: при коллизии одного товара у двух Ozon-клиентов приоритет у K1. |
| | 8 | 50 | | |
| | 8 | 51 | | КОЛИЧЕСТВА — НЕ ПУТАТЬ ИСТОЧНИКИ: |
| | 8 | 52 | | • Таблица WarehouseStock и карта движений 1С OData могут показывать ДРОБНЫЕ единицы (напр. 9.7). |
| | 8 | 53 | | • Отчёт BitrixStock: OneCQty и OzonQty — ЦЕЛЫЕ (int); при построении отчёта остаток 1С округляется Math.Round. |
| | 8 | 54 | | • Накат в Ozon API (/v2/products/stocks) принимает только целые — цель из OneCQty отчёта, не дробь из таблицы. |
| | 8 | 55 | | • НЕ выдумывай «округление вверх до 10», если в строке отчёта OneCQty=9 — опирайся на поля отчёта. |
| | 8 | 56 | | • Если в таблице 1С дробь, а в отчёте целое — явно раздели два источника и укажи правило Round. |
| | 8 | 57 | | |
| | 8 | 58 | | ЦЕНЫ — НЕ ВЫДУМЫВАТЬ ФОРМУЛЫ: |
| | 8 | 59 | | • Бери C0, TruePrice, пуши этапа 4 и индекс ТОЛЬКО из строк отчёта и хронологии цен. |
| | 8 | 60 | | • Не придумывай «CostPrice × 1.25» — расчёт делает PriceOptimizationManager; цифры из данных. |
| | 8 | 61 | | |
| | 8 | 62 | | КАК ОТВЕЧАТЬ НА «ПОЧЕМУ ТАКАЯ ЦЕНА / КОЛИЧЕСТВО»: |
| | 8 | 63 | | 1. Назови склад 1С и склад Ozon из строки отчёта. |
| | 8 | 64 | | 2. Укажи OneCQty, OzonQty, Action и правило этапа (см. выше). |
| | 8 | 65 | | 3. Если BatchMultiplier>1 или OfferId с [N] — объясни пересчёт floor(1С/N). |
| | 8 | 66 | | 4. Для цены — индекс, C0, пуш этапа 4/5a (было→стало) из хронологии. |
| | 8 | 67 | | 5. Сошлись на ВСЕ отчёты по артикулу — не только последний. |
| | 8 | 68 | | 6. В конце предложи «Актуально на Ozon» — live-данные из Ozon Seller API. |
| | 8 | 69 | | 7. Упоминай статусы в кавычках «…» для подсветки в чате. |
| | 8 | 70 | | """; |
| | | 71 | | } |