< Summary

Information
Class: AspNetCoreGeneratedDocument.Areas_Studio2_Views_BitrixStock__SyncReportCard
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/BitrixStock/_SyncReportCard.cshtml
Line coverage
0%
Covered lines: 0
Uncovered lines: 23
Coverable lines: 23
Total lines: 1076
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 46
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ExecuteAsync()0%2756520%
<ExecuteAsync()100%210%
<ExecuteAsync()100%210%
<ExecuteAsync()100%210%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/BitrixStock/_SyncReportCard.cshtml

#LineLine coverage
 1@using Services.BitrixStockServices
 2@model OzonOrdersWeb.Areas.Studio2.ViewModels.Bitrix.RemainingStockViewModel
 3@{
 04    var defaultStores = BitrixStockClearFilterTemplate.StoreTitles.ToHashSet(StringComparer.OrdinalIgnoreCase);
 05    var syncStoreList = Model.ActiveStores is { Count: > 0 }
 06        ? Model.ActiveStores
 07        : BitrixStockClearFilterTemplate.StoreTitles.ToList();
 08    var reserveStatusOptions = ViewBag.ReserveStatusOptions as List<ReserveStatusOptionDto> ?? new();
 9}
 10
 11<div class="bsx-card" id="bitrixSyncReportCard">
 12    <div class="bsx-card-header card-header-sync d-flex justify-content-between align-items-center flex-wrap gap-2">
 13        <div class="d-flex align-items-center gap-3">
 14            <strong class="small"><i class="fas fa-exchange-alt me-1 text-primary"></i>Синхронизация остатков 1С → Ozon<
 15            <a href="@Url.Action("Index", "JobLogs", new { area = "Studio2", tab = "stocksync" })"
 16               class="small text-muted text-decoration-none" target="_blank">
 17                <i class="fas fa-history me-1"></i>История синхр.
 18            </a>
 19        </div>
 20        <div>
 21            <span class="sync-action-badge sync-row-push">↑ Накатить</span>
 22            <span class="sync-action-badge sync-row-decrease ms-1">↓ Уменьшить</span>
 23            <span class="sync-action-badge sync-row-clear ms-1">⊘ Обнулить</span>
 24            <span class="sync-action-badge sync-row-match ms-1">✓ Совпадает</span>
 25            <span class="sync-action-badge sync-row-cleanup-extra ms-1">⓪₃ Этап 3</span>
 26        </div>
 27    </div>
 28
 29    <div class="card-body p-3">
 30
 31        <div class="sync-section-title mb-1"><i class="fas fa-bookmark me-1"></i>Пресеты
 32            <button type="button" class="btn btn-link btn-sm p-0 ms-2 small fw-normal" id="bitrixSyncPresetSaveBtn">
 33                + Сохранить текущие настройки
 34            </button>
 35        </div>
 36        <div id="bitrixSyncPresetsContainer" class="mb-3">
 37            <span class="small text-muted">Загрузка…</span>
 38        </div>
 39
 40        <hr class="my-2"/>
 41        <div class="sync-section-title"><i class="fas fa-file-alt me-1"></i>Новый отчёт</div>
 42        <div class="row g-2 mb-2">
 43            <div class="col-md-4">
 44                <label class="form-label small mb-0">Артикул <span class="text-muted">(необязательно)</span></label>
 45                <input type="text" class="form-control form-control-sm" id="bitrixSyncStartArticle" placeholder="огранич
 46            </div>
 47            <div class="col-md-3 d-flex align-items-end">
 48                <button type="button" class="btn btn-primary btn-sm w-100" id="bitrixSyncReportStartBtn">
 49                    <i class="fas fa-file-alt me-1"></i>Сформировать
 50                </button>
 51            </div>
 52            <div class="col-md-auto d-flex align-items-end">
 53                <button type="button" class="btn btn-outline-secondary btn-sm" id="bitrixReserveMapBtn"
 54                        title="Показать заказы резерва по выбранным статусам и складам">
 55                    <i class="fas fa-lock me-1"></i>Карта резерва
 56                </button>
 57            </div>
 58        </div>
 59
 60        <div id="bitrixReserveMapPanel" class="d-none mb-2 border rounded p-2 bg-light">
 61            <div class="d-flex justify-content-between align-items-center mb-2">
 62                <span class="small fw-semibold text-secondary"><i class="fas fa-lock me-1"></i>Резерв заказов (настраива
 63                <div class="d-flex gap-1 align-items-center">
 64                    <a id="bitrixReserveMapExcelLink" href="#" class="btn btn-outline-success btn-sm" target="_blank">
 65                        <i class="fas fa-file-excel me-1"></i>Скачать Excel
 66                    </a>
 67                    <button type="button" class="btn btn-outline-secondary btn-sm" id="bitrixReserveMapRefreshBtn" title
 68                        <i class="fas fa-sync-alt"></i>
 69                    </button>
 70                    <button type="button" class="btn-close btn-sm" id="bitrixReserveMapCloseBtn" aria-label="Закрыть"></
 71                </div>
 72            </div>
 73            <div class="mb-2">
 74                <label class="form-label small mb-1">Статусы резерва</label>
 75                <div id="bitrixReserveStatusList" class="d-flex flex-wrap gap-2">
 076                    @foreach (var st in reserveStatusOptions)
 77                    {
 78                        <div class="form-check form-check-inline mb-0">
 79                            <input class="form-check-input bitrix-reserve-status-cb" type="checkbox"
 080                                   value="@st.Id" id="bitrixReserveStatus_@st.Id" @(st.IsDefault ? "checked" : "") />
 81                            <label class="form-check-label small" for="bitrixReserveStatus_@st.Id">
 082                                <span class="badge border" style="background:@st.Color;color:#212529">@st.Name</span>
 83                            </label>
 84                        </div>
 85                    }
 86                </div>
 87            </div>
 88            <div id="bitrixReserveMapSummary" class="small text-muted mb-1"></div>
 89            <div class="bsx-grid-toolbar mb-1">
 90                <div class="bsx-grid-search">
 91                    <i class="fas fa-search"></i>
 92                    <input type="search" id="bitrixReserveMapSearch" placeholder="Артикул, склад, заказ…" autocomplete="
 93                </div>
 94                <span class="bsx-grid-count" id="bitrixReserveMapCount"></span>
 95            </div>
 96            <div id="bitrixReserveMapTable" class="bsx-grid bsx-grid--modal"></div>
 97        </div>
 98        <div class="mb-2">
 99            <div class="d-flex justify-content-between align-items-center mb-1">
 100                <label class="form-label small mb-0">Клиенты Ozon <span class="text-muted">(порядок = приоритет)</span><
 101                <div class="d-flex gap-1 align-items-center">
 102                    <select class="form-select form-select-sm" id="bitrixSyncClientAddSelect" style="width:auto">
 103                        <option value="">+ добавить клиента</option>
 0104                        @foreach (var c in Model.OzonClients)
 105                        {
 0106                            <option value="@c.Id" data-name="@c.Name">@c.Name</option>
 107                        }
 108                    </select>
 109                </div>
 110            </div>
 111            <div id="bitrixSyncClientList" class="d-flex flex-wrap gap-1 align-items-center">
 112                <span class="small text-muted" id="bitrixSyncClientListEmpty">Нет выбранных клиентов</span>
 113            </div>
 114        </div>
 115        <div class="mb-2" id="bitrixSyncStartStoresSection">
 116            <label class="form-label small mb-1">Склады 1С</label>
 117            <div class="d-flex flex-wrap gap-1 align-items-center" id="bitrixSyncReportStores">
 0118                @foreach (var store in syncStoreList)
 119                {
 120                    <div class="form-check form-check-inline mb-0">
 121                        <input class="form-check-input bitrix-sync-start-store-cb" type="checkbox"
 122                               value="@store" id="bitrixSyncStartStore_@store"
 0123                               @(defaultStores.Contains(store) ? "checked" : null)/>
 0124                        <label class="form-check-label small" for="bitrixSyncStartStore_@store">@store</label>
 125                    </div>
 126                }
 127                <button type="button" class="btn btn-link btn-sm p-0 ms-1 small" id="bitrixSyncStartStoresAll">все</butt
 128                <button type="button" class="btn btn-link btn-sm p-0 small" id="bitrixSyncStartStoresNone">снять</button
 129            </div>
 130        </div>
 131        <div id="bitrixSyncReportProgressWrap" class="d-none mb-2">
 132            <div class="d-flex justify-content-between small mb-1">
 133                <span id="bitrixSyncReportProgressText">Подготовка…</span>
 134                <span id="bitrixSyncReportElapsed" class="text-muted">00:00</span>
 135            </div>
 136            <div class="progress mb-1" style="height: 1rem;">
 137                <div class="progress-bar progress-bar-striped progress-bar-animated" id="bitrixSyncReportProgressBar"
 138                     role="progressbar" style="width: 0%">0%</div>
 139            </div>
 140            <div id="bitrixSyncReportLog"></div>
 141        </div>
 142
 143        <hr class="my-2"/>
 144
 145
 146        <div class="bsx-grid-toolbar mb-2">
 147            <div class="sync-section-title mb-0"><i class="fas fa-history me-1"></i>История обработок</div>
 148            <div class="bsx-grid-search">
 149                <i class="fas fa-search"></i>
 150                <input type="search" id="bitrixSyncRunsSearch" placeholder="Поиск: клиент, склад, ID…" autocomplete="off
 151            </div>
 152            <select class="form-select form-select-sm bsx-grid-select" id="bitrixSyncRunsStatusFilter" title="Статус">
 153                <option value="">Все статусы</option>
 154                <option value="Running">Выполняется</option>
 155                <option value="Completed">Завершён</option>
 156                <option value="Failed">Ошибка</option>
 157            </select>
 158            <span class="bsx-grid-count" title="Показано / всего"><span id="bitrixSyncRunsCount"></span> <span class="bs
 159            <div class="bsx-grid-toolbar-end">
 160                <button type="button" class="btn btn-sm btn-outline-secondary" id="bitrixSyncRunsRefreshBtn" title="Обно
 161                <button type="button" class="btn btn-sm btn-outline-secondary" id="bitrixSyncRunsColumnsBtn" data-bsx-po
 162            </div>
 163        </div>
 164        <div id="bitrixSyncRunsTable" class="bsx-grid bsx-grid--runs"></div>
 165        <div class="bsx-grid-hint">Двойной клик по строке — открыть отчёт. Сортировка — клик по заголовку (Shift — по не
 166
 167    </div>
 168</div>
 169
 170<section id="bitrixSyncReportView" class="bsx-report-view" hidden aria-label="Результаты отчёта">
 171            <div id="syncArchiveToast" class="toast align-items-center text-white border-0" role="alert" aria-live="asse
 172                <div class="d-flex">
 173                    <div class="toast-body" id="syncArchiveToastBody"></div>
 174                    <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button
 175                </div>
 176            </div>
 177    <header class="bsx-report-head">
 178        <button type="button" class="btn btn-sm btn-outline-secondary" id="bsxReportBackBtn" title="К истории обработок 
 179            <i class="fas fa-arrow-left me-1"></i>История
 180        </button>
 181        <h6 class="bsx-report-title">
 182            <i class="fas fa-table me-1 text-primary"></i>Результаты отчёта
 183            <code class="small ms-1" id="bitrixSyncCurrentReportId"></code>
 184        </h6>
 185        <div class="bsx-report-head-actions">
 186            <button type="button" class="btn btn-sm btn-success d-none" id="bitrixSyncReportExportBtn" title="Экспорт в 
 187                <i class="fas fa-file-excel me-1"></i>Excel
 188            </button>
 189            <button type="button" class="btn btn-sm btn-outline-info d-none" id="bitrixSyncReportSendEmailBtn" title="От
 190                <i class="fas fa-envelope me-1"></i>Почта
 191            </button>
 192            <button type="button" class="btn btn-sm btn-outline-secondary d-none" id="bitrixSyncReportDownloadLogBtn" ti
 193                <i class="fas fa-file-alt me-1"></i>Лог
 194            </button>
 195            <div class="btn-group btn-group-sm bsx-theme-switch" role="group" aria-label="Тема">
 196                <button type="button" class="btn btn-outline-secondary" data-bsx-theme="light" title="Светлая тема"><i c
 197                <button type="button" class="btn btn-outline-secondary" data-bsx-theme="dark" title="Тёмная тема"><i cla
 198                <button type="button" class="btn btn-outline-secondary" data-bsx-theme="auto" title="Как в системе"><i c
 199            </div>
 200        </div>
 201    </header>
 202
 203    <div id="bitrixSyncReportSummary" class="bsx-report-summary small"></div>
 204
 205    <div class="bsx-stages-row" id="bsxStagesRow">
 206        <div class="bsx-stages-row-head">
 207            <span class="bsx-stages-row-title small text-muted">
 208                <i class="fas fa-layer-group me-1"></i>Этапы 2-5
 209            </span>
 210            <button type="button"
 211                    class="btn btn-xs btn-outline-secondary"
 212                    id="bsxStagesRowToggle"
 213                    aria-expanded="false"
 214                    title="Свернуть/развернуть этапы">
 215                <i class="fas fa-chevron-down" id="bsxStagesRowToggleIcon"></i>
 216            </button>
 217        </div>
 218        <div class="bsx-stages-content" id="bsxStagesContent">
 219        <div class="sync-stage-card sync-stage-apply" id="bitrixSyncApplyCard">
 220            <div class="d-flex flex-wrap align-items-center gap-2">
 221                <strong class="small text-nowrap"><i class="fas fa-cloud-upload-alt me-1 text-warning"></i>Этап 2</stron
 222                <div class="d-flex gap-2">
 223                    <label class="form-check form-check-inline mb-0 small"><input class="form-check-input bitrix-sync-ap
 224                    <label class="form-check form-check-inline mb-0 small"><input class="form-check-input bitrix-sync-ap
 225                    <label class="form-check form-check-inline mb-0 small"><input class="form-check-input bitrix-sync-ap
 226                </div>
 227                <input type="number" class="form-control form-control-sm" id="bitrixSyncApplyMaxLines" min="1" placehold
 228                <label class="form-check mb-0 small"><input class="form-check-input" type="checkbox" id="bitrixSyncApply
 229                <button type="button" class="btn btn-sm btn-outline-danger" id="bitrixSyncOpenDisabledMappingsModalBtn" 
 230                <button type="button" class="btn btn-warning btn-sm" id="bitrixSyncApplyStartBtn" disabled><i class="fas
 231                <button type="button" class="btn btn-outline-secondary btn-sm" id="bitrixSyncApplyLogBtn" title="Журнал"
 232                <button type="button" class="btn btn-outline-info btn-sm" data-bs-toggle="modal" data-bs-target="#syncPr
 233            </div>
 234            <div id="bitrixSyncApplyProgressWrap" class="d-none mt-2">
 235                <div class="d-flex justify-content-between small mb-1">
 236                    <span id="bitrixSyncApplyProgressText">Подготовка…</span>
 237                    <span id="bitrixSyncApplyElapsed" class="text-muted">00:00</span>
 238                </div>
 239                <div class="progress mb-1" style="height:0.6rem">
 240                    <div class="progress-bar progress-bar-striped progress-bar-animated bg-warning" id="bitrixSyncApplyP
 241                </div>
 242                <div id="bitrixSyncApplyLog"></div>
 243            </div>
 244        </div>
 245
 246        <div class="sync-stage-card sync-stage-cleanup" id="bitrixSyncCleanupCard">
 247            <div class="d-flex flex-wrap align-items-center gap-2">
 248                <strong class="small text-nowrap"><i class="fas fa-broom me-1 text-info"></i>Этап 3</strong>
 249                <span class="small text-muted d-none d-xl-inline">Очистка лишних складов</span>
 250                <label class="form-check mb-0 small"><input class="form-check-input" type="checkbox" id="bitrixSyncClean
 251                <input type="number" class="form-control form-control-sm d-none" id="bitrixSyncCleanupMaxProducts" min="
 252                <button type="button" class="btn btn-outline-info btn-sm" id="bitrixSyncCleanupPreviewBtn" disabled><i c
 253                <button type="button" class="btn btn-info btn-sm" id="bitrixSyncCleanupStartBtn" disabled><i class="fas 
 254            </div>
 255            <div id="bitrixSyncCleanupPreviewSummary" class="small text-muted mt-1 d-none"></div>
 256            <div id="bitrixSyncCleanupProgressWrap" class="d-none mt-2">
 257                <div class="d-flex justify-content-between small mb-1">
 258                    <span id="bitrixSyncCleanupProgressText">Подготовка…</span>
 259                    <span id="bitrixSyncCleanupElapsed" class="text-muted">00:00</span>
 260                </div>
 261                <div class="progress mb-1" style="height:0.6rem">
 262                    <div class="progress-bar progress-bar-striped progress-bar-animated bg-info" id="bitrixSyncCleanupPr
 263                </div>
 264                <div id="bitrixSyncCleanupLog"></div>
 265            </div>
 266        </div>
 267
 268        <div class="sync-stage-card sync-stage-price-push" id="bitrixSyncPricePushCard">
 269            <div class="d-flex flex-wrap align-items-center gap-2">
 270                <strong class="small text-nowrap"><i class="fas fa-tag me-1 text-success"></i>Этап 4</strong>
 271                <span class="small text-muted d-none d-xl-inline">Накат цен</span>
 272                <label class="form-check mb-0 small"><input class="form-check-input" type="checkbox" id="bitrixSyncPrice
 273                <button type="button" class="btn btn-success btn-sm" id="bitrixSyncPricePushStartBtn" disabled><i class=
 274            </div>
 275            <div id="bitrixSyncPricePushProgressWrap" class="d-none mt-2">
 276                <div class="d-flex justify-content-between small mb-1">
 277                    <span id="bitrixSyncPricePushProgressText">Подготовка…</span>
 278                    <span id="bitrixSyncPricePushElapsed" class="text-muted">00:00</span>
 279                </div>
 280                <div class="progress mb-1" style="height:0.6rem">
 281                    <div class="progress-bar progress-bar-striped progress-bar-animated bg-success" id="bitrixSyncPriceP
 282                </div>
 283                <div id="bitrixSyncPricePushLog"></div>
 284            </div>
 285            <div id="bitrixSyncPriceIndexWrap" class="d-none mt-2 border-top pt-2">
 286                <div class="d-flex justify-content-between small mb-1">
 287                    <span id="bitrixSyncPriceIndexText" class="text-muted"><i class="fas fa-clock me-1"></i>Обновление и
 288                    <span id="bitrixSyncPriceIndexElapsed" class="text-muted">00:00</span>
 289                </div>
 290                <div class="progress mb-1" style="height:0.4rem">
 291                    <div class="progress-bar progress-bar-striped progress-bar-animated bg-info"
 292                         id="bitrixSyncPriceIndexBar" style="width:100%"></div>
 293                </div>
 294                <div id="bitrixSyncPriceIndexLog" class="small"></div>
 295            </div>
 296        </div>
 297
 298        @* ── Этап 5: Очистка сирот ──────────────────────────────────────────────── *@
 299        <div class="sync-stage-card sync-stage-orphan" id="bitrixSyncOrphanCard" style="display:none">
 300            <div class="d-flex flex-wrap align-items-center gap-2">
 301                <strong class="small text-nowrap" style="color:#7c4b00"><i class="fas fa-ghost me-1"></i>Этап 5</strong>
 302                <span class="small text-muted d-none d-xl-inline">Сироты Ozon · каталог OzonApi.ozon_products + остатки 
 303                <button type="button" class="btn btn-sm" style="border-color:#7c4b00;color:#7c4b00" id="bitrixSyncOrphan
 304                    <i class="fas fa-eye me-1"></i>Превью
 305                </button>
 306                <a id="bitrixSyncOrphanFilesBtn" class="btn btn-sm btn-outline-success d-none"
 307                   href="#" target="_blank" title="Скачать legacy xlsx-отчёт Ozon (только WarehouseStockReport)">
 308                    <i class="fas fa-file-excel me-1"></i>xlsx
 309                </a>
 310                <button type="button" class="btn btn-sm text-white" style="background:#7c4b00;border-color:#7c4b00" id="
 311                    <i class="fas fa-ghost me-1"></i>Обнулить
 312                </button>
 313            </div>
 314            <div id="bitrixSyncOrphanPreviewWrap" class="d-none mt-2">
 315                <div id="bitrixSyncOrphanFilesList" class="mb-1 small text-muted"></div>
 316                <div id="bitrixSyncOrphanFilesTable" class="bsx-grid bsx-grid--auto mb-2" hidden></div>
 317                <div id="bitrixSyncOrphanTable" class="bsx-grid bsx-grid--modal"></div>
 318                <div id="bitrixSyncOrphanSummary" class="small text-muted mt-1"></div>
 319            </div>
 320            <div id="bitrixSyncOrphanProgressWrap" class="d-none mt-2">
 321                <div class="d-flex justify-content-between small mb-1">
 322                    <span id="bitrixSyncOrphanProgressText">Подготовка…</span>
 323                    <span id="bitrixSyncOrphanElapsed" class="text-muted">00:00</span>
 324                </div>
 325                <div class="progress mb-1" style="height:0.6rem">
 326                    <div class="progress-bar progress-bar-striped progress-bar-animated" style="background:#7c4b00" id="
 327                </div>
 328                <div id="bitrixSyncOrphanLog"></div>
 329            </div>
 330        </div>
 331        </div>
 332    </div>
 333
 334
 335
 336    <div class="bsx-grid-toolbar bsx-report-toolbar">
 337        <div class="bsx-grid-search bsx-grid-search--wide">
 338            <i class="fas fa-search"></i>
 339            <input type="search" id="bsxReportSearch" placeholder="Артикул, offer_id, склад, примечание… (Ctrl+F)" autoc
 340        </div>
 341        <div class="bsx-quick" id="bsxReportQuick" role="group" aria-label="Быстрые фильтры">
 342            <button type="button" data-quick="requires" title="↑ накатить, ↓ уменьшить, ⊘ обнулить, ⚠ FBS">Требуют дейст
 343            <button type="button" data-quick="pending" title="Требуют действия и ещё не применены в Ozon">Не применено</
 344            <button type="button" data-quick="failed">Ошибки Ozon</button>
 345            <button type="button" data-quick="archived">Архивные</button>
 346            <button type="button" data-quick="orphans">Сироты</button>
 347            <button type="button" data-quick="redIndex">Невыгодный индекс</button>
 348            <button type="button" data-flag="onlyPriceChanged" title="Строки с накатом цены (этап 4)">Цена изменена</but
 349            <button type="button" data-flag="showZero" title="Показать строки, где 1С = 0 и Ozon = 0">0/0 остатки</butto
 350        </div>
 351        <div class="bsx-grid-toolbar-end">
 352            <span class="bsx-grid-count" id="bsxReportCount"></span>
 353            <button type="button" class="btn btn-sm btn-outline-secondary" id="bsxReportRefreshBtn" title="Перечитать ст
 354            <button type="button" class="btn btn-sm btn-outline-secondary" id="bsxReportColumnsBtn" data-bsx-popup-ancho
 355        </div>
 356    </div>
 357    <div class="bsx-chips" id="bsxReportChips" hidden></div>
 358
 359    <div class="bsx-report-body">
 360        <div id="bitrixSyncReportTable" class="bsx-grid bsx-grid--report"></div>
 361        <aside class="bsx-rowcard" id="bsxReportCard" hidden aria-label="Строка отчёта">
 362            <div class="bsx-rowcard-head">
 363                <div class="bsx-rowcard-title"></div>
 364                <button type="button" class="btn-close btn-sm" id="bsxReportCardClose" aria-label="Закрыть"></button>
 365            </div>
 366            <div class="bsx-rowcard-actions"></div>
 367            <div class="bsx-rowcard-body"></div>
 368        </aside>
 369    </div>
 370</section>
 371
 372<div class="modal fade" id="bitrixSyncExcelExportModal" tabindex="-1" aria-hidden="true">
 373    <div class="modal-dialog modal-dialog-centered">
 374        <div class="modal-content">
 375            <div class="modal-header py-2 border-bottom">
 376                <h6 class="modal-title mb-0">
 377                    <i class="fas fa-file-excel text-success me-1"></i>Формирование Excel
 378                </h6>
 379                <button type="button" class="btn-close btn-sm" data-bs-dismiss="modal" aria-label="Закрыть"></button>
 380            </div>
 381            <div class="modal-body py-3">
 382                <div class="small text-muted mb-2" id="bitrixSyncExcelExportStageText">Постановка задачи…</div>
 383                <div class="progress mb-2" style="height:0.8rem">
 384                    <div class="progress-bar progress-bar-striped progress-bar-animated bg-success"
 385                         id="bitrixSyncExcelExportProgressBar"
 386                         style="width:0%">0%</div>
 387                </div>
 388                <div class="small text-muted" id="bitrixSyncExcelExportDetails">Ожидание запуска…</div>
 389                <div class="mt-3 small">
 390                    <div class="d-flex align-items-center gap-2 mb-1" id="bitrixSyncExcelStepQueue" data-step="Queue"><i
 391                    <div class="d-flex align-items-center gap-2 mb-1" id="bitrixSyncExcelStepLoad" data-step="Load"><i c
 392                    <div class="d-flex align-items-center gap-2 mb-1" id="bitrixSyncExcelStepBuild" data-step="Build"><i
 393                    <div class="d-flex align-items-center gap-2" id="bitrixSyncExcelStepDone" data-step="Done"><i class=
 394                </div>
 395            </div>
 396            <div class="modal-footer py-2">
 397                <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Закрыть</button>
 398            </div>
 399        </div>
 400    </div>
 401</div>
 402
 403<div class="modal fade" id="bitrixSyncCleanupPreviewModal" tabindex="-1">
 404    <div class="modal-dialog modal-xl modal-dialog-scrollable">
 405        <div class="modal-content">
 406            <div class="modal-header py-2 border-bottom">
 407                <h6 class="modal-title"><i class="fas fa-broom me-1 text-info"></i>Этап 3 — превью очистки</h6>
 408                <button type="button" class="btn-close btn-sm" data-bs-dismiss="modal"></button>
 409            </div>
 410            <div class="modal-body p-0">
 411                <div class="px-3 py-2 border-bottom bg-light d-flex flex-wrap align-items-center gap-2">
 412                    <div class="input-group input-group-sm" style="max-width:280px">
 413                        <span class="input-group-text"><i class="fas fa-search"></i></span>
 414                        <input type="search" class="form-control" id="bitrixSyncCleanupModalSearch"
 415                               placeholder="Поиск по артикулу…" autocomplete="off"/>
 416                    </div>
 417                    <div class="form-check mb-0">
 418                        <input class="form-check-input" type="checkbox" id="bitrixSyncCleanupModalOnlyPending" checked/>
 419                        <label class="form-check-label small" for="bitrixSyncCleanupModalOnlyPending">Только новые</labe
 420                    </div>
 421                    <button type="button" class="btn btn-outline-secondary btn-sm py-0" id="bitrixSyncCleanupModalRefres
 422                            title="Обновить превью с сервера"><i class="fas fa-sync-alt"></i></button>
 423                    <span id="bitrixSyncCleanupModalSummary" class="small text-muted ms-auto"></span>
 424                </div>
 425                <div id="bitrixSyncCleanupModalTable" class="bsx-grid bsx-grid--modal-tall"></div>
 426                <div id="bitrixSyncCleanupModalLoader" class="text-center py-1 small text-muted d-none">
 427                    <span class="spinner-border spinner-border-sm me-1"></span>Загрузка…
 428                </div>
 429            </div>
 430            <div class="modal-footer py-2 border-top">
 431                <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Закрыть</button>
 432                <button type="button" class="btn btn-info btn-sm" id="bitrixSyncCleanupModalRunAllBtn">
 433                    <i class="fas fa-broom me-1"></i>Запустить всю очистку
 434                </button>
 435            </div>
 436        </div>
 437    </div>
 438</div>
 439
 440<div class="modal fade" id="bitrixSyncDisabledMappingsModal" tabindex="-1">
 441    <div class="modal-dialog modal-lg">
 442        <div class="modal-content">
 443            <div class="modal-header py-2 border-bottom">
 444                <h6 class="modal-title"><i class="fas fa-link me-1"></i>Управление связями складов</h6>
 445                <button type="button" class="btn-close btn-sm" data-bs-dismiss="modal"></button>
 446            </div>
 447            <div class="modal-body pb-3">
 448                <p class="small text-muted mb-3">Отключите связи, которые не должны участвовать в синхронизации. <strong
 449                <div id="bitrixSyncModalZeroWarning" class="d-none alert alert-warning py-2 px-3 mb-3 small border-0">
 450                    <div class="fw-semibold mb-1"><i class="fas fa-exclamation-triangle me-1"></i>Будет обнулено на Ozon
 451                    <ul id="bitrixSyncModalZeroWarningList" class="mb-0 ps-3"></ul>
 452                </div>
 453                <div id="bitrixSyncModalMappingsContainer" class="mb-3">
 454                    <span class="small text-muted">Загрузка связей…</span>
 455                </div>
 456                <div id="bitrixSyncModalMappingsError" class="text-danger small d-none"></div>
 457            </div>
 458            <div class="modal-footer py-2 border-top">
 459                <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Отмена</button>
 460                <button type="button" class="btn btn-primary btn-sm" id="bitrixSyncModalDisabledMappingsApplyBtn">Пересч
 461            </div>
 462        </div>
 463    </div>
 464</div>
 465
 466<div class="modal fade" id="syncApplyWarningModal" tabindex="-1">
 467    <div class="modal-dialog modal-dialog-centered">
 468        <div class="modal-content border-warning border-2">
 469            <div class="modal-header py-3 bg-warning bg-opacity-10 border-warning">
 470                <h6 class="modal-title fw-semibold"><i class="fas fa-exclamation-triangle text-warning me-2"></i>Внимани
 471                <button type="button" class="btn-close btn-sm" data-bs-dismiss="modal"></button>
 472            </div>
 473            <div class="modal-body py-3">
 474                <p class="mb-2">
 475                    <strong>Отключено <span id="syncApplyWarningCount" class="text-warning">0</span> связей складов</str
 476                </p>
 477                <ul id="syncApplyWarningMappingList" class="list-unstyled small mb-3 d-none ps-1"></ul>
 478                <p class="mb-2 text-muted small">
 479                    Остатки товаров с отключёнными связями будут <strong>обнулены на Ozon</strong> (установлены в&nbsp;0
 480                </p>
 481                <p class="mb-0 text-muted small">
 482                    После обнуления основная синхронизация продолжится без этих связей.
 483                </p>
 484            </div>
 485            <div class="modal-footer py-2 border-top">
 486                <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Отмена</button>
 487                <button type="button" class="btn btn-warning btn-sm" id="syncApplyWarningConfirmBtn">
 488                    <i class="fas fa-check me-1"></i>Продолжить синхронизацию
 489                </button>
 490            </div>
 491        </div>
 492    </div>
 493</div>
 494
 495<div class="modal fade" id="syncApplyArchiveModal" tabindex="-1">
 496    <div class="modal-dialog modal-dialog-centered modal-xl modal-dialog-scrollable">
 497        <div class="modal-content border-secondary border-2">
 498            <div class="modal-header py-2">
 499                <h6 class="modal-title fw-semibold"><i class="fas fa-box-archive me-2 text-secondary"></i>Архивные товар
 500                <button type="button" class="btn-close btn-sm" data-bs-dismiss="modal"></button>
 501            </div>
 502            <div class="modal-body py-3">
 503                <p class="mb-2">
 504                    В отчёте найдено <strong><span id="syncApplyArchiveCount">0</span> архивных товаров</strong>
 505                    <span id="syncApplyArchiveAutoNote" class="text-muted small d-none">(авто: <span id="syncApplyArchiv
 506                </p>
 507
 508                @* Превью: спиннер + таблица товаров *@
 509                <div id="syncArchivePreviewWrap" class="d-none mb-3">
 510                    <div id="syncArchivePreviewSpinner" class="text-muted small py-1"><span class="spinner-border spinne
 511                    <div id="syncArchivePreviewTable" class="d-none">
 512                        <div id="syncArchivePreviewGrid" class="bsx-grid bsx-grid--modal"></div>
 513                        <div id="syncArchivePreviewMore" class="text-muted small mt-1 d-none">
 514                            <i class="fas fa-info-circle me-1"></i>Показаны первые 500 строк
 515                        </div>
 516                    </div>
 517                </div>
 518
 519                @* Фильтр типа архива (только если есть оба типа) *@
 520                <div id="syncArchiveFilterWrap" class="d-none mb-3">
 521                    <p class="small text-muted mb-1">Какие товары разархивировать:</p>
 522                    <div class="d-flex gap-3 flex-wrap">
 523                        <div class="form-check">
 524                            <input class="form-check-input" type="radio" name="syncArchiveFilter" id="syncArchiveFilterA
 525                            <label class="form-check-label small" for="syncArchiveFilterAll">Все архивные</label>
 526                        </div>
 527                        <div class="form-check">
 528                            <input class="form-check-input" type="radio" name="syncArchiveFilter" id="syncArchiveFilterA
 529                            <label class="form-check-label small" for="syncArchiveFilterAuto"><span class="text-danger">
 530                        </div>
 531                        <div class="form-check">
 532                            <input class="form-check-input" type="radio" name="syncArchiveFilter" id="syncArchiveFilterM
 533                            <label class="form-check-label small" for="syncArchiveFilterManual">Только ручной архив</lab
 534                        </div>
 535                    </div>
 536                </div>
 537
 538                <p class="mb-0 small text-muted">Выберите как обработать при синхронизации:</p>
 539            </div>
 540            <div class="modal-footer py-2 gap-2 justify-content-start">
 541                <button type="button" class="btn btn-success btn-sm" id="syncApplyArchiveUnarchiveBtn">
 542                    <i class="fas fa-box-open me-1"></i>Разархивировать и накатить
 543                </button>
 544                <button type="button" class="btn btn-outline-secondary btn-sm" id="syncApplyArchiveSkipBtn">
 545                    Пропустить архивные
 546                </button>
 547                <button type="button" class="btn btn-outline-primary btn-sm" id="syncApplyArchiveIncludeBtn">
 548                    Накатить как обычные
 549                </button>
 550                <button type="button" class="btn btn-link btn-sm text-muted" data-bs-dismiss="modal">Отмена</button>
 551            </div>
 552        </div>
 553    </div>
 554</div>
 555
 556<div class="modal fade" id="syncPresetModal" tabindex="-1">
 557    <div class="modal-dialog modal-lg modal-dialog-scrollable">
 558        <div class="modal-content">
 559            <div class="modal-header py-2">
 560                <h6 class="modal-title" id="syncPresetModalTitle">Пресет</h6>
 561                <button type="button" class="btn-close btn-sm" data-bs-dismiss="modal"></button>
 562            </div>
 563            <div class="modal-body pb-2">
 564                <input type="hidden" id="syncPresetEditId"/>
 565                <div class="mb-3">
 566                    <label class="form-label small mb-0 fw-semibold">Название пресета</label>
 567                    <input type="text" class="form-control form-control-sm" id="syncPresetName" placeholder="Например: I
 568                </div>
 569
 570                @* ── Этап 1 ── *@
 571                <div class="border rounded mb-3">
 572                    <div class="px-3 py-2 rounded-top d-flex align-items-center gap-2" style="background:var(--color-pri
 573                        <span class="badge bg-white text-primary fw-bold" style="font-size:.75rem">1</span>
 574                        <span class="small fw-semibold text-white">Генерация отчёта</span>
 575                    </div>
 576                    <div class="p-3">
 577                        <div class="mb-2">
 578                            <div class="d-flex justify-content-between align-items-center mb-1">
 579                                <label class="form-label small mb-0">Клиенты Ozon <span class="text-muted">(порядок = пр
 580                                <select class="form-select form-select-sm" id="syncPresetClientAddSelect" style="width:a
 581                                    <option value="">+ добавить клиента</option>
 0582                                    @foreach (var c in Model.OzonClients)
 583                                    {
 0584                                        <option value="@c.Id" data-name="@c.Name">@c.Name</option>
 585                                    }
 586                                </select>
 587                            </div>
 588                            <div id="syncPresetClientList" class="d-flex flex-wrap gap-1 align-items-center">
 589                                <span class="small text-muted" id="syncPresetClientListEmpty">Нет выбранных клиентов</sp
 590                            </div>
 591                            @* скрытый select для loadPresetDisabledMappings *@
 592                            <select class="d-none" id="syncPresetClient">
 593                                <option value="">— —</option>
 0594                                @foreach (var c in Model.OzonClients)
 595                                {
 0596                                    <option value="@c.Id" data-name="@c.Name">@c.Name</option>
 597                                }
 598                            </select>
 599                        </div>
 600                        <div class="mb-2">
 601                            <label class="form-label small mb-0">Артикул <span class="text-muted">(необязательно)</span>
 602                            <input type="text" class="form-control form-control-sm" id="syncPresetArticle"/>
 603                        </div>
 604                        <div class="mb-2">
 605                            <label class="form-label small mb-1">Статусы резерва</label>
 606                            <div id="syncPresetReserveStatuses" class="d-flex flex-wrap gap-1">
 0607                                @foreach (var st in reserveStatusOptions)
 608                                {
 609                                    <div class="form-check form-check-inline mb-0">
 610                                        <input class="form-check-input sync-preset-reserve-status-cb" type="checkbox"
 0611                                               value="@st.Id" id="syncPresetReserveStatus_@st.Id" @(st.IsDefault ? "chec
 612                                        <label class="form-check-label small" for="syncPresetReserveStatus_@st.Id">
 0613                                            <span class="badge border" style="background:@st.Color;color:#212529">@st.Na
 614                                        </label>
 615                                    </div>
 616                                }
 617                            </div>
 618                            <div class="text-muted small mt-1">Эти статусы участвуют в расчёте резерва и в вычитании из 
 619                        </div>
 620                        <div class="mb-2">
 621                            <label class="form-label small mb-1">Склады 1С</label>
 622                            <div class="d-flex flex-wrap gap-1" id="syncPresetStores">
 0623                                @foreach (var store in syncStoreList)
 624                                {
 625                                    <div class="form-check form-check-inline mb-0">
 626                                        <input class="form-check-input sync-preset-store-cb" type="checkbox"
 627                                               value="@store" id="syncPresetStore_@store"/>
 0628                                        <label class="form-check-label small" for="syncPresetStore_@store">@store</label
 629                                    </div>
 630                                }
 631                            </div>
 632                            <div class="mt-1">
 633                                <button type="button" class="btn btn-link btn-sm p-0 small" id="syncPresetStoresAll">все
 634                                <button type="button" class="btn btn-link btn-sm p-0 small ms-2" id="syncPresetStoresNon
 635                            </div>
 636                        </div>
 637                        <div id="syncPresetDisabledMappingsSection" style="display: none;">
 638                            <label class="form-label small mb-1">Связи складов 1С ↔ Ozon <span class="text-muted">(снять
 639                            <div class="border rounded p-2 bg-light" style="max-height: 180px; overflow-y: auto;">
 640                                <div id="syncPresetDisabledMappingsContainer" class="small">
 641                                    <span class="text-muted">Загрузка...</span>
 642                                </div>
 643                            </div>
 644                        </div>
 645                    </div>
 646                </div>
 647
 648                @* ── Этап 2 ── *@
 649                <div class="border rounded mb-3">
 650                    <div class="px-3 py-2 rounded-top d-flex align-items-center gap-2" style="background:var(--color-suc
 651                        <span class="badge bg-white text-success fw-bold" style="font-size:.75rem">2</span>
 652                        <span class="small fw-semibold text-white">Накат на Ozon</span>
 653                        <span class="ms-auto">
 654                            <div class="form-check form-switch mb-0 d-flex align-items-center gap-1">
 655                                <input class="form-check-input" type="checkbox" role="switch" id="syncPresetAutoRunApply
 656                                <label class="form-check-label small text-white" for="syncPresetAutoRunApply">авто после
 657                            </div>
 658                        </span>
 659                    </div>
 660                    <div class="p-3">
 661                        <div class="mb-2">
 662                            <label class="form-label small mb-1">Действия</label>
 663                            <div class="d-flex flex-wrap gap-2">
 664                                <div class="form-check form-check-inline mb-0">
 665                                    <input class="form-check-input sync-preset-apply-action-cb" type="checkbox" value="P
 666                                    <label class="form-check-label small" for="syncPresetActionPush"><i class="fas fa-ar
 667                                </div>
 668                                <div class="form-check form-check-inline mb-0">
 669                                    <input class="form-check-input sync-preset-apply-action-cb" type="checkbox" value="D
 670                                    <label class="form-check-label small" for="syncPresetActionDecrease"><i class="fas f
 671                                </div>
 672                                <div class="form-check form-check-inline mb-0">
 673                                    <input class="form-check-input sync-preset-apply-action-cb" type="checkbox" value="C
 674                                    <label class="form-check-label small" for="syncPresetActionClear"><i class="fas fa-t
 675                                </div>
 676                            </div>
 677                        </div>
 678                        <div class="mb-2">
 679                            <label class="form-label small mb-1">Архивные товары</label>
 680                            <div class="d-flex flex-column gap-1">
 681                                <div class="form-check mb-0">
 682                                    <input class="form-check-input" type="radio" name="syncPresetArchivedMode" id="syncP
 683                                    <label class="form-check-label small" for="syncPresetArchivedSkip">Пропустить</label
 684                                </div>
 685                                <div class="form-check mb-0">
 686                                    <input class="form-check-input" type="radio" name="syncPresetArchivedMode" id="syncP
 687                                    <label class="form-check-label small" for="syncPresetArchivedInclude">Включить в нак
 688                                </div>
 689                                <div class="form-check mb-0">
 690                                    <input class="form-check-input" type="radio" name="syncPresetArchivedMode" id="syncP
 691                                    <label class="form-check-label small" for="syncPresetArchivedUnarchive">Разархивиров
 692                                </div>
 693                            </div>
 694                            <div id="syncPresetUnarchiveFilterSection" class="ms-3 mt-1 d-none">
 695                                <label class="form-label small mb-1 text-muted">Какие архивные разархивировать:</label>
 696                                <div class="d-flex gap-3">
 697                                    <div class="form-check mb-0">
 698                                        <input class="form-check-input" type="radio" name="syncPresetUnarchiveFilter" id
 699                                        <label class="form-check-label small" for="syncPresetUnarchiveAll">Все</label>
 700                                    </div>
 701                                    <div class="form-check mb-0">
 702                                        <input class="form-check-input" type="radio" name="syncPresetUnarchiveFilter" id
 703                                        <label class="form-check-label small" for="syncPresetUnarchiveAuto">Только авто<
 704                                    </div>
 705                                    <div class="form-check mb-0">
 706                                        <input class="form-check-input" type="radio" name="syncPresetUnarchiveFilter" id
 707                                        <label class="form-check-label small" for="syncPresetUnarchiveManual">Только руч
 708                                    </div>
 709                                </div>
 710                            </div>
 711                        </div>
 712                        <div class="mb-2">
 713                            <label class="form-label small mb-0">Лимит строк <span class="text-muted">(необязательно, пу
 714                            <input type="number" min="1" class="form-control form-control-sm" id="syncPresetApplyMaxLine
 715                        </div>
 716                        <div id="syncPresetApplyMappingsSection" style="display:none">
 717                            <label class="form-label small mb-1">Связи складов 1С ↔ Ozon <span class="text-muted">(снять
 718                            <div class="alert alert-warning py-2 px-3 mb-2 small border-0">
 719                                <i class="fas fa-exclamation-triangle me-1"></i>
 720                                <strong>Снятые связи</strong> — остатки этих складов будут обнулены на Ozon при накате.
 721                            </div>
 722                            <div id="syncPresetApplyMappingsContainer" class="mb-1">
 723                                <span class="text-muted small">Загрузка...</span>
 724                            </div>
 725                        </div>
 726                    </div>
 727                </div>
 728
 729                @* ── Этап 3 ── *@
 730                <div class="border rounded mb-2">
 731                    <div class="px-3 py-2 rounded-top d-flex align-items-center gap-2" style="background:var(--color-war
 732                        <span class="badge bg-white fw-bold" style="font-size:.75rem;color:var(--color-warning)">3</span
 733                        <span class="small fw-semibold text-white">Очистка лишних складов</span>
 734                        <span class="ms-auto">
 735                            <div class="form-check form-switch mb-0 d-flex align-items-center gap-1">
 736                                <input class="form-check-input" type="checkbox" role="switch" id="syncPresetAutoRunClean
 737                                <label class="form-check-label small text-white" for="syncPresetAutoRunCleanup">авто пос
 738                            </div>
 739                        </span>
 740                    </div>
 741                    <div class="p-3">
 742                        <div class="form-check mb-0">
 743                            <input class="form-check-input" type="checkbox" id="syncPresetCleanupOnlyPending" checked/>
 744                            <label class="form-check-label small" for="syncPresetCleanupOnlyPending">Только непроделанны
 745                        </div>
 746                    </div>
 747                </div>
 748
 749                @* ── Этап 4 ── *@
 750                <div class="border rounded mb-2">
 751                    <div class="px-3 py-2 rounded-top d-flex align-items-center gap-2" style="background:var(--color-pur
 752                        <span class="badge bg-white fw-bold" style="font-size:.75rem;color:var(--color-purple)">4</span>
 753                        <span class="small fw-semibold text-white">Оптимизация цен</span>
 754                        <span class="ms-auto">
 755                            <div class="form-check form-switch mb-0 d-flex align-items-center gap-1">
 756                                <input class="form-check-input" type="checkbox" role="switch" id="syncPresetAutoRunPrice
 757                                <label class="form-check-label small text-white" for="syncPresetAutoRunPricePush">авто п
 758                            </div>
 759                        </span>
 760                    </div>
 761                    <div class="p-3">
 762                        <div class="row g-2">
 763                            <div class="col">
 764                                <label class="form-label small mb-1" for="syncPresetNoPromoMarkup">Надбавка без акции, %
 765                                <input type="number" step="0.1" min="0" max="99" class="form-control form-control-sm"
 766                                       id="syncPresetNoPromoMarkup" value="5">
 767                                <div id="syncPresetNoPromoMarkupHint" class="text-info small mt-1 d-none">
 768                                    <i class="fas fa-ban me-1"></i>Снижение не применится
 769                                </div>
 770                            </div>
 771                            <div class="col">
 772                                <label class="form-label small mb-1" for="syncPresetPromoMarkup">Надбавка в акции, %</la
 773                                <input type="number" step="0.1" min="0" max="99" class="form-control form-control-sm"
 774                                       id="syncPresetPromoMarkup" value="10">
 775                                <div id="syncPresetPromoMarkupHint" class="text-info small mt-1 d-none">
 776                                    <i class="fas fa-ban me-1"></i>Снижение не применится
 777                                </div>
 778                            </div>
 779                        </div>
 780                        <div class="text-muted small mt-2">
 781                            <i class="fas fa-info-circle me-1"></i>
 782                            Применяются при расчёте оптимальной цены в модалке цен товара.
 783                        </div>
 784                    </div>
 785                </div>
 786
 787                @* ── Этап 5 ── *@
 788                <div class="border rounded mb-2">
 789                    <div class="px-3 py-2 rounded-top d-flex align-items-center gap-2" style="background:#7c4b00">
 790                        <span class="badge bg-white fw-bold" style="font-size:.75rem;color:#7c4b00">5</span>
 791                        <span class="small fw-semibold text-white"><i class="fas fa-trash-alt me-1"></i>Очистка сирот Oz
 792                        <span class="ms-auto">
 793                            <div class="form-check form-switch mb-0 d-flex align-items-center gap-1">
 794                                <input class="form-check-input" type="checkbox" role="switch" id="syncPresetAutoRunOzonO
 795                                <label class="form-check-label small text-white" for="syncPresetAutoRunOzonOrphanCleanup
 796                            </div>
 797                        </span>
 798                    </div>
 799                    <div class="p-2 px-3">
 800                        <p class="small text-muted mb-2">
 801                            Каталог из <code>OzonApi.ozon_products</code> (account_id = Client-Id), остатки по складам —
 802                            Находит товары с остатком &gt; 0, отсутствующие в снэпшоте, и обнуляет их.
 803                            Legacy xlsx-отчёт (<code>WarehouseStockReport</code>) — только по флагу пресета.
 804                        </p>
 805                        <input type="hidden" id="syncPresetStage5CatalogSource" value="OzonProductsDb"/>
 806                        <div id="syncPresetOrphanMappingsSection" style="display:none">
 807                            <label class="form-label small mb-1">Склады для очистки сирот <span class="text-muted">(галк
 808                            <div id="syncPresetOrphanMappingsContainer" class="mb-1">
 809                                <span class="text-muted small">Загрузка...</span>
 810                            </div>
 811                        </div>
 812                    </div>
 813                </div>
 814
 815                <div id="syncPresetModalError" class="text-danger small d-none"></div>
 816            </div>
 817            <div class="modal-footer py-2">
 818                <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Отмена</button>
 819                <button type="button" class="btn btn-primary btn-sm" id="syncPresetSaveConfirmBtn">Сохранить</button>
 820            </div>
 821        </div>
 822    </div>
 823</div>
 824
 825@* ── Модаль: логика приоритетного наката ────────────────────────────────── *@
 826<div class="modal fade" id="syncPriorityInfoModal" tabindex="-1">
 827    <div class="modal-dialog modal-xl modal-dialog-scrollable">
 828        <div class="modal-content">
 829            <div class="modal-header py-2">
 830                <h6 class="modal-title"><i class="fas fa-sitemap me-1 text-info"></i>Логика приоритетного наката (2 клие
 831                <button type="button" class="btn-close btn-sm" data-bs-dismiss="modal"></button>
 832            </div>
 833            <div class="modal-body">
 834                <p class="small text-muted mb-3">Применяется при 2 клиентах Ozon. Один физический остаток 1С-склада прин
 835
 836
 837                <div class="border rounded p-3 mb-4 bg-light overflow-auto">
 838                    <div class="small fw-bold text-uppercase text-muted mb-3" style="letter-spacing:.05em">Блок-схема</d
 839                    <div class="flow-col">
 840                        <div class="flow-box flow-start">Для каждого Артикул × Склад 1С</div>
 841                        <div class="flow-arrow">↓</div>
 842                        <div class="flow-box flow-diamond">К1 найден в Ozon?</div>
 843                        <div class="flow-branch">
 844
 845                            <div class="flow-col">
 846                                <div class="flow-label">ДА</div>
 847                                <div class="flow-arrow">↓</div>
 848                                <div class="flow-box flow-diamond">К1 в архиве?</div>
 849                                <div class="flow-branch">
 850                                    <div class="flow-col">
 851                                        <div class="flow-label">ДА</div>
 852                                        <div class="flow-arrow">↓</div>
 853                                        <div class="flow-box flow-unarch">Разархивировать К1<br/>→ накатить Q_1С</div>
 854                                    </div>
 855                                    <div class="flow-col">
 856                                        <div class="flow-label">НЕТ</div>
 857                                        <div class="flow-arrow">↓</div>
 858                                        <div class="flow-box flow-apply">К1 Match?<br/>→ накатить Q_1С<br/>(или пропусти
 859                                    </div>
 860                                </div>
 861                                <div class="flow-arrow">↓</div>
 862                                <div class="flow-box flow-diamond">К2 архивный?</div>
 863                                <div class="flow-branch">
 864                                    <div class="flow-col">
 865                                        <div class="flow-label">ДА → не трогать К2</div>
 866                                        <div class="flow-arrow">↓</div>
 867                                        <div class="flow-box flow-skip">Не трогать К2</div>
 868                                    </div>
 869                                    <div class="flow-col">
 870                                        <div class="flow-label">НЕТ</div>
 871                                        <div class="flow-arrow">↓</div>
 872                                        <div class="flow-box flow-diamond">К2 OzonQty&gt;0?</div>
 873                                        <div class="flow-branch">
 874                                            <div class="flow-col">
 875                                                <div class="flow-label">ДА</div>
 876                                                <div class="flow-arrow">↓</div>
 877                                                <div class="flow-box flow-clear">Clear К2 → 0</div>
 878                                            </div>
 879                                            <div class="flow-col">
 880                                                <div class="flow-label">НЕТ</div>
 881                                                <div class="flow-arrow">↓</div>
 882                                                <div class="flow-box flow-skip">Пропустить К2</div>
 883                                            </div>
 884                                        </div>
 885                                    </div>
 886                                </div>
 887                            </div>
 888
 889                            <div class="flow-col" style="border-left:2px dashed #dee2e6; padding-left:16px;">
 890                                <div class="flow-label">НЕТ (NotFound)</div>
 891                                <div class="flow-arrow">↓</div>
 892                                <div class="flow-box flow-skip">Пропустить К1</div>
 893                                <div class="flow-arrow">↓</div>
 894                                <div class="flow-box flow-diamond">К2 в архиве?</div>
 895                                <div class="flow-branch">
 896                                    <div class="flow-col">
 897                                        <div class="flow-label">ДА</div>
 898                                        <div class="flow-arrow">↓</div>
 899                                        <div class="flow-box flow-unarch">Разархивировать К2<br/>→ накатить Q_1С</div>
 900                                    </div>
 901                                    <div class="flow-col">
 902                                        <div class="flow-label">НЕТ</div>
 903                                        <div class="flow-arrow">↓</div>
 904                                        <div class="flow-box flow-diamond">К2 найден?</div>
 905                                        <div class="flow-branch">
 906                                            <div class="flow-col">
 907                                                <div class="flow-label">ДА</div>
 908                                                <div class="flow-arrow">↓</div>
 909                                                <div class="flow-box flow-apply">Накатить К2 Q_1С</div>
 910                                            </div>
 911                                            <div class="flow-col">
 912                                                <div class="flow-label">НЕТ</div>
 913                                                <div class="flow-arrow">↓</div>
 914                                                <div class="flow-box flow-skip">Пропустить оба</div>
 915                                            </div>
 916                                        </div>
 917                                    </div>
 918                                </div>
 919                            </div>
 920                        </div>
 921                    </div>
 922                </div>
 923
 924
 925                <div class="small fw-bold text-uppercase text-muted mb-2" style="letter-spacing:.05em">Таблица всех вари
 926                <div id="syncPriorityMatrixTable" class="bsx-grid bsx-grid--auto mb-3"></div>
 927
 928                <div class="alert alert-light border small mb-0 py-2">
 929                    <strong>Принцип:</strong> К1 всегда авторитетен — при наличии забирает весь остаток Q_1С, К2 обнуляе
 930                    Исключение: К2 в архиве — его не трогаем. К1 Match (совпадает) — К2 всё равно обнуляется.
 931                </div>
 932            </div>
 933            <div class="modal-footer py-2 gap-2">
 934                <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Закрыть</button>
 935                <button type="button" class="btn btn-warning btn-sm d-none" id="syncPriorityInfoProceedBtn">
 936                    <i class="fas fa-cloud-upload-alt me-1"></i>Продолжить синхронизацию
 937                </button>
 938            </div>
 939        </div>
 940    </div>
 941</div>
 942
 943@* ── Модаль: журнал наката по артикулам ─────────────────────────────────── *@
 944<div class="modal fade" id="syncApplyLogModal" tabindex="-1">
 945    <div class="modal-dialog modal-xl modal-dialog-scrollable">
 946        <div class="modal-content">
 947            <div class="modal-header py-2">
 948                <h6 class="modal-title"><i class="fas fa-list-alt me-1"></i>Журнал наката по артикулам</h6>
 949                <button type="button" class="btn-close btn-sm" data-bs-dismiss="modal"></button>
 950            </div>
 951            <div class="modal-body p-2">
 952                <div class="d-flex gap-2 mb-2 align-items-center flex-wrap">
 953                    <select class="form-select form-select-sm" id="applyLogJobSelect" style="width:auto">
 954                        <option value="">Все прогоны</option>
 955                    </select>
 956                    <input type="text" class="form-control form-control-sm" id="applyLogArticleFilter" placeholder="Филь
 957                    <button type="button" class="btn btn-sm btn-outline-secondary" id="applyLogRefreshBtn">
 958                        <i class="fas fa-sync-alt"></i>
 959                    </button>
 960                    <div class="btn-group btn-group-sm ms-auto">
 961                        <button type="button" class="btn btn-outline-secondary" id="applyLogPrevPage">←</button>
 962                        <button type="button" class="btn btn-outline-secondary" id="applyLogNextPage">→</button>
 963                    </div>
 964                    <span class="small text-muted" id="applyLogPageInfo"></span>
 965                </div>
 966                <div id="applyLogAnalysisContainer" class="mb-2">
 967                    <div class="text-muted small">Анализ журнала…</div>
 968                </div>
 969                <div id="applyLogAnalysisTable" class="bsx-grid bsx-grid--modal mb-2" hidden></div>
 970                <div id="applyLogContainer" class="bsx-grid bsx-grid--modal-tall"></div>
 971            </div>
 972            <div class="modal-footer py-2">
 973                <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Закрыть</button>
 974            </div>
 975        </div>
 976    </div>
 977</div>
 978
 979<div class="modal fade" id="syncOzonPricesModal" tabindex="-1" aria-labelledby="syncOzonPricesModalLabel" aria-hidden="t
 980    <div class="modal-dialog modal-xl modal-dialog-scrollable">
 981        <div class="modal-content">
 982            <div class="modal-header py-2 card-header-sync">
 983                <div class="d-flex align-items-center gap-2">
 984                    <i class="fas fa-tag text-primary" style="font-size:0.9rem"></i>
 985                    <h6 class="modal-title mb-0" id="syncOzonPricesModalLabel">
 986                        Цены Ozon — <span id="syncOzonPricesArticle" class="fw-bold"></span>
 987                        <span id="syncOzonPricesBatchBadge" class="badge bg-info text-dark ms-1" style="display:none;fon
 988                    </h6>
 989                </div>
 990                <button type="button" class="btn-close btn-sm" data-bs-dismiss="modal" aria-label="Закрыть"></button>
 991            </div>
 992            <div class="modal-body p-0" id="syncOzonPricesBody">
 993                <div class="text-center py-4"><div class="spinner-border spinner-border-sm text-primary"></div></div>
 994            </div>
 995            <div class="modal-footer py-2 border-top">
 996                <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Закрыть</button>
 997            </div>
 998        </div>
 999    </div>
 1000</div>
 1001
 1002@* ── Модалка: расширенный лог запуска пресета ───────────────────────────── *@
 1003<div class="modal fade" id="parLogModal" tabindex="-1" aria-hidden="true">
 1004    <div class="modal-dialog modal-xl modal-dialog-scrollable">
 1005        <div class="modal-content border-0">
 1006            <div class="modal-header py-2 px-3" style="background:var(--bg-sidebar);color:var(--text-inverse)">
 1007                <span id="parLogModalSpinner" class="spinner-border spinner-border-sm text-light me-2 d-none" style="wid
 1008                <h6 class="modal-title fw-semibold flex-grow-1 mb-0" id="parLogModalTitle">Журнал запуска</h6>
 1009                <button type="button" class="btn btn-sm btn-outline-light py-0 px-2 me-2" id="parLogModalDownloadBtn">
 1010                    <i class="fas fa-download me-1"></i>txt
 1011                </button>
 1012                <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
 1013            </div>
 1014            <div class="modal-body p-0">
 1015                <div id="parLogModalBody" class="par-log-modal-body"></div>
 1016            </div>
 1017        </div>
 1018    </div>
 1019</div>
 1020
 1021@* ── Панель авто-прогресса пресета ─────────────────────────────────────── *@
 1022<div id="presetAutoRunPanel" class="d-none">
 1023    <div class="card border-0 rounded-3 overflow-hidden">
 1024        <div class="card-header py-2 px-3 d-flex align-items-center gap-2" id="presetAutoRunHeader"
 1025             style="background:var(--bg-sidebar);color:var(--text-inverse)">
 1026            <span id="presetAutoRunSpinner" class="spinner-border spinner-border-sm text-light" style="width:14px;height
 1027            <span class="small fw-semibold flex-grow-1" id="presetAutoRunTitle">Авто-запуск пресета</span>
 1028            <button type="button" class="btn btn-sm btn-outline-light py-0 px-1 me-1" id="presetAutoRunExpand" title="Ра
 1029                <i class="fas fa-expand-alt"></i>
 1030            </button>
 1031            <button type="button" class="btn-close btn-close-white btn-sm p-0 ms-1" id="presetAutoRunClose" style="font-
 1032        </div>
 1033        <div class="card-body p-3 pb-2">
 1034            <div class="par-stepper mb-3">
 1035                <div class="par-step" id="parStep1" style="--par-color:var(--color-primary)">
 1036                    <div class="par-step-circle" id="parStep1Circle">1</div>
 1037                    <div class="par-step-label">Отчёт</div>
 1038                </div>
 1039                <div class="par-connector" id="parConn1"></div>
 1040                <div class="par-step" id="parStep2" style="--par-color:var(--color-success)">
 1041                    <div class="par-step-circle" id="parStep2Circle">2</div>
 1042                    <div class="par-step-label">Накат</div>
 1043                </div>
 1044                <div class="par-connector" id="parConn2"></div>
 1045                <div class="par-step" id="parStep3" style="--par-color:var(--color-warning)">
 1046                    <div class="par-step-circle" id="parStep3Circle">3</div>
 1047                    <div class="par-step-label">Очистка</div>
 1048                </div>
 1049                <div class="par-connector" id="parConn3"></div>
 1050                <div class="par-step" id="parStep4" style="--par-color:var(--color-success)">
 1051                    <div class="par-step-circle" id="parStep4Circle">4</div>
 1052                    <div class="par-step-label">Цены</div>
 1053                </div>
 1054                <div class="par-connector" id="parConn4"></div>
 1055                <div class="par-step" id="parStep5" style="--par-color:#7c4b00">
 1056                    <div class="par-step-circle" id="parStep5Circle">5</div>
 1057                    <div class="par-step-label">Сироты</div>
 1058                </div>
 1059            </div>
 1060            <div class="mb-2">
 1061                <div class="d-flex justify-content-between align-items-center mb-1">
 1062                    <span class="small fw-semibold" id="presetAutoRunStageText" style="color:var(--text-secondary)">Подг
 1063                    <span class="small text-muted" id="presetAutoRunElapsed">00:00</span>
 1064                </div>
 1065                <div class="progress" style="height:.45rem;border-radius:var(--radius-sm);background:var(--border-defaul
 1066                    <div class="progress-bar progress-bar-striped progress-bar-animated" id="presetAutoRunProgressBar"
 1067                         style="width:0%;background:var(--color-primary);transition:width .3s,background .3s"></div>
 1068                </div>
 1069            </div>
 1070            <div id="presetAutoRunLog"></div>
 1071        </div>
 1072    </div>
 1073</div>
 1074
 1075<script src="~/js/Studio2/BitrixStock/bitrixStockMskDateTime.js" asp-append-version="true"></script>
 01076@await Html.PartialAsync("_SyncReportScript")