< Summary

Information
Class: AspNetCoreGeneratedDocument.Areas_Studio2_Views_Partials_Transaction__TransactionTableBody
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/Partials/Transaction/_TransactionTableBody.cshtml
Line coverage
0%
Covered lines: 0
Uncovered lines: 413
Coverable lines: 413
Total lines: 1502
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 710
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%3463325880%
Col()0%620%
<ExecuteAsync()0%420200%
<ExecuteAsync()100%210%
<ExecuteAsync()100%210%
<ExecuteAsync()100%210%
<ExecuteAsync()0%7280%
<ExecuteAsync()100%210%
<ExecuteAsync()0%7280%
<ExecuteAsync()100%210%
<ExecuteAsync()0%2040%
<ExecuteAsync()0%2040%
GetPriceTextColorClass(...)0%210140%
GetIndexColor(...)0%2040%
GetColorIndexHex(...)0%210140%
GetPriceTooltip(...)0%600240%
Money()100%210%
Row()100%210%
GetPromotionTooltip(...)0%110100%
Value()100%210%
Text()0%620%
Date()0%2040%
GetColorIndexDescription(...)0%210140%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/Partials/Transaction/_TransactionTableBody.cshtml

#LineLine coverage
 1@using OzonDomains.Models
 2@using OzonDomains
 3@using OzonOrdersWeb.Areas.Studio2.Controllers.Base
 4@using OzonOrdersWeb.Areas.Studio2.ViewHelpers
 5@model OzonOrdersWeb.ViewModels.OrderViewModels.MultiplayEditOrderViewModel
 6@{
 07    var fieldMap = Model.OrderListFieldReadAccess;
 08    var requirePurchasePrice = ViewData["RequirePurchasePrice"] is not bool configuredRequirePurchasePrice || configured
 09    bool Col(string fieldName) => fieldMap == null || fieldMap.GetValueOrDefault(fieldName, true);
 10}
 11
 12@{
 013    var qtyZeroStatus = ViewData["QuantityZeroStatusName"] as string ?? "К отмене";
 14}
 15
 16<script src="~/js/copyArticle.js"></script>
 17
 18<tbody>
 19
 20@{
 021    int? previousShipmentId = null;
 022    var shipmentGroups = new Dictionary<int, int>();
 023    foreach (var o in Model.Orders.Where(o => o?.ShipmentId != null))
 24    {
 025        var sid = o!.ShipmentId!.Value;
 026        shipmentGroups[sid] = shipmentGroups.GetValueOrDefault(sid) + 1;
 27    }
 28}
 29
 030@for (int i = 0; i < Model.Orders.Count; i++)
 31{
 032    var order = Model.Orders[i];
 033    bool isFirstInGroup = false;
 034    bool isInGroup = false;
 35
 036    if (order?.ShipmentId is int shipmentId)
 37    {
 038        var groupSize = shipmentGroups.GetValueOrDefault(shipmentId, 1);
 039        isInGroup = groupSize > 1;
 040        isFirstInGroup = isInGroup && shipmentId != previousShipmentId;
 041        previousShipmentId = shipmentId;
 42    }
 43    else
 44    {
 045        previousShipmentId = null;
 46    }
 47
 48    // Базовый класс строки — по статусу заказа (обновляется при смене селекта)
 049    string rowClass = "custom-white";
 050    if ((order?.AppStatus?.Name == "Отменен" && (order.ClientStatus?.Name == "Отменён" || order.ClientStatus == null)) |
 051        order?.ClientStatus?.Name is "Отменен" or "Отменён")
 52    {
 053        rowClass = "table-danger";
 54    }
 055    else if (order?.IsReturnable == true)
 56    {
 057        rowClass = "custom-red";
 58    }
 059    else if (order != null)
 60    {
 061        rowClass = order.AppStatus?.Name switch
 062        {
 063            "Заказан поставщику" => "table-success",
 064            "Отгружен поставщиком" => "table-info",
 065            "Отгружен реализатору" => "table-warning",
 066            "Заказан реализатору" => "table-secondary",
 067            _ => rowClass
 068        };
 69    }
 70
 071    if (isFirstInGroup)
 72    {
 073        rowClass += " shipment-group-first";
 74    }
 075    else if (isInGroup && !isFirstInGroup)
 76    {
 077        rowClass += " shipment-group-continuation";
 78    }
 79
 80    <tr id="orderRow_@Model.Orders[i].Id" class="@rowClass transaction-order-row"
 081        data-order-id="@Model.Orders[i].Id"
 082        data-min-commission-info="@(Model.Orders[i].MinCommissionInfo ?? "")"
 083        data-max-commission-info="@(Model.Orders[i].MaxCommissionInfo ?? "")"
 084        data-shipment-number="@(Model.Orders[i].ShipmentNumber ?? "")"
 085        data-ozon-client-id="@(Model.Orders[i].OzonClientId ?? Model.Orders[i].OzonClient?.Id)"
 086        data-client-type="@(Model.Orders[i].OzonClient?.ClientType.ToString() ?? "")"
 087        data-client-name="@(Model.Orders[i].OzonClient?.Name ?? "")"
 088        data-order-quantity="@Model.Orders[i].Quantity"
 089        data-barcode="@(Model.Orders[i].ProductInfo?.Barcode ?? "")"
 090        data-article="@Html.Raw(Model.Orders[i].Article ?? "")"
 091        data-cleaned-article="@Html.Raw(Model.Orders[i].CleanedArticle ?? "")"
 092        data-product-name="@Html.Raw(Model.Orders[i].ProductName ?? "")"
 093        data-producer="@Html.Raw(Model.Orders[i].EtProducer?.Name ?? "")"
 094        data-true-quantity="@(Model.Orders[i].TrueQuantity ?? Model.Orders[i].Quantity ?? 1)"
 95        @* Кратность упаковки из ключа вида ARTICLE=BRAND[N]: заказ на 2 упаковки списывает 2×N штук.
 96           Нужна проверке остатков 1С, когда пользователь правит количество прямо в форме. *@
 097        data-pack-multiplier="@(Model.Orders[i].Quantity is > 0 && Model.Orders[i].TrueQuantity is > 0
 098                                  ? Model.Orders[i].TrueQuantity!.Value / Model.Orders[i].Quantity!.Value
 099                                  : 1)"
 0100        data-shipment-warehouse-id="@(Model.Orders[i].ShipmentWarehouseId ?? Model.Orders[i].ShipmentWarehouse?.Id)"
 0101        data-shipment-warehouse-name="@(Model.Orders[i].ShipmentWarehouse?.Name ?? "")"
 0102        data-original-row-class="@rowClass">
 0103        @if (Col("ShipmentNumber"))
 104        {
 105            <td>
 106                <div class="two-row-cell">
 107                    <div class="cell-row" style="justify-content: flex-start;">
 108                        <span onclick="copyArticle(this)" role="button" class="d-inline-block">
 0109                            @if (isInGroup && !isFirstInGroup)
 110                            {
 111                                <span class="shipment-group-indicator"
 112                                      title="Часть отправления @(order?.ShipmentId)">
 113                                    <i class="fas fa-level-up-alt fa-rotate-90 fa-lg"></i>
 114                                </span>
 115                            }
 116                            else
 117                            {
 0118                                @Html.DisplayFor(modelItem => Model.Orders[i].ShipmentNumber)
 119                            }
 0120                            @if (!(isInGroup && !isFirstInGroup))
 121                            {
 0122                                var shipmentNumber = Model.Orders[i].ShipmentNumber;
 0123                                var shipmentNumberKey = !string.IsNullOrWhiteSpace(shipmentNumber)
 0124                                    ? shipmentNumber.Split('-')[0]
 0125                                    : null;
 0126                                var shipmentNumberCount = 0;
 0127                                var foundShipmentNumberCount = 0;
 0128                                var hasShipmentNumberCount = shipmentNumberKey != null
 0129                                    && Model.UniqueNumbers != null
 0130                                    && Model.UniqueNumbers.TryGetValue(shipmentNumberKey, out foundShipmentNumberCount);
 0131                                if (hasShipmentNumberCount)
 132                                {
 0133                                    shipmentNumberCount = foundShipmentNumberCount;
 134                                }
 0135                                if (hasShipmentNumberCount && shipmentNumberCount != 1)
 136                                {
 0137                                    <a href="#">(@shipmentNumberCount)</a>
 138                                }
 139                            }
 140                        </span>
 141                    </div>
 142                    <div class="cell-row text-muted" style="white-space: nowrap; justify-content: flex-start;">
 143                        @{
 0144                            string? delivery = Model.Orders[i].Delivery?.Provider?.Name;
 145
 0146                            if (!string.IsNullOrEmpty(delivery) && delivery.Length > 20)
 147                            {
 0148                                delivery = delivery.Substring(0, 20) + "...";
 149                            }
 150
 0151                            <div title="@Model.Orders[i].Delivery?.Name">@delivery</div>
 152                        }
 153                    </div>
 154                </div>
 155            </td>
 156        }
 157        <td style="vertical-align: middle;">
 158            @* Скрытые поля заказа держим внутри ячейки: <div> прямым потомком <tr> — невалидный
 159               HTML, браузер выносит такие блоки за пределы таблицы (foster parenting), и тогда
 160               удаление строки в JS не убирает её поля из формы.
 161               Orders.index задаёт индексы явно: удалённая строка уносит с собой и своё поле index,
 162               а без него model binder оборвал бы коллекцию на первом пропущенном номере. *@
 163            <input type="hidden" name="Orders.index" value="@i"/>
 164            <div class="form-group" style="display: none">
 165                <label asp-for="Orders[i].Id" class="control-label"></label><span
 166                    style="margin-left: 10px; color: gray;">/ </span>
 167                <input asp-for="Orders[i].Id" class="form-control"/>
 168                <span asp-validation-for="Orders[i].Id" class="text-danger"></span>
 169            </div>
 170            @* Статус маркетплейса читают фильтры заказов (StandardOrderFilter и фильтр
 171               возврата покупателя) прямо у объекта, пришедшего с формы. *@
 172            <div class="form-group" style="display: none">
 173                <input asp-for="Orders[i].Status" class="form-control"/>
 174            </div>
 175            <div class="form-group" style="display: none">
 176                <label asp-for="Orders[i].ShipmentNumber" class="control-label"></label><span
 177                    style="margin-left: 10px; color: gray;">/ </span>
 178                <input asp-for="Orders[i].ShipmentNumber" class="form-control"/>
 179                <span asp-validation-for="Orders[i].ShipmentNumber" class="text-danger"></span>
 180            </div>
 181            <div class="form-group" style="display: none">
 182                <label asp-for="Orders[i].Price" class="control-label"></label><span
 183                    style="margin-left: 10px; color: gray;">/ </span>
 0184                <input asp-for="Orders[i].Price" class="form-control price-sale-field" data-order-id="@Model.Orders[i].I
 185                <span asp-validation-for="Orders[i].Price" class="text-danger"></span>
 186            </div>
 187
 188
 189
 190
 191            <div class="form-group" style="display: none">
 192                <label asp-for="Orders[i].OzonClientId" class="control-label"></label>
 193                <input asp-for="Orders[i].OzonClientId" class="form-control"
 0194                       value="@(Model.Orders[i].OzonClientId ?? Model.Orders[i].OzonClient?.Id)"/>
 195                <span asp-validation-for="Orders[i].OzonClientId" class="text-danger"></span>
 196            </div>
 197            <div class="form-group" style="display: none">
 198                <label asp-for="Orders[i].OzonClient.Id" class="control-label"></label><span
 199                    style="margin-left: 10px; color: gray;">/ </span>
 200                <input asp-for="Orders[i].OzonClient.Id" class="form-control"
 0201                       value="@(Model.Orders[i].OzonClient?.Id ?? Model.Orders[i].OzonClientId)"/>
 202                <span asp-validation-for="Orders[i].OzonClient.Id" class="text-danger"></span>
 203            </div>
 204            <div class="form-group" style="display: none">
 205                <label asp-for="Orders[i].Article" class="control-label"></label><span
 206                    style="margin-left: 10px; color: gray;">/ </span>
 0207                <input asp-for="Orders[i].Article" class="form-control" value="@Model.Orders[i].Article"/>
 208                <span asp-validation-for="Orders[i].Article" class="text-danger"></span>
 209            </div>
 210
 211            <div class="form-group" style="display: none">
 212                <label asp-for="Orders[i].ShipmentWarehouse.Name" class="control-label"></label><span
 213                    style="margin-left: 10px; color: gray;">/ </span>
 214                <input asp-for="Orders[i].ShipmentWarehouse.Name" class="form-control"
 0215                       value="@Model.Orders[i].ShipmentWarehouse?.Name"/>
 216                <span asp-validation-for="Orders[i].ShipmentWarehouse" class="text-danger"></span>
 217            </div>
 218
 219            <div class="two-row-cell">
 220                <div class="cell-row" style="justify-content: flex-start;">
 0221                    @if (Col("OzonClient"))
 222                    {
 223                        <div title="@(Model.Orders[i].OzonClient?.Name ?? "")">
 0224                            @if (Model.Orders[i].OzonClient != null)
 225                            {
 0226                                string ozonClient = Model.Orders[i].OzonClient?.Name ?? "";
 0227                                if (ozonClient.Length > 18)
 228                                {
 0229                                    ozonClient = ozonClient.Substring(0, 18) + "...";
 230                                }
 231
 0232                                @ozonClient
 233                            }
 234                        </div>
 235                    }
 236                </div>
 237                <div class="cell-row text-muted" style="justify-content: flex-start; white-space: nowrap;">
 0238                    @if (Col("DeliveryCity"))
 239                    {
 0240                        @if (Model.Orders[i].DeliveryCity != null && Model.Orders[i].DeliveryCity != "")
 241                        {
 242                            <div title="@Model.Orders[i].DeliveryCity">
 243                                @{
 0244                                    string? deliveryCity = Model.Orders[i].DeliveryCity;
 0245                                    if (!string.IsNullOrEmpty(deliveryCity) && deliveryCity.Length > 11)
 246                                    {
 0247                                        deliveryCity = deliveryCity.Substring(0, 11) + "...";
 248                                    }
 249
 0250                                    @deliveryCity
 251                                }
 0252                                @if (Model.Orders[i].DeliveryCity is { } dcKey && Model.UniqueDeliveryCitys.ContainsKey(
 253                                {
 254                                    <a href="#"
 0255                                       style="margin-left: 2px;">(@Model.UniqueDeliveryCitys[dcKey])</a>
 256                                }
 257                            </div>
 258                        }
 259                    }
 260                </div>
 0261                @if (!string.IsNullOrEmpty(Model.Orders[i].TrackingNumber))
 262                {
 263                    <div class="cell-row text-muted" style="white-space: nowrap;" title="@Model.Orders[i].TrackingNumber
 0264                        <small>@Model.Orders[i].TrackingNumber</small>
 265                    </div>
 266                }
 267            </div>
 268        </td>
 269
 270        <td data-column="dates">
 271            <div class="two-row-cell">
 0272                @if (Col("ProcessingDate"))
 273                {
 274                    <div class="cell-row"
 275                         id="td2ProcessingDate"
 276                         title="@Html.DisplayFor(modelItem => Model.Orders[i].FullFormattedProcessingDate)">
 0277                        @OrderDateDisplay.FormatTableDateFull(Model.Orders[i].ProcessingDate)
 278                    </div>
 279                }
 280
 0281                @if (Col("ShippingDate"))
 282                {
 283                    <div class="cell-row text-muted"
 284                         title="@Html.DisplayFor(modelItem => Model.Orders[i].FullFormattedShippingDate)">
 0285                        @OrderDateDisplay.FormatTableDateFull(Model.Orders[i].ShippingDate)
 286                    </div>
 287                }
 288            </div>
 289        </td>
 290
 291        <td data-column="period">
 292            <div class="two-row-cell">
 0293                @if (Col("DeliveryPeriod"))
 294                {
 295                    <div class="cell-row orders-period-row">
 296                        <div title="@Html.DisplayFor(modelItem => Model.Orders[i].FormattedDeliveryDay) @Html.DisplayFor
 0297                            @Html.DisplayFor(modelItem => Model.Orders[i].FormattedDeliveryDay)
 298                        </div>
 299                    </div>
 300                    <div class="cell-row text-muted orders-period-row">
 0301                        <div>@Html.DisplayFor(modelItem => Model.Orders[i].FormattedTimeLeftDay)</div>
 302                    </div>
 303                }
 304            </div>
 305        </td>
 306
 307        <td class="orders-product-col" style="vertical-align: middle;" data-column="productInfo">
 308            <div class="d-flex orders-product-cell">
 0309                @if (!string.IsNullOrEmpty(Model.Orders[i].ProductInfo?.ImageUrl))
 310                {
 311                    <img src="@(Model.Orders[i].ProductInfo?.ImageUrl ?? "")"
 312                         alt="@(Model.Orders[i].ProductName ?? Model.Orders[i].Article)"
 313                         class="product-thumb flex-shrink-0"
 314                         onerror="this.style.display='none'"
 315                         loading="lazy" />
 316                }
 317                <div class="orders-product-text-grid" style="min-width: 0; flex: 1; overflow: hidden; display: grid; gri
 318                    <div class="orders-product-top-row">
 0319                    @if (Col("ProductKey"))
 320                    {
 321                        <div id="thArticle" class="product-key-cell d-flex align-items-center mb-0"
 322                             title="@Model.Orders[i].ProductKey">
 323                            @{
 0324                                string? productKey = Model.Orders[i].ProductKey;
 0325                                string? displayText = productKey;
 326
 0327                                if (!string.IsNullOrEmpty(productKey))
 328                                {
 329                                    // Регулярное выражение для поиска [число] в конце строки
 0330                                    var match = System.Text.RegularExpressions.Regex.Match(productKey, @"\[(\d+)\]$");
 331
 0332                                    if (match.Success)
 333                                    {
 334                                        // Разделяем строку на основную часть и числовую часть в скобках
 0335                                        string mainPart = productKey.Substring(0, match.Index);
 0336                                        string numberPart = match.Value; // [число]
 337
 338                                        // Обрезаем основную часть если нужно
 0339                                        if (mainPart.Length > 17)
 340                                        {
 0341                                            mainPart = mainPart.Substring(0, 17) + "...";
 342                                        }
 343
 344                                        // Формируем текст с подсветкой
 0345                                        displayText = $"{mainPart}<span class='text-danger fw-bold product-key-multiplie
 346                                    }
 347                                    else
 348                                    {
 349                                        // Старая логика обрезки если нет [числа] в конце
 0350                                        if (productKey.Length > 17)
 351                                        {
 0352                                            displayText = productKey.Substring(0, 17) + "...";
 353                                        }
 354                                    }
 355                                }
 356                            }
 357
 358                            <span class="custom-product-link">
 0359                                @Html.Raw(displayText)
 360                            </span>
 361
 362                            <button type="button" class="btn-link p-0 hidden-info-icon"
 363                                    style="background-color: inherit; width: 20px; height: 20px; display: none;"
 0364                                    data-order-id="@Model.Orders[i].Id">
 365                                <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor"
 366                                     class="bi bi-check" viewBox="0 0 16 16">
 367                                    <path
 368                                        d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.38
 369                                </svg>
 370                            </button>
 371                        </div>
 372                    }
 0373                    @if (Col("Manufacturer"))
 374                    {
 375                        <div id="thManufacturer" class="product-manufacturer-cell d-flex flex-column mb-0 nowrap text-ce
 0376                            @(Model.Orders[i].EtProducer?.Name ?? "")
 377                        </div>
 378                    }
 0379                    @if (Col("Article"))
 380                    {
 381                        <div id="thArticle2"
 382                             class="article-and-replacement-cell d-flex flex-column mb-0 nowrap text-end align-items-end
 0383                            @if (Model.Orders[i].Article != null)
 384                            {
 385                                <div role="button"
 386                                     title="Нажмите, чтобы скопировать"
 387                                     onclick="copyArticle(this)">
 0388                                    @Html.DisplayFor(modelItem => Model.Orders[i].Article)
 389                                </div>
 390                            }
 391                        </div>
 392                    }
 393                    </div>
 394                    <div class="d-flex align-items-center flex-nowrap orders-product-name-row" style="min-width: 0; over
 0395                    @if (Col("ProductName"))
 396                    {
 0397                        var productName = Model.Orders[i].ProductName;
 0398                        var truncatedName = string.IsNullOrEmpty(productName)
 0399                            ? productName
 0400                            : (productName.Length > 110 ? productName.Substring(0, 110) + "..." : productName);
 401
 402                        <div id="thProductName" class="text-muted" title="@productName"
 403                             style="overflow: hidden; white-space: nowrap; text-overflow: ellipsis; width: 100%; flex: 1
 404                            <a href="http://studio-web-client.interparts.ru?producerId=@Model.Orders[i].EtProducer?.Id&c
 405                               target="_blank"
 406                               class="custom-product-link">
 0407                                @truncatedName
 408                            </a>
 409                        </div>
 410                    }
 411                    <div class="orders-product-preferred-btns d-flex flex-shrink-0" style="gap: 2px;">
 412                        <!-- Поставщик (зеленый) -->
 413                        <button type="button" class="btn-link p-0 preferred-supplier"
 414                                style="background: none; border: none; width: 12px; height: 12px; display: none; margin-
 0415                                data-order-id="@Model.Orders[i].Id"
 416                                title="Поставщик">
 417                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#28a745">
 418                                <path
 419                                    d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0
 420                            </svg>
 421                        </button>
 422
 423                        <!-- Tradesoft (зеленый) -->
 424                        <button type="button" class="btn-link p-0 preferred-tradesoft"
 425                                style="background: none; border: none; width: 12px; height: 12px; display: none; margin-
 0426                                data-order-id="@Model.Orders[i].Id"
 427                                title="Tradesoft">
 428                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#28a745">
 429                                <path
 430                                    d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0
 431                            </svg>
 432                        </button>
 433
 434                        <!-- Склад (зеленый) -->
 435                        <button type="button" class="btn-link p-0 preferred-stock"
 436                                style="background: none; border: none; width: 12px; height: 12px; display: none; margin-
 0437                                data-order-id="@Model.Orders[i].Id"
 438                                title="Склад">
 439                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#28a745">
 440                                <path
 441                                    d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0
 442                            </svg>
 443                        </button>
 444
 445                        <!-- ABCP (синий) -->
 446                        <button type="button" class="btn-link p-0 preferred-abcp"
 447                                style="background: none; border: none; width: 12px; height: 12px; display: none; margin-
 0448                                data-order-id="@Model.Orders[i].Id"
 449                                title="ABCP">
 450                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#1976d2">
 451                                <path
 452                                    d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0
 453                            </svg>
 454                        </button>
 455
 456                        <!-- AVD (голубой, text-info) -->
 457                        <button type="button" class="btn-link p-0 preferred-avd"
 458                                style="background: none; border: none; width: 12px; height: 12px; display: none; margin-
 0459                                data-order-id="@Model.Orders[i].Id"
 460                                title="AVD">
 461                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#17a2b8">
 462                                <path
 463                                    d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0
 464                            </svg>
 465                        </button>
 466
 467                        <!-- Interparts (желтый, text-warning) -->
 468                        <button type="button" class="btn-link p-0 preferred-interparts"
 469                                style="background: none; border: none; width: 12px; height: 12px; display: none; margin-
 0470                                data-order-id="@Model.Orders[i].Id"
 471                                title="Studio1">
 472                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#ffc107">
 473                                <path
 474                                    d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0
 475                            </svg>
 476                        </button>
 477
 478                        <!-- Armtek -->
 479                        <button type="button" class="btn-link p-0 preferred-armtek"
 480                                style="background: none; border: none; width: 12px; height: 12px; display: none; margin-
 0481                                data-order-id="@Model.Orders[i].Id"
 482                                title="Armtek">
 483                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#f0ad4e">
 484                                <path
 485                                    d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0
 486                            </svg>
 487                        </button>
 488
 489                        <!-- Yumbo (оранжевый) -->
 490                        <button type="button" class="btn-link p-0 preferred-yumbo"
 491                                style="background: none; border: none; width: 12px; height: 12px; display: none; margin-
 0492                                data-order-id="@Model.Orders[i].Id"
 493                                title="Yumbo">
 494                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#fd7e14">
 495                                <path
 496                                    d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0
 497                            </svg>
 498                        </button>
 0499                        <span class="tx-api-supplier-indicators" data-order-id="@Model.Orders[i].Id" aria-live="polite">
 0500                        <span class="tx-cart-selection-slot" data-order-id="@Model.Orders[i].Id" aria-live="polite"></sp
 501                    </div>
 502                    </div>
 503                </div>
 504            </div>
 505        </td>
 506
 507        <td class="permanent-col actions-col-cell" data-column="actionsCol">
 508            <div class="actions-col-stack">
 509                <button type="button" class="btn-link p-0 delete-order actions-col-btn"
 0510                        data-order-id="@Model.Orders[i].Id" title="Удалить">
 511                    <svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor"
 512                         class="bi bi-trash-fill" viewBox="0 0 16 16">
 513                        <path
 514                           d="M2.5 1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1H3v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4h.5a1 1 0 0 0 1-1V
 515                    </svg>
 516                    <span class="visually-hidden">Удалить</span>
 517                </button>
 518                <button type="button"
 519                        class="btn-link p-0 print-label-btn actions-col-btn"
 520                        title="Печать этикетки по артикулу"
 521                        style="color: #6c757d;"
 522                        onclick="generateArticleLabel(@Model.Orders[i].Id, '@(Model.Orders[i].EtProducer?.Name ?? "")', 
 523                    <svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-upc-
 524                        <path d="M1.5 1a.5.5 0 0 0-.5.5v3a.5.5 0 0 1-1 0v-3A1.5 1.5 0 0 1 1.5 0h3a.5.5 0 0 1 0 1zM11 .5a
 525                    </svg>
 526                    <span class="visually-hidden">Этикетка</span>
 527                </button>
 528                <button type="button"
 529                        class="btn-link p-0 get-full-order-info actions-col-btn"
 0530                        data-order-id="@Model.Orders[i].Id"
 0531                        data-order-article="@Model.Orders[i].Article"
 0532                        data-order-producer-id="@Model.Orders[i].EtProducer?.Id"
 0533                        data-order-producer-name="@Model.Orders[i].EtProducer?.Name"
 534                        title="Подробнее">
 535                    <svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor"
 536                         class="bi bi-chevron-double-down" viewBox="0 0 16 16">
 537                        <path fill-rule="evenodd"
 538                              d="M1.646 6.646a.5.5 0 0 1 .708 0L8 12.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1
 539                        <path fill-rule="evenodd"
 540                              d="M1.646 2.646a.5.5 0 0 1 .708 0L8 8.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-
 541                    </svg>
 542                    <span class="visually-hidden">Info</span>
 543                </button>
 544            </div>
 545        </td>
 546
 547        <td style="vertical-align: middle;" data-column="status">
 548            <div class="two-row-cell order-status-cell">
 549                <div class="cell-row order-status-cell__client">
 0550                    @if (Col("ClientStatus"))
 551                    {
 552                        <div class="status-select-stack">
 553                            <div class="ozon-client-status-line nowrap">
 0554                            @if (Model.Orders[i].Status != null)
 555                            {
 0556                                string? status = Model.Orders[i].Status;
 0557                                if (!string.IsNullOrEmpty(status) && status.Length > 14)
 558                                {
 0559                                    status = status.Substring(0, 14) + "...";
 560                                }
 561
 0562                                @status
 563                            }
 564                            else
 565                            {
 566                                <br/>
 567                            }
 568                            </div>
 569                        </div>
 570                    }
 571                </div>
 572
 573                <div class="cell-row text-muted order-status-cell__app">
 0574                    @if (Col("AppStatus"))
 575                    {
 576                        @* Причина, по которой отмена в Ozon недоступна, раньше висела подписью под
 577                           селектом и съедала полосу под сам селект. Текст тот же, но в подсказке. *@
 0578                        Model.OzonPostingCancelReasons.TryGetValue(Model.Orders[i].Id, out var ozonCancelInfo);
 0579                        var ozonCancelHint = ozonCancelInfo is { CanCancelInOzon: false }
 0580                                             && !string.IsNullOrWhiteSpace(ozonCancelInfo.ErrorMessage)
 0581                            ? ozonCancelInfo.ErrorMessage
 0582                            : null;
 583
 584                        <div class="status-select-stack">
 585                            <div class="status-select-row d-flex align-items-start gap-1">
 586                                <div class="status-select-column" title="@ozonCancelHint">
 587                                    <select asp-for="Orders[i].AppStatusId"
 588                                            class="form-control form-control-sm status-select custom-select-init"
 589                                            id="AppStatusId_@Model.Orders[i].Id"
 590                                            style="margin-bottom: 0; min-width: 0;">
 0591                                        @if (ViewBag.AppStatuses is IEnumerable<SelectListItem> appStatuses)
 592                                        {
 0593                                            foreach (var status in appStatuses)
 594                                            {
 0595                                                var color = ViewBag.StatusColors != null
 0596                                                    ? ViewBag.StatusColors[status.Value] as string
 0597                                                    : "#ffffff";
 0598                                                <option value="@status.Value" data-color="@color"
 599                                                        style="background-color: @color; color: @StatusColorContrast.Get
 0600                                                    @status.Text
 601                                                </option>
 602                                            }
 603                                        }
 604                                    </select>
 605                                </div>
 0606                                @if (Model.OrderStatusWarnings.TryGetValue(Model.Orders[i].Id, out var statusWarning) &&
 607                                {
 608                                    <span class="d-inline-flex align-items-center justify-content-center flex-shrink-0"
 609                                          title="@statusWarning"
 610                                          style="width: 20px; height: 20px; color: #7a4b00; font-size: 0.9rem; margin-to
 611                                        <i class="fas fa-triangle-exclamation"></i>
 612                                    </span>
 613                                }
 614                            </div>
 0615                            @if (ozonCancelInfo != null)
 616                            {
 0617                                @if (ozonCancelInfo.CanCancelInOzon)
 618                                {
 0619                                    bool linkedToStatusSelect = ViewBag.OzonCancelPanelLinkedToStatusSelect == true;
 620                                    <div class="ozon-cancel-panel"
 0621                                         data-order-id="@Model.Orders[i].Id"
 0622                                         data-posting-number="@Model.Orders[i].ShipmentNumber"
 0623                                         data-linked-to-status-select="@(linkedToStatusSelect ? "true" : "false")"
 0624                                         data-requires-message-reason-id="@Services.ApiServices.OzonApi.OzonOrderCancelR
 625                                         style="@(linkedToStatusSelect ? "display:none;" : "")">
 626                                        <label class="ozon-cancel-toggle" for="CancelInOzon_@Model.Orders[i].Id">
 0627                                            @if (linkedToStatusSelect)
 628                                            {
 629                                                <input type="checkbox"
 630                                                       class="ozon-cancel-checkbox"
 631                                                       name="CancelInOzon[@Model.Orders[i].Id]"
 632                                                       value="true"
 633                                                       id="CancelInOzon_@Model.Orders[i].Id"/>
 634                                            }
 635                                            else
 636                                            {
 637                                                <input type="checkbox"
 638                                                       class="ozon-cancel-checkbox"
 639                                                       name="CancelInOzon[@Model.Orders[i].Id]"
 640                                                       value="true"
 641                                                       id="CancelInOzon_@Model.Orders[i].Id"
 642                                                       checked/>
 643                                            }
 644                                            <span>Отменить в Ozon</span>
 645                                            <span class="ozon-cancel-info-badge badge bg-warning text-dark ms-1"
 646                                                  id="ozonCancelBadge_@Model.Orders[i].Id"
 647                                                  tabindex="0"
 648                                                  data-bs-toggle="popover"
 649                                                  data-bs-trigger="hover focus"
 650                                                  data-bs-placement="top"
 651                                                  data-bs-html="true"
 652                                                  data-bs-content="&lt;b&gt;Что произойдёт:&lt;/b&gt;&lt;ul class=&quot;
 653                                                <i class="fas fa-exclamation-triangle me-1"></i>Будет отменён в Ozon
 654                                            </span>
 655                                        </label>
 656                                        <div class="ozon-cancel-fields" id="ozonCancelFields_@Model.Orders[i].Id">
 657                                            <select class="form-control form-control-sm ozon-cancel-reason-select custom
 658                                                    name="OzonCancelReasonId[@Model.Orders[i].Id]"
 659                                                    id="OzonCancelReasonId_@Model.Orders[i].Id">
 660                                                <option value="">Выберите причину...</option>
 0661                                                @foreach (var reason in ozonCancelInfo.Reasons)
 662                                                {
 0663                                                    if (reason.Id == 352)
 664                                                    {
 0665                                                        <option value="@reason.Id" selected>@reason.Title</option>
 666                                                    }
 667                                                    else
 668                                                    {
 0669                                                        <option value="@reason.Id">@reason.Title</option>
 670                                                    }
 671                                                }
 672                                            </select>
 673                                            <input type="hidden"
 674                                                   class="ozon-cancel-reason-title"
 675                                                   name="OzonCancelReasonTitle[@Model.Orders[i].Id]"
 676                                                   id="OzonCancelReasonTitle_@Model.Orders[i].Id"
 677                                                   value=""/>
 678                                            <input type="text"
 679                                                   class="form-control form-control-sm ozon-cancel-message"
 680                                                   name="OzonCancelReasonMessage[@Model.Orders[i].Id]"
 681                                                   id="OzonCancelReasonMessage_@Model.Orders[i].Id"
 682                                                   placeholder="Комментарий к отмене (обязателен для «Другое»)"/>
 683                                        </div>
 684                                    </div>
 685                                }
 686                            }
 687                        </div>
 0688                        @if (Model.Orders[i].CartItemStatus != null)
 689                        {
 690                            <div class="text-info nowrap" style="font-size: 13px;"
 691                                 title="@(Model.Orders[i].CartItemStatus?.Name ?? "")">
 692                                @{
 0693                                    string? cartStatus = Model.Orders[i].CartItemStatus?.Name ?? "";
 0694                                    if (cartStatus.Length > 14)
 695                                    {
 0696                                        cartStatus = cartStatus.Substring(0, 14) + "...";
 697                                    }
 698                                }
 0699                                @cartStatus
 700                            </div>
 701                        }
 702
 0703                    }
 704                </div>
 705            </div>
 706        </td>
 707
 708        <td style="vertical-align: middle;" data-column="warehouse">
 709            <div class="two-row-cell">
 710                <div class="cell-row" style="justify-content: flex-start; gap: 6px;">
 0711                    @if (Col("ShipmentWarehouse"))
 712                    {
 0713                        @if (ViewBag.ShowWarehouseSelect != true)
 714                        {
 715                            <div
 716                                class="@(Model.Orders[i].UpdatedColumns is { } updatedColsBody && updatedColsBody.Contai
 717                                style="display: flex; align-items: center;">
 0718                                @(Model.Orders[i].ShipmentWarehouse?.Name)
 719                            </div>
 720                        }
 721                    }
 722                </div>
 723                @{
 0724                    var effectiveWarehouseForStock = TransactionWarehouseHelper.GetEffectiveWarehouseName(Model.Orders[i
 725                }
 726                <div class="cell-row text-muted">
 0727                    @if (Col("ShipmentWarehouse"))
 728                    {
 0729                        @if (ViewBag.ShowWarehouseSelect == true)
 730                        {
 731                            <select asp-for="Orders[i].ShipmentWarehouseId"
 732                                    class="form-control form-control-sm warehouse-select custom-select-init"
 733                                    id="ShipmentWarehouseId_@Model.Orders[i].Id"
 734                                    style="margin-bottom: 0; width: 100%;">
 735                                <option value="">Выберите склад</option>
 0736                                @foreach (var warehouse in Model.Warehouses)
 737                                {
 0738                                    <option value="@warehouse.Id"
 739                                            selected="@(Model.Orders[i].ShipmentWarehouseId == warehouse.Id)">
 0740                                        @warehouse.Name
 741                                    </option>
 742                                }
 743                            </select>
 744                        }
 745
 746                        <div class="form-group mb-0" style="width: 100%;">
 747                            @{
 748                                // Находим расширение с ключом "Засыл"
 0749                                var zasylValue = Model.Orders[i].OrderExtensions?
 0750                                    .FirstOrDefault(e => e.Key == "Засыл")?.Value;
 751                            }
 752
 753                            <div class="position-relative" style="width: 100%;">
 754                                <input type="text"
 755                                       name="orderSupplierMap[@Model.Orders[i].Id]"
 756                                       id="orderSupplierMap_@Model.Orders[i].Id"
 757                                       class="form-control form-control-sm zasyl-input combobox-input"
 758                                       placeholder="Засыл"
 759                                       value="@(zasylValue ?? string.Empty)"
 760                                       autocomplete="off"
 761                                       aria-haspopup="listbox"
 762                                       aria-expanded="false"
 763                                       style="width: 100%;"/>
 764
 765                                <ul class="list-group combobox-menu"
 766                                    id="zasylMenu_@Model.Orders[i].Id"
 767                                    style="position:absolute; z-index:9999; width:100%; max-height:200px; overflow:auto;
 0768                                    @foreach (var w in Model.Warehouses)
 769                                    {
 770                                        <li class="list-group-item list-group-item-action combobox-item"
 0771                                            data-value="@w.Name" role="option">@w.Name</li>
 772                                    }
 773                                </ul>
 774
 775                                <script>
 776                                    document.addEventListener('DOMContentLoaded', () => {
 0777                                        new Combobox('orderSupplierMap_@Model.Orders[i].Id', 'zasylMenu_@Model.Orders[i]
 778                                    });
 779                                </script>
 780
 781                            </div>
 782                        </div>
 783                    }
 784                </div>
 785                @* Остатки 1С — служебная третья полоса под засылом. Раньше блок стоял между
 786                   складом и засылом и появлялся только после асинхронной загрузки: строка
 787                   меняла высоту уже после отрисовки и разъезжалась с соседними.
 788                   Id по заказу, а не по артикулу: одинаковые артикулы давали дубль id. *@
 789                <div class="cell-row tx-stock-row">
 790                    <div id="bitrixStores_@Model.Orders[i].Id" class="tx-bitrix-stores"></div>
 0791                    @if (ViewBag.DisableBitrixAutoLoad != true)
 792                    {
 793                        <span class="d-none"
 794                              data-warehouse-stock-load="true"
 0795                              data-article="@Model.Orders[i].Article"
 0796                              data-manufacturer="@(Model.Orders[i].EtProducer?.Name ?? "")"
 0797                              data-warehouse="@effectiveWarehouseForStock"
 0798                              data-container-id="bitrixStores_@Model.Orders[i].Id"
 0799                              data-price-input-id="PurchasePrice_@Model.Orders[i].Id"
 800                              data-stock-source="onec"></span>
 801                    }
 802                    else
 803                    {
 804                        <button type="button"
 805                                class="btn btn-link btn-sm p-0 tx-stock-load-btn"
 806                                onclick="loadWarehouseStock('@Model.Orders[i].Article', '@Html.Raw(Model.Orders[i].EtPro
 807                            Загрузить остатки
 808                        </button>
 809                    }
 810                </div>
 811            </div>
 812        </td>
 813
 814        <td style="vertical-align: middle;" data-column="supplier">
 815            <div class="two-row-cell" style="gap: 1px; min-width: 0;">
 0816                @if (Col("Supplier"))
 817                {
 818                    <div class="cell-row form-group supplier-hover-area mb-0" style="width: 100%;">
 819                        <select asp-for="Orders[i].SupplierId" asp-items="@ViewBag.Suppliers"
 820                                class="form-control form-control-sm supplier-select custom-select-init"
 821                                id="SupplierId_@Model.Orders[i].Id"
 822                                style="width: 100%;">
 823                            <option value="">Выберите поставщика</option>
 0824                            @foreach (var supplier in Model.Suppliers)
 825                            {
 0826                                <option value="@supplier.Id"
 0827                                        data-currency-code="@supplier.CurrencyCode"
 0828                                        data-cost-factor="@supplier.CostFactor"
 0829                                        data-weight-factor="@supplier.WeightFactor"
 0830                                        data-weight-currency-code="@supplier.WeightFactorCurrencyCode"
 0831                                        data-site="@supplier.Site"
 0832                                        data-is-reseller="@supplier.IsReSeller">
 0833                                    @supplier.Name (@supplier.CurrencyCode)
 834                                </option>
 835                            }
 836                        </select>
 837                    </div>
 838                }
 839
 0840                @if (Col("SupplierOrderNumber"))
 841                {
 842                    <div class="cell-row form-group mb-0" style="width: 100%;">
 843                        <input asp-for="Orders[i].OrderNumberToSupplier" type="text"
 844                               class="form-control form-control-sm orderNumberToSupplie-field"
 845                               id="OrderNumberToSupplier_@Model.Orders[i].Id"
 846                               style="width: 100%;" placeholder="Номер зак. п."/>
 847                        <span asp-validation-for="Orders[i].OrderNumberToSupplier" class="text-danger"
 848                              id="OrderNumberToSupplierValidation_@Model.Orders[i].Id"></span>
 849                    </div>
 850                }
 851            </div>
 852        </td>
 853
 854        <td style="vertical-align: middle;" data-column="category">
 855            <div class="two-row-cell">
 856                <div class="cell-row category-name-row">
 0857                    @if (Col("Category"))
 858                    {
 0859                        @if (Model.Orders[i].ProductInfo?.CommercialCategory is { } categoryFull)
 860                        {
 0861                            var categoryDisplay = categoryFull.Length > 10
 0862                                ? categoryFull.Substring(0, 10) + "..."
 0863                                : categoryFull;
 864                            <span class="category-name-text" title="@categoryFull">
 0865                                @categoryDisplay
 866                            </span>
 867                        }
 868
 869                        <span class="category-volumetric text-muted">
 0870                            @(Model.Orders[i].ProductInfo?.VolumetricWeight)
 871                        </span>
 872                    }
 873                </div>
 874
 875                <div class="text-muted cell-row product-weight-row">
 0876                    @if (Col("VolumeWeight"))
 877                    {
 878                        <div class="form-group d-flex align-items-center product-weight-wrap mb-0">
 879                            <div class="form-group mb-0">
 880                                <input asp-for="Orders[i].ProductInfo.Weight"
 881                                       class="form-control form-control-sm product-weight-field"
 882                                       id="ProductWeight_@Model.Orders[i].Id"
 0883                                       value="@string.Format("{0:N0}", Model.Orders[i].ProductInfo?.Weight ?? 0).Replace
 884                                <span asp-validation-for="Orders[i].ProductInfo.Weight" class="text-danger"></span>
 885                            </div>
 886                            <div class="product-weight-label">
 887                                <span>Вес кг.</span>
 888                            </div>
 889                        </div>
 890                    }
 891                </div>
 892            </div>
 893        </td>
 894
 895        <td class="tx-calculation-cell" style="vertical-align: middle;">
 0896            @if (Col("ShipmentTotal"))
 897            {
 898                <button type="button" class="tx-calculation-help tx-calculation-help--shipment"
 899                        data-calculation-focus="shipment" aria-label="Показать расчёт суммы отправления"
 900                        title="Показать расчёт суммы отправления">
 901                    <i class="fas fa-dollar-sign" aria-hidden="true"></i>
 902                </button>
 903            }
 904            <div class="two-row-cell">
 905                <div class="cell-row">
 0906                    @if (Col("SitePrice"))
 907                    {
 908                        <div
 909                            style="text-align: right; display: flex; align-items: center; justify-content: flex-end; pad
 0910                            @Html.Raw(string.Format("{0:N0}", Model.Orders[i].ProductInfo?.CurrentPriceWithDiscount ?? 0
 911                        </div>
 912                    }
 913                </div>
 914                <div class="text-muted cell-row">
 0915                    @if (Col("Quantity"))
 916                    {
 0917                        var originalQuantity = Model.Orders[i].Quantity;
 918                        <div
 919                            style="text-align: right; position: relative; margin-top: 1px; display: flex; align-items: c
 920                            id="Quantity_@Model.Orders[i].Id">
 921                            <div
 922                                style="display: flex; align-items: center; justify-content: flex-end; gap: 2px; padding-
 923                                <!-- Показываем отклонение от оригинального количества -->
 924                                <span class="quantity-diff-display"
 0925                                      data-order-id="@Model.Orders[i].Id"
 926                                      style="min-width: 28px; text-align: right; font-size: 0.95em; color: #6c757d; font
 927                                </span>
 928
 929                                <!-- Поле ввода количества -->
 930                                <input asp-for="Orders[i].Quantity"
 931                                       type="number"
 932                                       min="0"
 933                                       max="@originalQuantity"
 934                                       class="form-control form-control-sm quantity-input-field"
 0935                                       data-original-quantity="@originalQuantity"
 0936                                       data-order-id="@Model.Orders[i].Id"
 937                                       title="При изменении количества будет создан новый заказ с отнятым остатком и исх
 938
 939                                <span asp-validation-for="Orders[i].Quantity" class="text-danger"></span>
 940                            </div>
 941                        </div>
 942                    }
 943                </div>
 944            </div>
 945        </td>
 946
 947        <td style="vertical-align: middle;">
 948            <div class="two-row-cell">
 949                <div class="cell-row">
 0950                    @if (Col("Price"))
 951                    {
 952                        <div
 953                            style="text-align: right; display: flex; align-items: center; justify-content: flex-end; wid
 954                            class="price-sale-display"
 955                            id="Price_@Model.Orders[i].Id">
 0956                            <span id="CurrentPriceWithDiscount_@Model.Orders[i].Id" class="d-none">@Model.Orders[i].Prod
 0957                            @if (Model.Orders[i].OzonPromotionSnapshot?.HasAnyPromotion == true)
 958                            {
 959                                <span class="ozon-promotion-icon"
 0960                                      data-app-tooltip-html="@GetPromotionTooltip(Model.Orders[i].OzonPromotionSnapshot)
 961                                      data-app-tooltip-variant="promotion"
 962                                      aria-label="Товар участвует в акции Ozon">
 963                                    <i class="fas fa-tags" aria-hidden="true"></i>
 964                                </span>
 965                            }
 966                            <span
 967                                class="@GetPriceTextColorClass(Model.Orders[i].OrderPrice?.PriceIndexes?.ColorIndex)"
 0968                                data-app-tooltip-html="@GetPriceTooltip(Model.Orders[i])"
 969                                data-app-tooltip-variant="price">
 0970                                @OrderCellFormat.Money(Model.Orders[i].Price)
 971                            </span>
 972                        </div>
 973                    }
 974                </div>
 975
 976                <div class="cell-row text-muted"
 977                     style="text-align: right; display: flex; align-items: center; justify-content: flex-end;">
 0978                    @if (Col("ShipmentTotal"))
 979                    {
 980                        <span id="shipmentAmount_@Model.Orders[i].Id"
 981                              style="text-align: right; display: flex; align-items: center; justify-content: flex-end;">
 0982                            @OrderCellFormat.Money(Model.Orders[i].ShipmentAmount)
 983                        </span>
 984                    }
 985                </div>
 986            </div>
 987        </td>
 988
 989        <td class="tx-calculation-cell" style="vertical-align: middle;" data-column="purchasePrice">
 0990            @if (Col("Cost"))
 991            {
 992                <button type="button" class="tx-calculation-help tx-calculation-help--cost"
 993                        data-calculation-focus="cost" aria-label="Показать расчёт себестоимости и конвертации"
 994                        title="Показать расчёт себестоимости и конвертации">
 995                    <i class="fas fa-dollar-sign" aria-hidden="true"></i>
 996                </button>
 997            }
 998            <div class="two-row-cell purchase-price-cell">
 999                <div class="cell-row purchase-price-top-row">
 01000                    @if (Col("PurchasePrice"))
 1001                    {
 01002                        decimal warehouseC0 = 0;
 01003                        if (Model.WarehouseStockC0ByOrderId != null)
 01004                            Model.WarehouseStockC0ByOrderId.TryGetValue(Model.Orders[i].Id, out warehouseC0);
 01005                        var c0ForAttr = warehouseC0 > 0
 01006                            ? warehouseC0
 01007                            : Model.Orders[i].OriginalPurchasePrice ?? 0;
 01008                        var warehouseC0Text = c0ForAttr > 0
 01009                            ? c0ForAttr.ToString(System.Globalization.CultureInfo.InvariantCulture)
 01010                            : "";
 1011
 1012                        <input asp-for="Orders[i].OriginalPurchasePrice"
 1013                               type="text"
 1014                               class="form-control form-control-sm purchase-price-field"
 1015                               id="PurchasePrice_@Model.Orders[i].Id"
 01016                               data-order-id="@Model.Orders[i].Id"
 01017                               data-price-required="@(requirePurchasePrice ? "true" : "false")"
 01018                               data-warehouse-c0="@warehouseC0Text"
 1019                               oninput="this.value = this.value.replace(',', '.').replace(/[^0-9.]/g, '').replace(/(\..*
 1020                        <input type="hidden" asp-for="Orders[i].PurchasePrice"
 1021                               id="PurchasePriceHidden_@Model.Orders[i].Id"/>
 1022                        <span class="purchase-price-rub visually-hidden"
 1023                              id="PurchasePriceRub_@Model.Orders[i].Id"
 1024                              aria-hidden="true"></span>
 1025                    }
 1026                </div>
 1027
 1028                <div class="cell-row text-muted purchase-price-bottom-row">
 01029                    @if (Col("Cost"))
 1030                    {
 1031                        <span id="CostPrice_@Model.Orders[i].Id"></span>
 1032                    }
 01033                    @if (Col("PurchasePrice"))
 1034                    {
 1035                        <a href="javascript:void(0)"
 1036                           class="toggleExtraExpensesLink"
 1037                           onclick="toggleExtraExpensesColumn()"
 1038                           title="Показать/скрыть дополнительные расходы">
 1039                            <i class="fas fa-plus-circle"></i> Доп. расх.
 1040                        </a>
 1041                    }
 1042                </div>
 1043            </div>
 1044        </td>
 1045
 1046        <!-- Столбец дополнительных расходов -->
 1047        <td class="extra-expenses-cell"
 1048            style="display: none; vertical-align: middle; width: 180px; max-width: 180px; ">
 1049            @{
 01050                var existingExpensesDict = ViewBag.ExistingExpenses as Dictionary<int, List<OrderExtraExpense>>
 01051                                               ?? new Dictionary<int, List<OrderExtraExpense>>();
 01052                var existingExpenses = existingExpensesDict.ContainsKey(Model.Orders[i].Id)
 01053                    ? existingExpensesDict[Model.Orders[i].Id]
 01054                    : new List<OrderExtraExpense>();
 1055            }
 1056            <div id="extraExpensesContainer_@Model.Orders[i].Id" class="extra-expenses-container h-100"
 01057                 data-order-id="@Model.Orders[i].Id" data-order-index="@i">
 01058                @if (existingExpenses.Any())
 1059                {
 01060                    @for (int expIdx = 0; expIdx < existingExpenses.Count; expIdx++)
 1061                    {
 01062                        var expense = existingExpenses[expIdx];
 01063                        var isFirst = expIdx == 0;
 01064                        var hasMultipleExpenses = existingExpenses.Count > 1;
 1065
 01066                        <div class="extra-expense-item two-row-cell" data-expense-index="@expIdx"
 1067                             style="gap: 8px; @(!isFirst ? "border-top: 1px solid #dee2e6; padding-top: 0.3rem; margin-t
 1068                            <div class="cell-row" style="display: flex; gap: 4px; align-items: center; width: 100%;">
 1069                                <select class="form-control form-control-sm expense-type-select custom-select-init"
 1070                                        name="Orders[@i].ExtraExpenses[@expIdx].ExpenseTypeId"
 1071                                        id="ExpenseTypeId_@(Model.Orders[i].Id)_@(expIdx)"
 01072                                        data-expense-amount-id="ExpenseAmount_@(Model.Orders[i].Id)_@(expIdx)"
 1073                                        style="width: 70px; font-size: 11px; padding: 0.2rem 0.3rem; height: auto;">
 1074                                    <option value="">Тип</option>
 01075                                    @if (ViewBag.ExpenseTypes != null)
 1076                                    {
 01077                                        @foreach (var expenseType in ViewBag.ExpenseTypes)
 1078                                        {
 01079                                            <option value="@expenseType.Id"
 1080                                                    selected="@(expense.ExpenseTypeId == expenseType.Id)">
 01081                                                @expenseType.Name
 1082                                            </option>
 1083                                        }
 1084                                    }
 1085                                    <option value="__ADD_NEW__" style="font-style: italic; color: #007bff;">+
 1086                                        Добавить новый...
 1087                                    </option>
 1088                                </select>
 1089                                <span class="text-danger expense-type-validation position-absolute"
 1090                                      id="ExpenseTypeValidation_@(Model.Orders[i].Id)_@(expIdx)"
 1091                                      style="display: none; font-size: 10px;">Заполните тип</span>
 1092
 1093                                <input type="number"
 1094                                       class="form-control form-control-sm expense-amount-input"
 1095                                       name="Orders[@i].ExtraExpenses[@expIdx].Amount"
 1096                                       id="ExpenseAmount_@(Model.Orders[i].Id)_@(expIdx)"
 01097                                       data-expense-type-id="ExpenseTypeId_@(Model.Orders[i].Id)_@(expIdx)"
 1098                                       placeholder="₽"
 1099                                       step="0.01"
 1100                                       min="0"
 1101                                       value="@expense.Amount.ToString("0.00", System.Globalization.CultureInfo.Invarian
 1102                                       style="width: 70px; font-size: 11px; padding: 0.2rem 0.3rem; height: auto;"/>
 1103                                <span class="text-danger expense-amount-validation position-absolute"
 1104                                      id="ExpenseAmountValidation_@(Model.Orders[i].Id)_@(expIdx)"
 1105                                      style="display: none; font-size: 10px;">Заполните сумму</span>
 1106
 01107                                @if (!isFirst || hasMultipleExpenses)
 1108                                {
 1109                                    <button type="button"
 1110                                            class="btn btn-sm btn-link text-danger remove-expense-btn p-0"
 01111                                            data-order-id="@Model.Orders[i].Id"
 01112                                            data-expense-index="@expIdx"
 1113                                            title="Удалить"
 1114                                            style="font-size: 12px; line-height: 1; border: none; background: none; box-
 1115                                            onmouseover="this.style.textDecoration='none'; this.style.opacity='0.7';"
 1116                                            onmouseout="this.style.textDecoration='none'; this.style.opacity='1';">
 1117                                        <i class="fas fa-times"></i>
 1118                                    </button>
 1119                                }
 1120                                else
 1121                                {
 1122                                    <button type="button"
 1123                                            class="btn btn-sm btn-link text-primary add-expense-btn p-0"
 01124                                            data-order-id="@Model.Orders[i].Id"
 01125                                            data-order-index="@i"
 1126                                            title="Добавить расход"
 1127                                            style="font-size: 12px; line-height: 1; border: none; background: none; box-
 1128                                            onmouseover="this.style.textDecoration='none'; this.style.opacity='0.7';"
 1129                                            onmouseout="this.style.textDecoration='none'; this.style.opacity='1';">
 1130                                        <i class="fas fa-plus"></i>
 1131                                    </button>
 1132                                }
 1133                            </div>
 1134                            <div class="cell-row align-items-end">
 1135                                <input type="text"
 1136                                       class="form-control form-control-sm expense-comment-input"
 1137                                       name="Orders[@i].ExtraExpenses[@expIdx].Comment"
 1138                                       id="ExpenseComment_@(Model.Orders[i].Id)_@(expIdx)"
 1139                                       placeholder="Комментарий"
 1140                                       maxlength="255"
 1141                                       value="@expense.Comment"
 1142                                       style="width: calc(70px + 70px + 4px + 24px); font-size: 11px; padding: 0.2rem 0.
 1143                            </div>
 1144                            <input type="hidden"
 1145                                   name="Orders[@i].ExtraExpenses[@expIdx].OrderId"
 1146                                   value="@Model.Orders[i].Id"/>
 1147                        </div>
 01148                    }
 1149                }
 1150                else
 1151                {
 1152                    <div class="extra-expense-item two-row-cell" data-expense-index="0" style="gap: 8px;">
 1153                        <div class="cell-row">
 1154                            <div style="display: flex; gap: 4px; align-items: flex-start;">
 1155                                <div>
 1156                                    <select class="form-control form-control-sm expense-type-select custom-select-init"
 1157                                            name="Orders[@i].ExtraExpenses[0].ExpenseTypeId"
 1158                                            id="ExpenseTypeId_@(Model.Orders[i].Id)_0"
 01159                                            data-expense-amount-id="ExpenseAmount_@(Model.Orders[i].Id)_0"
 1160                                            style="width: 70px; font-size: 11px; padding: 0.2rem 0.3rem; height: auto;">
 1161                                        <option value="">Тип</option>
 01162                                        @if (ViewBag.ExpenseTypes != null)
 1163                                        {
 01164                                            @foreach (var expenseType in ViewBag.ExpenseTypes)
 1165                                            {
 01166                                                <option value="@expenseType.Id">@expenseType.Name</option>
 1167                                            }
 1168                                        }
 1169                                        <option value="__ADD_NEW__" style="font-style: italic; color: #007bff;">+
 1170                                            Добавить новый...
 1171                                        </option>
 1172                                    </select>
 1173                                    <span class="text-danger expense-type-validation position-absolute"
 1174                                          id="ExpenseTypeValidation_@(Model.Orders[i].Id)_0"
 1175                                          style="display: none; font-size: 10px;">Заполните тип</span>
 1176                                </div>
 1177                                <div>
 1178                                    <input type="number"
 1179                                           class="form-control form-control-sm expense-amount-input"
 1180                                           name="Orders[@i].ExtraExpenses[0].Amount"
 1181                                           id="ExpenseAmount_@(Model.Orders[i].Id)_0"
 01182                                           data-expense-type-id="ExpenseTypeId_@(Model.Orders[i].Id)_0"
 1183                                           placeholder="₽"
 1184                                           step="0.01"
 1185                                           min="0"
 1186                                           style="width: 70px; font-size: 11px; padding: 0.2rem 0.3rem; height: auto;"/>
 1187                                    <span class="text-danger expense-amount-validation position-absolute"
 1188                                          id="ExpenseAmountValidation_@(Model.Orders[i].Id)_0"
 1189                                          style="display: none; font-size: 10px;">Заполните сумму</span>
 1190                                </div>
 1191                                <button type="button"
 1192                                        class="btn btn-sm btn-link text-primary add-expense-btn p-0"
 01193                                        data-order-id="@Model.Orders[i].Id"
 01194                                        data-order-index="@i"
 1195                                        title="Добавить расход"
 1196                                        style="font-size: 12px; line-height: 1; border: none; background: none; box-shad
 1197                                        onmouseover="this.style.textDecoration='none'; this.style.opacity='0.7';"
 1198                                        onmouseout="this.style.textDecoration='none'; this.style.opacity='1';">
 1199                                    <i class="fas fa-plus"></i>
 1200                                </button>
 1201                            </div>
 1202                        </div>
 1203                        <div class="cell-row align-items-end">
 1204                                <input type="text"
 1205                                       class="form-control form-control-sm expense-comment-input"
 1206                                       name="Orders[@i].ExtraExpenses[0].Comment"
 1207                                       id="ExpenseComment_@(Model.Orders[i].Id)_0"
 1208                                       placeholder="Комментарий"
 1209                                       maxlength="255"
 1210                                       style="width: calc(70px + 70px + 4px + 24px); font-size: 11px; padding: 0.2rem 0.
 1211                        </div>
 1212                        <input type="hidden"
 1213                               name="Orders[@i].ExtraExpenses[0].OrderId"
 1214                               value="@Model.Orders[i].Id"/>
 1215                    </div>
 1216                }
 1217            </div>
 1218        </td>
 1219
 01220        @if (Col("OzonCommission"))
 1221        {
 01222            @if (Model.Orders[i].MinOzonCommission == 0 && Model.Orders[i].MaxOzonCommission == 0)
 1223            {
 1224                <td class="tx-metric-col tx-metric-col--explainable" style="text-align: right; vertical-align: middle;">
 1225                    <button type="button" class="tx-calculation-help" data-calculation-focus="commission"
 1226                            aria-label="Показать расчёт комиссии" title="Показать расчёт">
 1227                        <i class="fas fa-dollar-sign" aria-hidden="true"></i>
 1228                    </button>
 1229                    <div class="two-row-cell">
 1230                        <div class="cell-row justify-content-end">
 1231                            <div class="form-group d-flex align-items-center justify-content-end mb-0">
 1232                                <div style="display: flex; align-items: center;">
 1233                                    <input asp-for="Orders[i].MinOzonCommission"
 1234                                           class="form-control form-control-sm ozon-commission-field"
 1235                                           id="MinOzonCommission_@Model.Orders[i].Id"
 1236                                           style="text-align: right; width: 48px;"/>
 1237                                    <span asp-validation-for="Orders[i].MinOzonCommission" class="text-danger"
 1238                                          id="MinOzonCommissionValidation_@Model.Orders[i].Id"></span>
 1239                                </div>
 1240                                <div style="margin-left: 2px; width: 22px; text-align: left; font-size: 9px;">
 1241                                    <span>min.</span>
 1242                                </div>
 1243                            </div>
 1244                        </div>
 1245                        <div class="cell-row text-muted" style="justify-content: flex-end;">
 1246                            <div class="form-group d-flex align-items-center justify-content-end mb-0">
 1247                                <div style="display: flex; align-items: center;">
 1248                                    <input asp-for="Orders[i].MaxOzonCommission"
 1249                                           class="form-control form-control-sm ozon-commission-field"
 1250                                           id="MaxOzonCommission_@Model.Orders[i].Id"
 1251                                           style="text-align: right; width: 48px;"/>
 1252                                    <span asp-validation-for="Orders[i].MaxOzonCommission" class="text-danger"
 1253                                          id="MaxOzonCommissionValidation_@Model.Orders[i].Id"></span>
 1254                                </div>
 1255                                <div style="margin-left: 2px; width: 22px; text-align: left; font-size: 9px;">
 1256                                    <span>max.</span>
 1257                                </div>
 1258                            </div>
 1259                        </div>
 1260                    </div>
 1261                </td>
 1262            }
 1263            else
 1264            {
 1265                <td class="tx-metric-col tx-metric-col--explainable" style="text-align: right; vertical-align: middle;">
 1266                    <button type="button" class="tx-calculation-help" data-calculation-focus="commission"
 1267                            aria-label="Показать расчёт комиссии" title="Показать расчёт">
 1268                        <i class="fas fa-dollar-sign" aria-hidden="true"></i>
 1269                    </button>
 1270                    <div class="two-row-cell">
 1271                        <div class="cell-row justify-content-center">
 1272                            <div id="MinOzonCommission_@Model.Orders[i].Id" title="@Model.Orders[i].MinCommissionInfo">
 01273                                @OrderCellFormat.Money(Model.Orders[i].MinOzonCommission)
 1274                            </div>
 1275                        </div>
 1276                        <div class="cell-row text-muted justify-content-center">
 1277                            <div id="MaxOzonCommission_@Model.Orders[i].Id" title="@Model.Orders[i].MaxCommissionInfo">
 01278                                @OrderCellFormat.Money(Model.Orders[i].MaxOzonCommission)
 1279                            </div>
 1280                        </div>
 1281                    </div>
 1282                </td>
 1283                <div class="form-group" style="display: none">
 1284                    <label asp-for="Orders[i].MinOzonCommission" class="control-label"></label><span
 1285                        style="margin-left: 10px; color: gray;">/ </span>
 1286                    <input asp-for="Orders[i].MinOzonCommission" class="form-control"/>
 1287                    <span asp-validation-for="Orders[i].MinOzonCommission" class="text-danger"></span>
 1288                </div>
 1289                <div class="form-group" style="display: none">
 1290                    <label asp-for="Orders[i].MaxOzonCommission" class="control-label"></label><span
 1291                        style="margin-left: 10px; color: gray;">/ </span>
 1292                    <input asp-for="Orders[i].MaxOzonCommission" class="form-control"/>
 1293                    <span asp-validation-for="Orders[i].MaxOzonCommission" class="text-danger"></span>
 1294                </div>
 1295            }
 1296        }
 1297
 01298        @if (Col("Profit"))
 1299        {
 1300            <td class="tx-metric-col tx-metric-col--explainable" style="text-align: right; vertical-align: middle;">
 1301                <button type="button" class="tx-calculation-help" data-calculation-focus="profit"
 1302                        aria-label="Показать расчёт прибыли" title="Показать расчёт">
 1303                    <i class="fas fa-dollar-sign" aria-hidden="true"></i>
 1304                </button>
 1305                <div class="two-row-cell">
 1306                    <div class="cell-row justify-content-center">
 1307                        <span id="MinProfit_@Model.Orders[i].Id">
 01308                            <span class="@OrderCellFormat.SignClass(Model.Orders[i].MinProfit)">@OrderCellFormat.Money(M
 1309                        </span>
 1310                        <span id="TargetProfit_@Model.Orders[i].Id"
 1311                              class="text-muted ms-1 tx-metric-suffix"></span>
 1312
 1313                    </div>
 1314                    <div class="cell-row text-muted justify-content-center">
 1315
 1316                        <span id="MaxProfit_@Model.Orders[i].Id">
 01317                            <span class="@OrderCellFormat.SignClass(Model.Orders[i].MaxProfit)">@OrderCellFormat.Money(M
 1318                        </span>
 1319                        <span id="TargetProfit2_@Model.Orders[i].Id"
 1320                              class="text-muted ms-1 tx-metric-suffix"></span>
 1321                    </div>
 1322                </div>
 1323            </td>
 1324        }
 1325
 01326        @if (Col("MarkupPercent"))
 1327        {
 1328            <td class="tx-metric-col tx-metric-col--explainable" style="text-align: right; vertical-align: middle;">
 1329                <button type="button" class="tx-calculation-help" data-calculation-focus="markup"
 1330                        aria-label="Показать расчёт наценки" title="Показать расчёт">
 1331                    <i class="fas fa-dollar-sign" aria-hidden="true"></i>
 1332                </button>
 1333                <div class="two-row-cell">
 1334                    <div class="cell-row justify-content-center" id="MinDiscount_@Model.Orders[i].Id">
 01335                        <span class="@OrderCellFormat.SignClass(Model.Orders[i].MinDiscount)">@OrderCellFormat.Money(Mod
 1336                    </div>
 1337                    <div class="cell-row text-muted justify-content-center"
 1338                         id="MaxDiscount_@Model.Orders[i].Id">
 01339                        <span class="@OrderCellFormat.SignClass(Model.Orders[i].MaxDiscount)">@OrderCellFormat.Money(Mod
 1340                    </div>
 1341                </div>
 1342            </td>
 1343
 1344            <input asp-for="Orders[i].MinOzonCommission" type="hidden"/>
 1345            <input asp-for="Orders[i].MaxOzonCommission" type="hidden"/>
 1346        }
 1347    </tr>
 01348}
 1349</tbody>
 1350
 1351@functions{
 1352
 1353    public string GetPriceTextColorClass(string? colorIndex)
 1354    {
 01355        return colorIndex?.ToUpper() switch
 01356        {
 01357            "SUPER" => "text-success fw-bold",
 01358            "GREEN" => "text-success",
 01359            "YELLOW" => "text-warning",
 01360            "RED" => "text-danger",
 01361            "INDEX" => "text-secondary",
 01362            "UNSPECIFIED" => "text-muted",
 01363            _ => ""
 01364        };
 1365    }
 1366
 1367    // Метод для получения цвета индекса на основе значения
 1368    public string GetIndexColor(decimal indexValue)
 1369    {
 01370        return indexValue switch
 01371        {
 01372            > 1.2m => "#28a745", // Зеленый - выгодно
 01373            > 0.8m => "#ffc107", // Желтый - умеренно
 01374            _ => "#dc3545" // Красный - невыгодно
 01375        };
 1376    }
 1377
 1378    // Метод для получения HEX цвета индекса
 1379    public string GetColorIndexHex(string? colorIndex)
 1380    {
 01381        return colorIndex?.ToUpper() switch
 01382        {
 01383            "SUPER" => "#198754", // Темно-зеленый
 01384            "GREEN" => "#28a745", // Зеленый
 01385            "YELLOW" => "#ffc107", // Желтый
 01386            "RED" => "#dc3545", // Красный
 01387            "INDEX" => "#6c757d", // Серый
 01388            "UNSPECIFIED" => "#6c757d", // Серый
 01389            _ => "#6c757d" // Серый по умолчанию
 01390        };
 1391    }
 1392
 1393    public string GetPriceTooltip(Order item)
 1394    {
 01395        var price = item.OrderPrice;
 01396        if (price == null)
 01397            return "Информация о проценке Ozon отсутствует";
 1398
 01399        var snapshot = item.OzonPromotionSnapshot;
 01400        var indexes = price.PriceIndexes;
 01401        var currency = price.CurrencyCode;
 01402        var actionPrice = snapshot?.MarketingSellerPrice ?? price.MarketingSellerPrice;
 01403        var netPrice = snapshot?.NetPrice ?? price.NetPrice;
 01404        var retailPrice = snapshot?.RetailPrice ?? price.RetailPrice;
 01405        var vat = snapshot?.Vat ?? price.Vat;
 01406        var acquiring = snapshot?.Acquiring;
 1407
 01408        static string Money(decimal value, string currencyCode) => $"{value:N2} {System.Net.WebUtility.HtmlEncode(curren
 1409        static string Row(string title, string apiField, string value) =>
 01410            $"<div class='price-tooltip-row'><span title='{System.Net.WebUtility.HtmlEncode(apiField)}'>{System.Net.WebU
 1411
 01412        var priceRows = string.Join(string.Empty,
 01413        [
 01414            Row("Текущая цена", "price", Money(price.Price, currency)),
 01415            Row("Маркетинговая цена", "marketing_price", Money(price.MarketingPrice, currency)),
 01416            Row("Цена с акциями", "marketing_seller_price", Money(actionPrice, currency)),
 01417            Row("Минимальная цена", "min_price", Money(price.MinPrice, currency)),
 01418            Row("Старая цена", "old_price", Money(price.OldPrice, currency)),
 01419            Row("Розничная цена", "retail_price", Money(retailPrice, currency)),
 01420            Row("Чистая цена", "net_price", Money(netPrice, currency))
 01421        ]);
 1422
 01423        var indexRows = indexes == null
 01424            ? "<div class='price-tooltip-empty'>Индексы Ozon не получены.</div>"
 01425            : string.Join(string.Empty,
 01426            [
 01427                Row("Ozon", "ozon_index_data", $"{Money(indexes.OzonMinPrice, indexes.OzonMinPriceCurrency)} · <em>{inde
 01428                Row("Внешние конкуренты", "external_index_data", $"{Money(indexes.ExternalMinPrice, indexes.ExternalMinP
 01429                Row("Собственные МП", "self_marketplaces_index_data", $"{Money(indexes.SelfMarketplacesMinPrice, indexes
 01430            ]);
 1431
 01432        var serviceRows = string.Join(string.Empty,
 01433        [
 01434            Row("НДС", "vat", $"{vat:N2}%"),
 01435            Row("Автоакции", "auto_action_enabled", price.AutoActionEnabled ? "Включены" : "Выключены"),
 01436            Row("Автодобавление", "auto_add_to_ozon_actions_list_enabled", price.AutoAddToOzonActionsListEnabled ? "Вклю
 01437            acquiring.HasValue ? Row("Эквайринг", "acquiring", Money(acquiring.Value, currency)) : string.Empty
 01438        ]);
 1439
 01440        return $@"
 01441            <div class='price-tooltip-box'>
 01442                <div class='price-tooltip-title'><i class='fas fa-ruble-sign'></i> Проценка Ozon</div>
 01443                <div class='price-tooltip-index'>Ценовой индекс: <b style='color: {GetColorIndexHex(indexes?.ColorIndex)
 01444                <div class='price-tooltip-section'>Цены из <code>/v5/product/info/prices</code></div>
 01445                <div class='price-tooltip-rows'>{priceRows}</div>
 01446                <div class='price-tooltip-section'>Индексы: минимальная цена · индекс</div>
 01447                <div class='price-tooltip-rows'>{indexRows}</div>
 01448                <div class='price-tooltip-section'>Параметры</div>
 01449                <div class='price-tooltip-rows'>{serviceRows}</div>
 01450            </div>";
 1451    }
 1452
 1453    public string GetPromotionTooltip(OrderOzonPromotionSnapshot? snapshot)
 1454    {
 01455        if (snapshot == null)
 01456            return "Информация об акции Ozon отсутствует";
 1457
 01458        static string Value(decimal value) => value.ToString("N2");
 01459        static string Text(string? value) => System.Net.WebUtility.HtmlEncode(value ?? "—");
 01460        static string Date(DateTime? value) => value?.ToLocalTime().ToString("dd.MM.yyyy HH:mm") ?? "—";
 1461
 01462        var conditionLines = new List<string>();
 01463        foreach (var condition in snapshot.Conditions)
 1464        {
 01465            var valueLine = condition.Value.HasValue ? string.Format("<div>Значение условия: <b>{0}</b></div>", Value(co
 01466            var actionPriceLine = condition.ActionPrice.HasValue ? string.Format("<div>Акционная цена: <b>{0} {1}</b></d
 01467            conditionLines.Add(string.Format("<div class='promotion-tooltip-condition'><div class='promotion-tooltip-con
 1468        }
 1469
 01470        var conditions = conditionLines.Count == 0 ? "<div class='promotion-tooltip-empty'>Ozon подтвердил участие товар
 01471        return $@"
 01472            <div class='promotion-tooltip-box'>
 01473                <div class='promotion-tooltip-title'><i class='fas fa-tags'></i> Акции Ozon</div>
 01474                <div class='promotion-tooltip-prices'>
 01475                    <div><span>Цена</span><b>{Value(snapshot.Price)} {Text(snapshot.CurrencyCode)}</b></div>
 01476                    <div><span>Цена продавца</span><b>{Value(snapshot.MarketingSellerPrice)} {Text(snapshot.CurrencyCode
 01477                    <div><span>Мин. цена</span><b>{Value(snapshot.MinPrice)} {Text(snapshot.CurrencyCode)}</b></div>
 01478                    <div><span>Старая цена</span><b>{Value(snapshot.OldPrice)} {Text(snapshot.CurrencyCode)}</b></div>
 01479                </div>
 01480                <div class='promotion-tooltip-stats'>
 01481                    <span>Комиссия FBS: <b>{Value(snapshot.FbsCommissionPercent)}%</b></span>
 01482                    <span>Эквайринг: <b>{Value(snapshot.Acquiring)} {Text(snapshot.CurrencyCode)}</b></span>
 01483                    <span>Индекс Ozon: <b>{snapshot.OzonPriceIndex:N2}</b></span>
 01484                </div>
 01485                <div class='promotion-tooltip-conditions'>{conditions}</div>
 01486            </div>";
 1487    }
 1488
 1489    public string GetColorIndexDescription(string? colorIndex)
 1490    {
 01491        return colorIndex?.ToUpper() switch
 01492        {
 01493            "SUPER" => "Супервыгодный",
 01494            "GREEN" => "Выгодный",
 01495            "YELLOW" => "Умеренный",
 01496            "RED" => "Невыгодный",
 01497            "INDEX" => "Отсутствует",
 01498            "UNSPECIFIED" => "Не определён",
 01499            _ => "Не определён"
 01500        };
 1501    }
 1502}