< Summary

Information
Class: AspNetCoreGeneratedDocument.Areas_Studio2_Views_OrderHistory_GetOrderHistory
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/OrderHistory/GetOrderHistory.cshtml
Line coverage
0%
Covered lines: 0
Uncovered lines: 164
Coverable lines: 164
Total lines: 617
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 164
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%126561120%
<ExecuteAsync()0%812280%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/OrderHistory/GetOrderHistory.cshtml

#LineLine coverage
 1@model OrderHistoryViewModel
 2@using OzonDomains
 3@using OzonDomains.Models
 4@using OzonOrdersWeb.Areas.Studio2.ViewModels.Grid
 5@using OzonOrdersWeb.Areas.Studio2.ViewModels.OrderHistory
 6@using OzonOrdersWeb.WebServices.OrderHistory
 7@using Services.BitrixStockServices
 8@using Services.TransactionUtilsServices
 9
 10@{
 011    var transactions = Model.Transactions ?? new List<Transaction>();
 012    var shipmentTotalsHistory = Model.ShipmentTotalsHistory ?? new List<OrderShipmentTotalsHistoryRowViewModel>();
 013    var calculationSnapshots = Model.CalculationSnapshots ?? new List<OrderCalculationSnapshotHistoryViewModel>();
 014    ViewData["Title"] = $"История заказа - {Model.ShipmentNumber ?? $"Заказ #{Model.OrderId}"}";
 015    var isAdmin = User.IsInRole("Admin");
 016    var jsonOptions = new System.Text.Json.JsonSerializerOptions(System.Text.Json.JsonSerializerDefaults.Web);
 17
 18    // Строки гридов: вычисления — в OrderHistoryGridRows, вьюха только сериализует.
 019    var changeGrid = OrderHistoryGridRows.BuildChanges(Model);
 020    var orderedTransactions = transactions.OrderByDescending(t => t.CreatedDateTime).ToList();
 021    var gridData = new
 022    {
 023        orderId = Model.OrderId,
 024        isAdmin,
 025        changes = changeGrid.Rows,
 026        calculations = calculationSnapshots.Select(s => new
 027        {
 028            s.Id, s.CapturedAt, source = s.Source == "Transaction" ? "Проведение транзакции" : "Ручное редактирование",
 029            s.ShipmentAmount, s.CostPrice, s.MinProfit, s.MaxProfit, s.MinMarkup, s.MaxMarkup
 030        }),
 031        transactions = orderedTransactions.Select(t => new
 032        {
 033            t.Id,
 034            type = t.Type.HasValue ? t.Type.Value.GetDisplayName() : "Транзакция",
 035            icon = t.Type.HasValue ? t.Type.Value.GetTransactionTypeIcon() : "fas fa-circle",
 036            createdBy = t.CreateBy,
 037            createdAt = t.CreatedDateTime,
 038            createdAtMsk = t.CreatedDateTime.HasValue ? BitrixStockMskDateTime.Format(t.CreatedDateTime.Value) : null,
 039            // Проведено в 1С: в комментарии есть GUID документа 1С.
 040            conductedIn1C = !string.IsNullOrWhiteSpace(t.Comment)
 041                && System.Text.RegularExpressions.Regex.IsMatch(t.Comment, "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}
 042            commentText = string.IsNullOrWhiteSpace(t.Comment)
 043                ? null
 044                : System.Net.WebUtility.HtmlDecode(System.Text.RegularExpressions.Regex.Replace(t.Comment, "<[^>]+>", " 
 045        }),
 046        totals = shipmentTotalsHistory.Select(h => new
 047        {
 048            h.UpdatedAt, h.ListAmount, h.AccrualsForSale, h.SaleCommission, h.ProcessingAndDelivery, h.RefundsAndCancell
 049        }),
 050        handover = Model.HandoverItems.Select((h, i) =>
 051        {
 052            var (tone, icon, text) = OrderHistoryGridRows.DescribeHandoverStatus(h.Status);
 053            return new
 054            {
 055                id = i,
 056                act = h.ActType ?? h.GroupName ?? "Акт",
 057                actNo = string.IsNullOrWhiteSpace(h.ActNo) ? "—" : h.ActNo,
 058                actDate = h.ActDate != default ? h.ActDate.ToString("dd.MM.yyyy") : null,
 059                pvz = h.Pvz,
 060                returnType = h.ReturnType?.ToUpperInvariant(),
 061                status = text, statusTone = tone, statusIcon = icon,
 062                product = h.ProductName, barcode = h.Barcode, offerId = h.OfferId, sku = h.Sku,
 063                value = h.Value, valueApi = h.ValueApi != h.Value ? h.ValueApi : null,
 064                defect = h.Defect, missort = h.Missort, markedAt = h.MarkedAt,
 065                details = OrderHistoryGridRows.HandoverDetails(h).Select(d => new { label = d.Key, value = d.Value }),
 066                photos = h.Photos.Select(p => Url.Action("GetHandoverPhoto", "OrderHistory", new { area = "Studio2", ord
 067            };
 068        })
 069    };
 70}
 71
 72<link rel="stylesheet" href="~/css/Studio2/Orders/order-history.css" asp-append-version="true"/>
 73<link rel="stylesheet" href="~/css/Studio2/Orders/order-history-redesign.css" asp-append-version="true"/>
 74<link rel="stylesheet" href="~/css/Studio2/Orders/order-history-movement-map.css" asp-append-version="true"/>
 75<link rel="stylesheet" href="~/css/Studio2/BitrixStock/bitrix-stock.css" asp-append-version="true"/>
 76<link rel="stylesheet" href="~/css/Studio2/Orders/TransactionsTable.css" asp-append-version="true"/>
 077@await Html.PartialAsync("_BsxGridShell")
 78<link rel="stylesheet" href="~/css/Studio2/Orders/order-history-grids.css" asp-append-version="true"/>
 79<link rel="stylesheet" href="~/css/Studio2/OrdersGrid/feed-explain.css" asp-append-version="true"/>
 80
 81<div id="top" class="oh-page container-fluid bsx-surface">
 82    <a href="/Orders/Index" class="oh-page-back">
 83        <i class="fas fa-arrow-left"></i> К списку заказов
 84    </a>
 85
 86    @{
 087        var hasTransactions = Model.Transactions != null && Model.Transactions.Any();
 88        // Начисления Ozon грузятся лениво через AJAX (GetShipmentTotals) — на SSR-этапе
 89        // известно только, есть ли у заказа номер отправления, по которому их можно запросить.
 090        var hasFinanceTab = !string.IsNullOrWhiteSpace(Model.ShipmentNumber);
 091        var hasHistories = Model.Histories.Any();
 092        var hasTotalsHistory = shipmentTotalsHistory.Count > 0;
 093        var hasHandoverItems = Model.HandoverItems.Any();
 094        var createdRecord = Model.Histories
 095            .SelectMany(g => g.Value)
 096            .FirstOrDefault(c => c.ColumnDisplayName == "Создание записи");
 097        var totalChanges = Model.Histories.Values.Sum(h => h.Count);
 98    }
 99    <div class="card oh-section oh-hero-card">
 100        <div class="oh-hero-body">
 0101            @if (!string.IsNullOrWhiteSpace(Model.ProductImageUrl))
 102            {
 103                <div class="oh-hero-image">
 104                    <img src="@Model.ProductImageUrl"
 105                         alt="@(Model.ProductName ?? Model.Article ?? "Товар")"
 106                         class="oh-hero-thumb"
 107                         onclick="openProductImage(this.src, this.alt)"
 108                         onerror="this.style.display='none'"
 109                         loading="lazy" />
 110                </div>
 111            }
 112
 113            <div class="oh-hero-main">
 114                <div class="oh-hero-top">
 115                    <div class="oh-hero-heading">
 116                        <span class="oh-hero-title-text">
 117                            <i class="fas fa-history"></i>
 0118                            @(Model.ShipmentNumber ?? $"Заказ #{Model.OrderId}")
 119                        </span>
 0120                        @if (hasTransactions)
 121                        {
 122                            <span class="oh-hero-badge-blue">
 0123                                <i class="fas fa-exchange-alt me-1"></i>@transactions.Count тр.
 124                            </span>
 125                        }
 0126                        @if (hasHistories)
 127                        {
 128                            <span class="oh-hero-badge-purple">
 0129                                <i class="fas fa-pencil-alt me-1"></i>@totalChanges изм.
 130                            </span>
 131                        }
 132                    </div>
 133
 134                    <div class="oh-hero-meta-row">
 0135                        @if (!string.IsNullOrWhiteSpace(Model.Article))
 136                        {
 0137                            <span class="oh-hero-meta-chip"><i class="fas fa-barcode"></i>@Model.Article</span>
 138                        }
 0139                        @if (!string.IsNullOrWhiteSpace(Model.ClientName))
 140                        {
 0141                            <span class="oh-hero-meta-chip"><i class="fas fa-building"></i>@Model.ClientName</span>
 142                        }
 0143                        @if (!string.IsNullOrWhiteSpace(Model.ProductName))
 144                        {
 145                            <span class="oh-hero-meta-chip" title="@Model.ProductName">
 0146                                <i class="fas fa-box"></i>@Model.ProductName
 147                            </span>
 148                        }
 0149                        @if (createdRecord != null)
 150                        {
 151                            <span class="oh-hero-meta-chip">
 0152                                <i class="fas fa-calendar-alt"></i>@createdRecord.ChangedAt.ToString("dd.MM.yyyy HH:mm")
 153                            </span>
 154                        }
 155                    </div>
 156                </div>
 157
 158                <div class="oh-hero-stats">
 159                    <button type="button" class="oh-hero-stat-item" data-tab="tab-history">
 0160                        <span class="oh-hero-stat-value oh-hero-stat-value--accent">@totalChanges</span>
 161                        <span class="oh-hero-stat-label">Изменений</span>
 162                    </button>
 0163                    @if (hasTransactions)
 164                    {
 165                        <button type="button" class="oh-hero-stat-item" data-tab="tab-transactions">
 0166                            <span class="oh-hero-stat-value">@transactions.Count</span>
 167                            <span class="oh-hero-stat-label">Транзакций</span>
 168                        </button>
 169                    }
 0170                    @if (hasFinanceTab)
 171                    {
 172                        <button type="button" class="oh-hero-stat-item" data-tab="tab-finance">
 173                            <i class="fas fa-ruble-sign oh-hero-stat-icon"></i>
 174                            <span class="oh-hero-stat-label">Начисления Ozon</span>
 175                        </button>
 176                    }
 0177                    @if (hasTotalsHistory)
 178                    {
 179                        <button type="button" class="oh-hero-stat-item" data-tab="tab-totals">
 0180                            <span class="oh-hero-stat-value">@shipmentTotalsHistory.Count</span>
 181                            <span class="oh-hero-stat-label">История итогов</span>
 182                        </button>
 183                    }
 0184                    @if (!string.IsNullOrWhiteSpace(Model.Article))
 185                    {
 186                        <button type="button" class="oh-hero-stat-item" data-tab="tab-feed">
 187                            <i class="fas fa-tags oh-hero-stat-icon"></i>
 188                            <span class="oh-hero-stat-label">Фид</span>
 189                        </button>
 190                    }
 0191                    @if (Model.ProductId.HasValue)
 192                    {
 193                        <button type="button" class="oh-hero-stat-item" data-tab="tab-quotations">
 194                            <i class="fas fa-clock-rotate-left oh-hero-stat-icon"></i>
 195                            <span class="oh-hero-stat-label">Проценки</span>
 196                        </button>
 197                    }
 0198                    @if (hasHandoverItems)
 199                    {
 200                        <button type="button" class="oh-hero-stat-item" data-tab="tab-handover">
 0201                            <span class="oh-hero-stat-value">@Model.HandoverItems.Count</span>
 202                            <span class="oh-hero-stat-label">Данные по возврату</span>
 203                        </button>
 204                    }
 0205                    @if (!string.IsNullOrWhiteSpace(Model.Article))
 206                    {
 207                        <button type="button" class="oh-hero-stat-item" data-tab="tab-bitrix">
 208                            <i class="fas fa-sync oh-hero-stat-icon"></i>
 209                            <span class="oh-hero-stat-label">BitrixStock</span>
 210                        </button>
 211                        <button type="button" class="oh-hero-stat-item" data-tab="tab-movement">
 212                            <i class="fas fa-chart-line oh-hero-stat-icon"></i>
 213                            <span class="oh-hero-stat-label">Карта движений</span>
 214                        </button>
 215                    }
 216                </div>
 217            </div>
 218        </div>
 219    </div>
 220
 221    <partial name="_OrderProblems" model="Model.Problems" />
 222
 223    <!-- ── Панель вкладок ───────────────────────────────────────── -->
 224    <div class="oh-tabs-bar" id="oh-tabs-bar">
 225        <button class="oh-tab-btn active" data-tab="tab-history" id="btn-tab-history">
 226            <i class="fas fa-pencil-alt"></i>
 227            История изменений
 0228            @if (hasHistories)
 229            {
 0230                <span class="oh-tab-badge">@totalChanges</span>
 231            }
 232        </button>
 0233        @if (calculationSnapshots.Any())
 234        {
 235            <button class="oh-tab-btn" data-tab="tab-calculations" id="btn-tab-calculations">
 236                <i class="fas fa-calculator"></i>
 237                Расчёты
 0238                <span class="oh-tab-badge">@calculationSnapshots.Count</span>
 239            </button>
 240        }
 0241        @if (hasTransactions)
 242        {
 243            <button class="oh-tab-btn" data-tab="tab-transactions" id="btn-tab-transactions">
 244                <i class="fas fa-exchange-alt"></i>
 245                Транзакции
 0246                <span class="oh-tab-badge">@transactions.Count</span>
 247            </button>
 248        }
 0249        @if (hasFinanceTab)
 250        {
 251            <button class="oh-tab-btn" data-tab="tab-finance" id="btn-tab-finance">
 252                <i class="fas fa-ruble-sign"></i>
 253                Начисления Ozon
 254            </button>
 255        }
 0256        @if (hasTotalsHistory)
 257        {
 258            <button class="oh-tab-btn" data-tab="tab-totals" id="btn-tab-totals">
 259                <i class="fas fa-history"></i>
 260                История итогов
 0261                <span class="oh-tab-badge">@shipmentTotalsHistory.Count</span>
 262            </button>
 263        }
 0264        @if (!string.IsNullOrWhiteSpace(Model.Article))
 265        {
 266            <button class="oh-tab-btn" data-tab="tab-feed" id="btn-tab-feed">
 267                <i class="fas fa-tags"></i>
 268                Фид
 269            </button>
 270        }
 0271        @if (Model.ProductId.HasValue)
 272        {
 273            <button class="oh-tab-btn" data-tab="tab-quotations" id="btn-tab-quotations">
 274                <i class="fas fa-clock-rotate-left"></i>
 275                Проценки поставщиков
 0276                <span class="oh-tab-badge" id="oh-quotation-tab-count">@Model.QuotationOffersCount</span>
 277            </button>
 278        }
 0279        @if (hasHandoverItems)
 280        {
 281            <button class="oh-tab-btn" data-tab="tab-handover" id="btn-tab-handover">
 282                <i class="fas fa-box-open"></i>
 283                Данные по возврату
 0284                <span class="oh-tab-badge">@Model.HandoverItems.Count</span>
 285            </button>
 286        }
 0287        @if (!string.IsNullOrWhiteSpace(Model.Article))
 288        {
 289            <button class="oh-tab-btn" data-tab="tab-bitrix" id="btn-tab-bitrix">
 290                <i class="fas fa-sync"></i>
 291                BitrixStock
 292            </button>
 293            <button class="oh-tab-btn" data-tab="tab-movement" id="btn-tab-movement">
 294                <i class="fas fa-chart-line"></i>
 295                Карта движений 1С
 296            </button>
 297        }
 298    </div>
 299
 300    <!-- ── Содержимое вкладок: все таблицы — гриды AG Grid (order-history-grids.js) ── -->
 301    <div class="oh-tabs-content">
 302
 303        <div class="oh-tab-pane active" id="tab-history">
 0304            @if (!Model.Histories.Any())
 305            {
 306                <div class="oh-empty-state">
 307                    <img src="~/images/status/empty-state.svg" asp-append-version="true" alt="">
 308                    <div>
 309                        <strong>История изменений отсутствует</strong>
 310                        <div class="small mt-1">Записи появляются при создании и изменении заказа. Для старых заказов ис
 311                    </div>
 312                </div>
 313            }
 314            else
 315            {
 0316                @await Html.PartialAsync("_BsxDirectoryGrid", new BsxDirectoryGridViewModel("ohChanges", "История измене
 0317                    "Поиск по полю, значению, пользователю…",
 0318                    "Строки одной операции объединены по времени и автору. Фильтры колонок: дата, пользователь, поле. Кн
 319                @* Проведения 1С операций — разметка партиала, показывается модалкой по кнопке в строке. *@
 0320                @foreach (var operation in changeGrid.Operations.Where(o => o.OneCPostings.Count > 0))
 321                {
 322                    <template id="oh-onec-@operation.Index">
 0323                        @await Html.PartialAsync("~/Areas/Studio2/Views/OrderHistory/_OrderHistoryOneCPostingHeader.csht
 324                    </template>
 325                }
 326            }
 327        </div>
 328
 0329        @if (calculationSnapshots.Any())
 330        {
 331            <div class="oh-tab-pane" id="tab-calculations">
 0332                @await Html.PartialAsync("_BsxDirectoryGrid", new BsxDirectoryGridViewModel("ohCalc", "Сохранённые расчё
 0333                    "Значения зафиксированы на момент проведения или редактирования. «Подробный расчёт» — полный расчёт 
 334            </div>
 335        }
 336
 0337        @if (transactions.Any())
 338        {
 339            <div class="oh-tab-pane" id="tab-transactions">
 0340                @await Html.PartialAsync("_BsxDirectoryGrid", new BsxDirectoryGridViewModel("ohTx", "Транзакции заказа",
 0341                    "Комментарий транзакции целиком — кнопкой в строке или двойным кликом."))
 0342                @foreach (var tr in orderedTransactions)
 343                {
 344                    <template id="oh-txc-@tr.Id">
 345                        <partial name="_OrderHistoryTransactionComment" model="TransactionCommentDisplayFormatter.ParseF
 346                    </template>
 347                }
 348            </div>
 349        }
 350
 0351        @if (!string.IsNullOrWhiteSpace(Model.ShipmentNumber))
 352        {
 353            <div class="oh-tab-pane" id="tab-finance">
 0354                <div id="oh-finance-section" data-order-id="@Model.OrderId">
 355                    <div class="bsx-dir-stats mb-2">
 356                        <span class="bsx-dir-stat">Операций <b id="oh-finance-count-badge">—</b></span>
 357                        <span class="bsx-dir-stat">Период <b id="oh-finance-period">—</b></span>
 358                        <span class="bsx-dir-stat">Итого <b id="oh-finance-total">—</b></span>
 359                    </div>
 360                    <div id="oh-finance-error" class="alert alert-danger small d-none"></div>
 0361                    @await Html.PartialAsync("_BsxDirectoryGrid", new BsxDirectoryGridViewModel("ohFinance", "Начисления
 0362                        "Данные Ozon API по номеру отправления (кэш 20 мин). Фильтры колонок: дата, тип операции, катего
 363                    <div class="oh-totals-grid d-none" id="oh-finance-totals-grid">
 364                        <div class="oh-totals-grid-label"><i class="fas fa-calculator me-1"></i>Итоги по отправлению</di
 365                        <div class="oh-totals-cards" id="oh-finance-totals-cards"></div>
 366                    </div>
 367                </div>
 368            </div>
 369        }
 370
 0371        @if (shipmentTotalsHistory.Any())
 372        {
 373            <div class="oh-tab-pane" id="tab-totals">
 0374                @await Html.PartialAsync("_BsxDirectoryGrid", new BsxDirectoryGridViewModel("ohTotals", "История итогов 
 375            </div>
 376        }
 377
 0378        @if (!string.IsNullOrWhiteSpace(Model.Article))
 379        {
 380            <div class="oh-tab-pane" id="tab-feed">
 381                <div id="oh-feed-section"
 0382                     data-url="@Url.Action("GetFeedPriceStock", "OrderHistory", new { area = "Studio2", orderId = Model.
 0383                     data-explain-url="@Url.Action("GetFeedPriceExplanation", "OrderHistory", new { area = "Studio2", or
 384                    <div class="oh-feed-bar">
 385                        <div class="oh-feed-bar-main">
 386                            <span class="oh-feed-bar-label">Артикул в фиде</span>
 387                            <span class="oh-feed-key" id="oh-feed-key">Загрузка из фида…</span>
 388                        </div>
 389                        <div class="oh-feed-cabinets" id="oh-feed-cabinets" role="tablist" aria-label="Кабинет фида"></d
 390                        <button type="button" class="btn btn-sm btn-outline-secondary" id="oh-feed-refresh">
 391                            <i class="fas fa-rotate me-1"></i>Обновить
 392                        </button>
 393                    </div>
 394                    <div class="oh-feed-summary" id="oh-feed-summary"></div>
 395                    <div class="oh-feed-layout">
 396                        <section class="oh-feed-block oh-feed-block--explain">
 397                            <h3 class="oh-feed-title">Как получилась цена</h3>
 398                            <div id="oh-feed-explain" class="oh-feed-explain"></div>
 399                        </section>
 400                        <div class="oh-feed-side">
 401                            <section class="oh-feed-block">
 402                                <h3 class="oh-feed-title">Остатки по складам <span class="oh-feed-count" id="oh-feed-cur
 403                                <div id="ohFeedCurrent" class="oh-feed-grid"></div>
 404                            </section>
 405                            <section class="oh-feed-block">
 406                                <h3 class="oh-feed-title">История цены по сборкам <span class="oh-feed-count" id="oh-fee
 407                                <div id="ohFeedHistory" class="oh-feed-grid oh-feed-grid--history"></div>
 408                            </section>
 409                        </div>
 410                    </div>
 411                </div>
 412            </div>
 413        }
 414
 0415        @if (Model.ProductId.HasValue)
 416        {
 417            <div class="oh-tab-pane" id="tab-quotations">
 418                <div class="bsx-grid-toolbar mb-2">
 419                    <div class="bsx-quick" id="oh-quote-kind">
 0420                        <button type="button" class="is-active" data-kind="offers">Предложения <b>@Model.QuotationOffers
 421                        <button type="button" data-kind="failed">Ошибки запросов</button>
 422                    </div>
 0423                    <span class="bsx-grid-count">@Model.QuotationRequestsCount запросов по товару</span>
 424                </div>
 0425                @await Html.PartialAsync("_BsxDirectoryGrid", new BsxDirectoryGridViewModel("ohQuote", "История проценок
 0426                    "Поиск по наименованию, направлению, производителю…",
 0427                    "Фильтры и сортировка — по всей истории проценок товара на сервере. Срок с «+Nч» включает надбавку п
 428            </div>
 429        }
 430
 0431        @if (Model.HandoverItems.Any())
 432        {
 433            <div class="oh-tab-pane" id="tab-handover">
 0434                @await Html.PartialAsync("_BsxDirectoryGrid", new BsxDirectoryGridViewModel("ohHandover", "Данные по воз
 0435                    "Акты выдачи/приёма-передачи/отказа Ozon (импорт «Telegram Lite»). Подробности и фото — в последней 
 436            </div>
 437        }
 438
 0439        @if (!string.IsNullOrWhiteSpace(Model.Article))
 440        {
 441            <div class="oh-tab-pane" id="tab-bitrix">
 442                <div id="oh-bitrix-sync-section"
 0443                     data-order-id="@Model.OrderId"
 0444                     data-article="@Model.Article"
 0445                     data-export-url="@Url.Action("ExportBitrixSyncHistoryExcel", "OrderHistory", new { area = "Studio2"
 446                    <div class="bsx-grid-toolbar mb-2">
 0447                        <span class="bsx-dir-stat">Артикул <b>@Model.Article</b></span>
 448                        <span class="bsx-grid-count" id="oh-bitrix-sync-meta"></span>
 449                        <div class="bsx-grid-toolbar-end">
 450                            <button type="button" id="oh-bitrix-sync-more" class="btn btn-sm btn-outline-secondary d-non
 451                            <button type="button" class="btn btn-sm btn-outline-success d-none" id="oh-bitrix-sync-expor
 452                        </div>
 453                    </div>
 454                    <div id="oh-bitrix-sync-error" class="alert alert-danger small d-none"></div>
 0455                    @await Html.PartialAsync("_BsxDirectoryGrid", new BsxDirectoryGridViewModel("ohBitrix", "История син
 0456                        "Поиск по складу, действию, примечанию…", "Строка = склад в отчёте синхронизации; отчёт — колонк
 457                </div>
 458            </div>
 459
 460            <div class="oh-tab-pane" id="tab-movement">
 0461                <div id="ohm-section" data-article="@Model.Article" data-can-edit-reserve="@(isAdmin.ToString().ToLowerI
 462                    <div id="ohm-balance-wrap" class="mb-3 d-none">
 463                        <div class="small fw-semibold mb-1 bsx-muted">Текущий остаток:</div>
 464                        <div id="ohm-balance-line"></div>
 0465                        @if (isAdmin)
 466                        {
 467                            <div id="ohm-stock-actions" class="mt-2 small"></div>
 468                        }
 469                        <div class="small fw-semibold mb-1 mt-2 bsx-muted">В резерве:</div>
 470                        <div id="ohm-reserve-line"></div>
 0471                        @if (isAdmin)
 472                        {
 473                            <div id="ohm-reserve-actions" class="mt-2 small"></div>
 474                        }
 475                    </div>
 476                    <div class="bsx-dir-stats mb-3" id="ohm-stats-row">
 477                        <span class="bsx-muted small"><i class="fas fa-spinner fa-spin me-1"></i>Кликните на вкладку для
 478                    </div>
 479                    <div id="ohm-error" class="alert alert-danger d-none"></div>
 0480                    @await Html.PartialAsync("_BsxDirectoryGrid", new BsxDirectoryGridViewModel("ohMove", "Карта движени
 0481                        "Поиск по документу, складу…", "Баланс — нарастающий итог по непометкам на удаление в хронологич
 482                </div>
 483            </div>
 484        }
 485
 486    </div>@* /oh-tabs-content *@
 487</div>
 488
 0489@await Html.PartialAsync("~/Areas/Studio2/Views/Partials/Shared/_ProductImageModal.cshtml")
 490
 491<div class="modal fade" id="ohDetailModal" tabindex="-1" aria-hidden="true">
 492    <div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
 493        <div class="modal-content">
 494            <div class="modal-header">
 495                <h5 class="modal-title" id="ohDetailTitle"></h5>
 496                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
 497            </div>
 498            <div class="modal-body" id="ohDetailBody"></div>
 499        </div>
 500    </div>
 501</div>
 502
 0503@if (User.IsInRole("Admin"))
 504{
 0505    @Html.AntiForgeryToken()
 506
 507    <div class="modal fade" id="ohmAdjustmentModal" tabindex="-1" aria-labelledby="ohmAdjustmentModalTitle" aria-hidden=
 508        <div class="modal-dialog modal-dialog-centered">
 509            <div class="modal-content ohm-adjustment-modal">
 510                <div class="modal-header">
 511                    <div>
 512                        <div class="ohm-modal-kicker"><i class="fas fa-shield-halved"></i> Контролируемое изменение</div
 513                        <h5 class="modal-title" id="ohmAdjustmentModalTitle">Изменить резерв</h5>
 514                    </div>
 515                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
 516                </div>
 517                <form id="ohm-adjustment-form">
 518                    <div class="modal-body">
 519                        <div class="ohm-adjustment-summary" id="ohm-adjustment-summary"></div>
 520                        <div class="mb-3">
 521                            <label class="form-label" for="ohm-adjustment-quantity">Новое количество</label>
 522                            <input class="form-control" id="ohm-adjustment-quantity" type="number" min="0" step="0.001" 
 523                            <div class="form-text" id="ohm-adjustment-help"></div>
 524                        </div>
 525                        <div class="mb-0">
 526                            <label class="form-label" for="ohm-adjustment-reason">Причина изменения</label>
 527                            <textarea class="form-control" id="ohm-adjustment-reason" rows="3" minlength="3" maxlength="
 528                            <div class="form-text">Будет записана в комментарий документа 1С и журнал аудита.</div>
 529                        </div>
 530                        <div class="alert alert-danger py-2 px-3 mt-3 mb-0 d-none" id="ohm-adjustment-error"></div>
 531                    </div>
 532                    <div class="modal-footer">
 533                        <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Отмена</button>
 534                        <button type="submit" class="btn btn-primary" id="ohm-adjustment-submit"><i class="fas fa-check 
 535                    </div>
 536                </form>
 537            </div>
 538        </div>
 539    </div>
 540
 541    <div class="modal fade" id="ohRollbackModal" tabindex="-1" aria-hidden="true">
 542        <div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
 543            <div class="modal-content oh-rollback-modal">
 544                <div class="modal-header">
 545                    <h5 class="modal-title">
 546                        <i class="fas fa-clock-rotate-left me-2 text-primary"></i>
 547                        Восстановление заказа
 548                    </h5>
 549                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
 550                </div>
 551                <div class="modal-body">
 552                    <p class="oh-rollback-lead mb-3">
 553                        Заказ будет приведён к состоянию на момент
 554                        <strong id="ohRollbackPointLabel">—</strong>.
 555                        Восстановление будет записано в историю и помечено как «Откат».
 556                    </p>
 557
 558                    <div id="ohRollbackLoading" class="text-center text-muted py-4 d-none">
 559                        <i class="fas fa-spinner fa-spin me-2"></i>Расчёт изменений…
 560                    </div>
 561
 562                    <div id="ohRollbackError" class="alert alert-danger d-none" role="alert"></div>
 563
 564                    <div id="ohRollbackEmpty" class="alert alert-info d-none" role="alert">
 565                        <i class="fas fa-info-circle me-1"></i>Заказ уже находится в этом состоянии — изменять нечего.
 566                    </div>
 567
 568                    <div id="ohRollbackChangesWrap" class="d-none">
 569                        <div id="ohRollbackGrid" class="bsx-grid bsx-grid--auto"></div>
 570                        <div id="ohRollbackWarnings" class="mt-3 d-none"></div>
 571                    </div>
 572                </div>
 573                <div class="modal-footer">
 574                    <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Отмена</button>
 575                    <button type="button" class="btn btn-primary" id="ohRollbackConfirmBtn" disabled>
 576                        <i class="fas fa-check me-1"></i>Подтвердить восстановление
 577                    </button>
 578                </div>
 579            </div>
 580        </div>
 581    </div>
 582}
 583
 584@section Scripts {
 585    <script>
 0586        window.ohGridData = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(gridData, jsonOptions));
 0587        window.ohLabelPreviewUrl = '@Url.Action("Preview", "Labels", new { area = "Studio2" })';
 0588        window.ohQuotationProviders = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(
 0589            new[] { "Armtek", "ArmtekBy", "ShateM", "Avd", "Berg", "Motex", "MlAuto", "MlAutoRu", "ForumAuto", "ForumAut
 0590                    "ForumAutoRostov", "ForumAutoIstra", "FavoritParts", "FavoritPartsIstra", "FavoritPartsRostov", "Mos
 0591                    "Nikei", "MikadoMskHod20", "Japarts", "ZZapMoscow" }
 0592                .ToDictionary(k => k, SupplierQuotationProviders.Info), jsonOptions));
 593    </script>
 594    <script src="~/js/Studio2/Grid/bsx-directory.js" asp-append-version="true"></script>
 0595    @if (Model.LabelPrints.Any())
 596    {
 597        <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js" crossorigin="anonymous"></script
 598    }
 599    <script src="~/js/Studio2/OrdersGrid/orders-grid-feed-explain.js" asp-append-version="true"></script>
 600    <script src="~/js/Studio2/Orders/order-history-grids.js" asp-append-version="true"></script>
 601    <script src="~/js/Studio2/Orders/order-history-tx-comment.js" asp-append-version="true"></script>
 602    <partial name="~/Areas/Studio2/Views/Partials/Transaction/_TransactionCalculationModal.cshtml" />
 603    <script src="~/js/Studio2/Orders/Transactions/transaction-calculation-explainer.js" asp-append-version="true"></scri
 604    <script src="~/js/Studio2/Orders/order-history-bitrix-sync.js" asp-append-version="true"></script>
 0605    @if (!string.IsNullOrWhiteSpace(Model.ShipmentNumber))
 606    {
 607        <script src="~/js/Studio2/Orders/order-history-finance.js" asp-append-version="true"></script>
 608    }
 0609    @if (!string.IsNullOrWhiteSpace(Model.Article))
 610    {
 611        <script src="~/js/Studio2/Orders/order-history-movement-map.js" asp-append-version="true"></script>
 612    }
 0613    @if (isAdmin)
 614    {
 615        <script src="~/js/Studio2/Orders/order-history-rollback.js" asp-append-version="true"></script>
 616    }
 617}

Methods/Properties

ExecuteAsync()
<ExecuteAsync()