| | | 1 | | @using OzonDomains |
| | | 2 | | @using OzonDomains.Models |
| | | 3 | | @using OzonOrdersWeb.ViewModels.OrderViewModels |
| | | 4 | | @using OzonOrdersWeb.Areas.Studio2.ViewHelpers |
| | | 5 | | |
| | | 6 | | @model OrderPageViewModel<Order, OServices.FiltersServices.FilterModels.OrderFilterModel> |
| | | 7 | | |
| | | 8 | | @{ |
| | 0 | 9 | | Func<string?, string> truncateStatus = text => |
| | 0 | 10 | | { |
| | 0 | 11 | | if (string.IsNullOrEmpty(text)) return text ?? ""; |
| | 0 | 12 | | return text.Length > 20 ? text.Substring(0, 20) + "..." : text; |
| | 0 | 13 | | }; |
| | | 14 | | |
| | 0 | 15 | | var readMap = Model.OrderListFieldReadAccess; |
| | 0 | 16 | | var u = Model.User; |
| | 0 | 17 | | bool L(string field, string leg) => OrderListColumnGate.CanShowOrderColumn(u, readMap, field, leg); |
| | 0 | 18 | | var indexAction = ViewData["IndexAction"] as string ?? "Index"; |
| | 0 | 19 | | var isReturnsPage = string.Equals(indexAction, "Returns", StringComparison.OrdinalIgnoreCase); |
| | 0 | 20 | | var labelExistsMap = ViewData["LabelExistsByShipmentNumber"] as Dictionary<string, bool> |
| | 0 | 21 | | ?? new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); |
| | | 22 | | } |
| | | 23 | | <tbody id="shipmentTableBody"> |
| | | 24 | | @{ |
| | | 25 | | // Переменные для группировки по отправлениям |
| | 0 | 26 | | long? previousShipmentId = null; |
| | 0 | 27 | | bool groupByShipment = ViewBag.GroupByShipment ?? Model.FilterModel.GroupByShipment; |
| | 0 | 28 | | var shipmentGroups = new Dictionary<long, int>(); // ShipmentId -> количество заказов в группе |
| | | 29 | | |
| | | 30 | | // Подсчитываем количество заказов в каждой группе |
| | 0 | 31 | | if (groupByShipment) |
| | | 32 | | { |
| | 0 | 33 | | foreach (var order in Model.Items.Where(o => o.ShipmentId.HasValue)) |
| | | 34 | | { |
| | 0 | 35 | | var sid = order.ShipmentId!.Value; |
| | 0 | 36 | | if (!shipmentGroups.ContainsKey(sid)) |
| | 0 | 37 | | shipmentGroups[sid] = 0; |
| | 0 | 38 | | shipmentGroups[sid]++; |
| | | 39 | | } |
| | | 40 | | } |
| | | 41 | | } |
| | 0 | 42 | | @foreach (var item in Model.Items) |
| | | 43 | | { |
| | | 44 | | // Подсветка изменённых столбцов: возвращает " bg-warning" (для конкатенации в class=) |
| | | 45 | | // или "" — вместо разворачивания тернарника в разметке каждой ячейки. |
| | | 46 | | string upd(params string[] cols) |
| | 0 | 47 | | => item.UpdatedColumns != null && cols.Any(item.UpdatedColumns.Contains) ? " bg-warning" : ""; |
| | | 48 | | |
| | | 49 | | // Определяем позицию заказа в группе отправления |
| | 0 | 50 | | bool isFirstInGroup = false; |
| | 0 | 51 | | bool isInGroup = false; |
| | 0 | 52 | | int groupSize = 0; |
| | | 53 | | |
| | 0 | 54 | | if (groupByShipment && item.ShipmentId.HasValue) |
| | | 55 | | { |
| | 0 | 56 | | groupSize = shipmentGroups.GetValueOrDefault(item.ShipmentId.Value, 1); |
| | 0 | 57 | | isInGroup = groupSize > 1; |
| | 0 | 58 | | isFirstInGroup = isInGroup && item.ShipmentId != previousShipmentId; |
| | 0 | 59 | | previousShipmentId = item.ShipmentId; |
| | | 60 | | } |
| | | 61 | | else |
| | | 62 | | { |
| | 0 | 63 | | previousShipmentId = null; |
| | | 64 | | } |
| | | 65 | | |
| | 0 | 66 | | string rowClass = "custom-white"; |
| | 0 | 67 | | if ((item.AppStatus?.Name == "Отменен" && (item.ClientStatus?.Name == "Отменён" || item.ClientStatus == null |
| | 0 | 68 | | item.ClientStatus?.Name is "Отменен" or "Отменён") |
| | | 69 | | { |
| | 0 | 70 | | rowClass = "table-danger"; |
| | | 71 | | } |
| | 0 | 72 | | else if (item.IsReturnable == true) |
| | | 73 | | { |
| | 0 | 74 | | rowClass = "custom-red"; |
| | | 75 | | } |
| | | 76 | | else |
| | | 77 | | { |
| | 0 | 78 | | rowClass = item.AppStatus?.Name switch |
| | 0 | 79 | | { |
| | 0 | 80 | | "Заказан поставщику" => "table-success", |
| | 0 | 81 | | "Отгружен поставщиком" => "table-info", |
| | 0 | 82 | | "Отгружен реализатору" => "table-warning", |
| | 0 | 83 | | "Заказан реализатору" => "table-secondary", |
| | 0 | 84 | | _ => rowClass |
| | 0 | 85 | | }; |
| | | 86 | | } |
| | | 87 | | |
| | 0 | 88 | | @if (item.OrderArchive != null) |
| | | 89 | | { |
| | 0 | 90 | | rowClass = rowClass + " table-archived"; |
| | | 91 | | } |
| | | 92 | | |
| | | 93 | | // Добавляем классы группировки |
| | 0 | 94 | | if (isFirstInGroup) |
| | | 95 | | { |
| | 0 | 96 | | rowClass = rowClass + " shipment-group-first"; |
| | | 97 | | } |
| | 0 | 98 | | else if (isInGroup && !isFirstInGroup) |
| | | 99 | | { |
| | 0 | 100 | | rowClass = rowClass + " shipment-group-continuation"; |
| | | 101 | | } |
| | | 102 | | |
| | | 103 | | <tr class="@rowClass order-row" |
| | 0 | 104 | | data-order-id="@item.Id" |
| | 0 | 105 | | data-shipment-id="@item.ShipmentId" |
| | 0 | 106 | | data-is-archived="@(item.OrderArchive != null ? "true" : "false")" |
| | 0 | 107 | | data-cart-item-id="@item.CartItemId" |
| | 0 | 108 | | data-app-status-name="@item.AppStatus?.Name" |
| | 0 | 109 | | data-market-status="@item.Status" |
| | | 110 | | oncontextmenu="showInfoAtCursor('@item.Id', '@item.CartItemId', @(item.OrderArchive != null ? "true" : " |
| | | 111 | | style="cursor: pointer;"> |
| | 0 | 112 | | @if ((L("ShipmentNumber", "Номер заказа") || L("Delivery", "Номер заказа") || L("DeliveryType", "Номер з |
| | | 113 | | { |
| | | 114 | | <td data-column="shipmentNumber" class="@(item.UpdatedColumns?.Contains("ShipmentNumber") == true ? |
| | | 115 | | <div class="orders-shipment-content d-flex flex-column" style="min-width: 0;"> |
| | 0 | 116 | | @if (L("ShipmentNumber", "Номер заказа")) |
| | | 117 | | { |
| | | 118 | | <!-- Номер заказа --> |
| | | 119 | | <div class="ml-2" onclick="copyArticle(this)" role="button"> |
| | 0 | 120 | | @if (isInGroup && !isFirstInGroup) |
| | | 121 | | { |
| | | 122 | | <span class="shipment-group-indicator" |
| | | 123 | | title="Часть отправления @item.ShipmentId"> |
| | | 124 | | <i class="fas fa-level-up-alt fa-rotate-90 fa-lg"></i> |
| | | 125 | | </span> |
| | | 126 | | } |
| | | 127 | | else |
| | | 128 | | { |
| | 0 | 129 | | @Html.DisplayFor(modelItem => item.ShipmentNumber) |
| | | 130 | | } |
| | 0 | 131 | | @if (item.ShipmentNumber != null && item.ShipmentNumber != "" && !(isInGroup && |
| | | 132 | | { |
| | 0 | 133 | | var shipmentNumberParts = item.ShipmentNumber.Split('-'); |
| | 0 | 134 | | if (shipmentNumberParts.Length > 0) |
| | | 135 | | { |
| | 0 | 136 | | var key = shipmentNumberParts[0]; |
| | 0 | 137 | | @if (!isFirstInGroup || groupSize <= 1) |
| | | 138 | | { |
| | 0 | 139 | | if (@Model.UniqueNumbers.TryGetValue(key, out int count) && count != |
| | | 140 | | { |
| | | 141 | | <a href="javascript:void(0);" |
| | 0 | 142 | | onclick="setShipmentNumberFilter('@key')">@count</a> |
| | | 143 | | } |
| | | 144 | | } |
| | | 145 | | } |
| | | 146 | | } |
| | | 147 | | </div> |
| | | 148 | | } |
| | 0 | 149 | | @if (L("Delivery", "Номер заказа")) |
| | | 150 | | { |
| | | 151 | | <div class="d-flex" style="gap: 2px;"> |
| | 0 | 152 | | @if (item.ExcelFileData != null && item.OzonClient != null) |
| | | 153 | | { |
| | | 154 | | <a href="@Url.Action("ViewFile", new { filePath = item.OzonClient.Name, file |
| | | 155 | | target="_blank" class="orders-row-ic-xlsx" |
| | | 156 | | onclick="return validateLink('@item.ExcelFileData.FolderName', '@item.Exc |
| | | 157 | | <i class="bi bi-filetype-xlsx" aria-hidden="true"></i> |
| | | 158 | | </a> |
| | | 159 | | } |
| | | 160 | | <div |
| | | 161 | | class="text-muted d-flex@(upd("Delivery"))" |
| | | 162 | | style="white-space: nowrap;" title="@item.DeliveryCity"> |
| | 0 | 163 | | @if (!(isInGroup && !isFirstInGroup)) |
| | | 164 | | { |
| | 0 | 165 | | string? delivery = item.Delivery?.Provider?.Name; |
| | | 166 | | |
| | 0 | 167 | | if (!string.IsNullOrEmpty(delivery) && delivery.Length > 20) |
| | | 168 | | { |
| | 0 | 169 | | delivery = delivery.Substring(0, 20) + "..."; |
| | | 170 | | } |
| | | 171 | | |
| | 0 | 172 | | <span title="@item.Delivery?.Name">@delivery</span> |
| | | 173 | | } |
| | | 174 | | @{ |
| | 0 | 175 | | var transactionDocument = item.OrderExtensions? |
| | 0 | 176 | | .FirstOrDefault(e => e.Key == "Документ транзакции")?.Value; |
| | | 177 | | } |
| | 0 | 178 | | @if (!string.IsNullOrEmpty(transactionDocument)) |
| | | 179 | | { |
| | | 180 | | <div class="d-flex flex-column align-items-start ms-2"> |
| | | 181 | | <a href="@Url.Action("DownloadFile", "TransactionFiles", new { area |
| | | 182 | | class="transaction-document-link" |
| | 0 | 183 | | data-order-id="@item.Id" |
| | | 184 | | data-tooltip="Скачать документ транзакции" |
| | | 185 | | tooltip="" |
| | | 186 | | data-placement="top" |
| | | 187 | | title="Скачать документ транзакции" |
| | | 188 | | style="color: #0d6efd; text-decoration: none; margin-bottom: 4px; |
| | | 189 | | <i class="bi bi-file-earmark-arrow-down" aria-hidden="true"></i> |
| | | 190 | | </a> |
| | | 191 | | </div> |
| | | 192 | | } |
| | | 193 | | </div> |
| | | 194 | | </div> |
| | | 195 | | } |
| | 0 | 196 | | @if (L("DeliveryType", "Номер заказа") && !(isInGroup && !isFirstInGroup) && !string.IsNullO |
| | | 197 | | { |
| | | 198 | | <div class="orders-shipment-delivery-detail text-muted" style="white-space: nowrap; font |
| | | 199 | | @{ |
| | 0 | 200 | | var deliveryFullName = item.Delivery.Name; |
| | 0 | 201 | | if (deliveryFullName.Length > 30) |
| | 0 | 202 | | deliveryFullName = deliveryFullName.Substring(0, 30) + "..."; |
| | | 203 | | } |
| | 0 | 204 | | @deliveryFullName |
| | | 205 | | </div> |
| | | 206 | | } |
| | | 207 | | </div> |
| | | 208 | | </td> |
| | 0 | 209 | | @if (L("ShipmentNumber", "Номер заказа")) |
| | | 210 | | { |
| | | 211 | | <td data-column="shipmentActions" class="orders-shipment-actions-col"> |
| | | 212 | | <div class="d-flex flex-column align-items-center orders-shipment-actions"> |
| | 0 | 213 | | @if (!(isInGroup && !isFirstInGroup)) |
| | | 214 | | { |
| | 0 | 215 | | @if (item.OzonClient?.ClientType == OzonDomains.ClientType.OZON) |
| | | 216 | | { |
| | 0 | 217 | | var labelExists = false; |
| | 0 | 218 | | var labelKnown = !string.IsNullOrEmpty(item.ShipmentNumber) |
| | 0 | 219 | | && labelExistsMap.TryGetValue(item.ShipmentNumber!, out labelEx |
| | 0 | 220 | | var hasLabel = labelKnown && labelExists; |
| | | 221 | | <a href="javascript:void(0);" |
| | | 222 | | id="labelIcon_@item.Id" |
| | 0 | 223 | | data-shipment-number="@item.ShipmentNumber" |
| | 0 | 224 | | data-label-exists="@(hasLabel ? "true" : "false")" |
| | 0 | 225 | | data-label-checked="@(labelKnown ? "true" : "false")" |
| | | 226 | | onclick="printSingleOrderLabel(@item.Id)" |
| | | 227 | | title="@(hasLabel ? "Этикетка уже есть в системе. Нажмите для открытия" : "Пе |
| | | 228 | | style="@(hasLabel ? "color: #28a745; text-decoration: none; position: relativ |
| | | 229 | | class="label-icon-link"> |
| | | 230 | | <i class="bi bi-printer label-icon" aria-hidden="true" |
| | | 231 | | style="@(hasLabel ? "color:#28a745;" : null)"></i> |
| | | 232 | | <i class="bi bi-check-circle-fill label-exists-indicator" aria-hidden="true" |
| | | 233 | | style="display:@(hasLabel ? "block" : "none");"></i> |
| | | 234 | | </a> |
| | | 235 | | } |
| | 0 | 236 | | else if (item.OzonClient?.ClientType == OzonDomains.ClientType.YANDEX) |
| | | 237 | | { |
| | 0 | 238 | | var yandexLabelExists = false; |
| | 0 | 239 | | var yandexLabelKnown = !string.IsNullOrEmpty(item.ShipmentNumber) |
| | 0 | 240 | | && labelExistsMap.TryGetValue(item.ShipmentNumber!, out y |
| | 0 | 241 | | var yandexHasLabel = yandexLabelKnown && yandexLabelExists; |
| | | 242 | | <a href="javascript:void(0);" |
| | | 243 | | id="labelIconYandex_@item.Id" |
| | 0 | 244 | | data-shipment-number="@item.ShipmentNumber" |
| | | 245 | | data-order-source="yandex" |
| | 0 | 246 | | data-label-exists="@(yandexHasLabel ? "true" : "false")" |
| | 0 | 247 | | data-label-checked="@(yandexLabelKnown ? "true" : "false")" |
| | | 248 | | onclick="printSingleOrderLabelYandex(@item.Id)" |
| | | 249 | | title="@(yandexHasLabel ? "Этикетка уже есть в системе. Нажмите для открытия" |
| | | 250 | | style="@(yandexHasLabel ? "color: #28a745; text-decoration: none; position: r |
| | | 251 | | class="label-icon-link"> |
| | | 252 | | <i class="bi bi-printer label-icon" aria-hidden="true" |
| | | 253 | | style="@(yandexHasLabel ? "color:#28a745;" : null)"></i> |
| | | 254 | | <i class="bi bi-check-circle-fill label-exists-indicator" aria-hidden="true" |
| | | 255 | | style="display:@(yandexHasLabel ? "block" : "none");"></i> |
| | | 256 | | </a> |
| | | 257 | | } |
| | | 258 | | |
| | | 259 | | <a href="javascript:void(0);" |
| | | 260 | | id="articleLabelIcon_@item.Id" |
| | 0 | 261 | | data-order-id="@item.Id" |
| | | 262 | | onclick="generateArticleLabel(@item.Id, '@(item.EtProducer?.Name ?? "")', '@(item |
| | | 263 | | title="Этикетка по артикулу" |
| | | 264 | | style="color: #6c757d; text-decoration: none;" |
| | | 265 | | class="article-label-icon-link"> |
| | | 266 | | <i class="bi bi-upc-scan" aria-hidden="true"></i> |
| | | 267 | | </a> |
| | | 268 | | } |
| | | 269 | | else |
| | | 270 | | { |
| | | 271 | | <!-- Для продолжения группы показываем badge с количеством заказов --> |
| | 0 | 272 | | @if (groupSize > 1) |
| | | 273 | | { |
| | | 274 | | <span class="badge bg-light text-dark ms-2" |
| | | 275 | | title="Заказов в отправлении: @groupSize"> |
| | 0 | 276 | | <i class="fas fa-boxes me-1"></i>@groupSize |
| | | 277 | | </span> |
| | | 278 | | } |
| | | 279 | | } |
| | | 280 | | </div> |
| | | 281 | | </td> |
| | | 282 | | } |
| | | 283 | | } |
| | | 284 | | <td data-column="client"> |
| | | 285 | | <div class="two-row-cell"> |
| | 0 | 286 | | @if (L("OzonClient", "Клиент")) |
| | | 287 | | { |
| | | 288 | | <div |
| | | 289 | | class="cell-row orders-client-main-row@(upd("OzonClient"))" |
| | 0 | 290 | | data-app-tooltip-html="@GetClientTooltip(item.OzonClient)" |
| | | 291 | | data-app-tooltip-variant="client" |
| | | 292 | | style="cursor: help;"> |
| | 0 | 293 | | @if (item.OzonClient != null) |
| | | 294 | | { |
| | 0 | 295 | | string ozonClient = item.OzonClient.Name; |
| | 0 | 296 | | if (ozonClient.Length > 18) |
| | | 297 | | { |
| | 0 | 298 | | ozonClient = ozonClient.Substring(0, 18) + "..."; |
| | | 299 | | } |
| | | 300 | | |
| | 0 | 301 | | <span class="orders-client-name" title="@item.OzonClient.Name">@ozonClient</span> |
| | | 302 | | } |
| | | 303 | | </div> |
| | | 304 | | } |
| | 0 | 305 | | @if (L("DeliveryCity", "Город доставки")) |
| | | 306 | | { |
| | 0 | 307 | | @if (item.DeliveryCity != null && item.DeliveryCity != "") |
| | | 308 | | { |
| | | 309 | | <div |
| | | 310 | | class="cell-row@(upd("DeliveryCity")) text-muted d-flex align-items-center" |
| | | 311 | | style="white-space: nowrap;" title="@item.DeliveryCity"> |
| | | 312 | | @{ |
| | 0 | 313 | | string deliveryCity = item.DeliveryCity; |
| | 0 | 314 | | if (deliveryCity.Length > 11) |
| | | 315 | | { |
| | 0 | 316 | | deliveryCity = deliveryCity.Substring(0, 11) + "..."; |
| | | 317 | | } |
| | | 318 | | |
| | 0 | 319 | | @deliveryCity |
| | | 320 | | } |
| | 0 | 321 | | @if (Model.UniqueDeliveryCitys.ContainsKey(item.DeliveryCity) && Model.UniqueDeliver |
| | | 322 | | { |
| | | 323 | | <a href="javascript:void(0);" |
| | | 324 | | onclick="setDeliveryCityFilter('@item.DeliveryCity')" |
| | 0 | 325 | | style="margin-left: 2px;">(@Model.UniqueDeliveryCitys[item.DeliveryCity])</a> |
| | | 326 | | } |
| | | 327 | | </div> |
| | | 328 | | } |
| | | 329 | | } |
| | 0 | 330 | | @if (isReturnsPage && !string.IsNullOrEmpty(item.ReturnLogisticBarcode)) |
| | | 331 | | { |
| | | 332 | | <div class="cell-row text-muted" style="white-space: nowrap;" title="@item.ReturnLogisticBar |
| | | 333 | | <small style="cursor: pointer;" title="Нажмите, чтобы скопировать" onclick="copyArticle( |
| | 0 | 334 | | @item.ReturnLogisticBarcode |
| | | 335 | | <i class="bi bi-upc text-secondary barcode-icon-link" aria-hidden="true" |
| | 0 | 336 | | data-barcode="@item.ReturnLogisticBarcode"></i> |
| | | 337 | | </small> |
| | | 338 | | </div> |
| | | 339 | | } |
| | 0 | 340 | | else if (L("TrackingNumber", "Номер заказа") && !string.IsNullOrEmpty(item.TrackingNumber)) |
| | | 341 | | { |
| | | 342 | | <div class="cell-row text-muted" style="white-space: nowrap; cursor: pointer;" title="@item. |
| | 0 | 343 | | <small>@item.TrackingNumber</small> |
| | | 344 | | </div> |
| | | 345 | | } |
| | | 346 | | </div> |
| | | 347 | | </td> |
| | | 348 | | |
| | | 349 | | <td data-column="dates"> |
| | | 350 | | <div class="two-row-cell"> |
| | 0 | 351 | | @if (L("ProcessingDate", "Принят в обработку")) |
| | | 352 | | { |
| | | 353 | | <div |
| | | 354 | | class="cell-row@(upd("ProcessingDate"))" |
| | | 355 | | id="td2ProcessingDate" |
| | | 356 | | title="@Html.DisplayFor(modelItem => item.FullFormattedProcessingDate)"> |
| | 0 | 357 | | @OrderDateDisplay.FormatTableDateFull(item.ProcessingDate) |
| | | 358 | | </div> |
| | | 359 | | } |
| | | 360 | | |
| | 0 | 361 | | @if (L("ShippingDate", "Дата отгрузки")) |
| | | 362 | | { |
| | | 363 | | <div |
| | | 364 | | class="cell-row@(upd("ShippingDate")) text-muted" |
| | | 365 | | title="@Html.DisplayFor(modelItem => item.FullFormattedShippingDate)"> |
| | 0 | 366 | | @OrderDateDisplay.FormatTableDateFull(item.ShippingDate) |
| | | 367 | | </div> |
| | | 368 | | } |
| | 0 | 369 | | @if (isReturnsPage) |
| | | 370 | | { |
| | | 371 | | <div class="cell-row text-muted" |
| | | 372 | | title="@item.FullFormattedReturnStatusChangeDate"> |
| | 0 | 373 | | @item.FormattedReturnStatusChangeDate |
| | | 374 | | </div> |
| | | 375 | | } |
| | | 376 | | @* Прогнозная дата доставки от поставщика — всегда серая (это оценка, не факт). |
| | | 377 | | Гейт тот же, что у плашки статуса поставщика. *@ |
| | 0 | 378 | | @if (L("AppStatus", "Статус") && item.SupplierExpectedDeliveryDate.HasValue) |
| | | 379 | | { |
| | | 380 | | <div class="cell-row text-muted" |
| | | 381 | | title="@item.FullFormattedSupplierExpectedDeliveryDate"> |
| | 0 | 382 | | @item.FormattedSupplierExpectedDeliveryDate |
| | | 383 | | </div> |
| | | 384 | | } |
| | | 385 | | </div> |
| | | 386 | | </td> |
| | | 387 | | |
| | | 388 | | <td data-column="period"> |
| | | 389 | | <div class="two-row-cell"> |
| | 0 | 390 | | @if (L("DeliveryPeriod", "Срок доставки")) |
| | | 391 | | { |
| | | 392 | | <div class="cell-row orders-period-row"> |
| | | 393 | | <div |
| | 0 | 394 | | title="@Html.DisplayFor(modelItem => item.FormattedDeliveryDay) @Html.DisplayFor(mod |
| | | 395 | | </div> |
| | | 396 | | } |
| | 0 | 397 | | @if (L("DeliveryPeriod", "Срок доставки")) |
| | | 398 | | { |
| | | 399 | | <div class="cell-row text-muted orders-period-row"> |
| | 0 | 400 | | <div>@Html.DisplayFor(modelItem => item.FormattedTimeLeftDay)</div> |
| | | 401 | | </div> |
| | | 402 | | } |
| | 0 | 403 | | @if (L("AppStatus", "Статус") && item.SupplierExpectedDeliveryDate.HasValue) |
| | | 404 | | { |
| | | 405 | | <div class="cell-row text-muted orders-period-row"> |
| | 0 | 406 | | <div title="@item.FullFormattedSupplierExpectedDeliveryDate">@item.FormattedSupplierExpe |
| | | 407 | | </div> |
| | | 408 | | } |
| | | 409 | | </div> |
| | | 410 | | </td> |
| | | 411 | | |
| | | 412 | | <td class="orders-product-col" data-column="productInfo"> |
| | | 413 | | <div class="d-flex orders-product-cell"> |
| | 0 | 414 | | @if (!string.IsNullOrEmpty(item.ProductInfo?.ImageUrl)) |
| | | 415 | | { |
| | | 416 | | <img src="@item.ProductInfo.ImageUrl" |
| | | 417 | | alt="@(item.ProductName ?? item.Article)" |
| | | 418 | | class="product-thumb flex-shrink-0" |
| | | 419 | | onerror="this.style.display='none'" |
| | | 420 | | loading="lazy" /> |
| | | 421 | | } |
| | | 422 | | <div class="orders-product-text-grid" style="min-width: 0; flex: 1; overflow: hidden; display: g |
| | | 423 | | <div class="orders-product-top-row"> |
| | 0 | 424 | | @if (L("ProductKey", "Key")) |
| | | 425 | | { |
| | | 426 | | <div id="thArticle" title="@item.ProductKey" |
| | | 427 | | class="product-key-cell d-flex align-items-center min-w-0@(upd("ProductKey"))"> |
| | | 428 | | @{ |
| | 0 | 429 | | string? productKey = item.ProductKey; |
| | 0 | 430 | | string? displayText = productKey; |
| | | 431 | | |
| | 0 | 432 | | if (!string.IsNullOrEmpty(productKey)) |
| | | 433 | | { |
| | 0 | 434 | | var match = System.Text.RegularExpressions.Regex.Match(productKey, @"\[( |
| | | 435 | | |
| | 0 | 436 | | if (match.Success) |
| | | 437 | | { |
| | 0 | 438 | | string mainPart = productKey.Substring(0, match.Index); |
| | 0 | 439 | | string numberPart = match.Value; |
| | | 440 | | |
| | 0 | 441 | | if (mainPart.Length > 17) |
| | | 442 | | { |
| | 0 | 443 | | mainPart = mainPart.Substring(0, 17) + "..."; |
| | | 444 | | } |
| | | 445 | | |
| | 0 | 446 | | displayText = $"{mainPart}<span class='text-danger fw-bold product-k |
| | | 447 | | } |
| | | 448 | | else |
| | | 449 | | { |
| | 0 | 450 | | if (productKey.Length > 17) |
| | | 451 | | { |
| | 0 | 452 | | displayText = productKey.Substring(0, 17) + "..."; |
| | | 453 | | } |
| | | 454 | | } |
| | | 455 | | } |
| | | 456 | | } |
| | | 457 | | |
| | | 458 | | <span class="custom-product-link"> |
| | 0 | 459 | | @Html.Raw(displayText) |
| | | 460 | | </span> |
| | | 461 | | |
| | 0 | 462 | | @if (!string.IsNullOrEmpty(item.ProductInfo?.Barcode)) |
| | | 463 | | { |
| | | 464 | | <i class="bi bi-upc text-secondary barcode-icon-link" aria-hidden="true" |
| | 0 | 465 | | data-barcode="@item.ProductInfo.Barcode"></i> |
| | | 466 | | } |
| | | 467 | | |
| | 0 | 468 | | @if (item.ProductKey != null && item.ProductKey != "" && Model.UniqueArticles.Co |
| | | 469 | | { |
| | | 470 | | <a href="javascript:void(0);" |
| | | 471 | | onclick="setArticleFilter('@System.Net.WebUtility.HtmlEncode(item.Product |
| | | 472 | | style="margin-left:5px;flex-shrink:0;font-size:0.75rem;" |
| | | 473 | | title="Фильтровать по этому артикулу"> |
| | 0 | 474 | | (@Model.UniqueArticles[item.ProductKey]) |
| | | 475 | | </a> |
| | | 476 | | } |
| | | 477 | | </div> |
| | | 478 | | } |
| | 0 | 479 | | @if (L("Manufacturer", "Производитель")) |
| | | 480 | | { |
| | | 481 | | <div id="thManufacturer" class="product-manufacturer-cell d-flex flex-column nowrap |
| | 0 | 482 | | @if (item.EtProducer != null && item.EtProducer.Name == null) |
| | | 483 | | { |
| | 0 | 484 | | <form asp-area="Studio2" asp-action="@indexAction" asp-controller="Producers |
| | | 485 | | <input type="hidden" name="MarketPrefix" value="@item.EtProducer.MarketP |
| | | 486 | | <button type="submit" class="btn btn-link">Указать</button> |
| | | 487 | | </form> |
| | | 488 | | } |
| | | 489 | | else |
| | | 490 | | { |
| | 0 | 491 | | @(item.EtProducer?.Name ?? "") |
| | | 492 | | } |
| | | 493 | | </div> |
| | | 494 | | } |
| | 0 | 495 | | @if (L("Article", "Артикул")) |
| | | 496 | | { |
| | | 497 | | <div id="thArticle2" |
| | | 498 | | class="d-flex flex-column nowrap text-end align-items-end article-and-replaceme |
| | | 499 | | <div class="d-flex align-items-center" style="gap: 2px;"> |
| | | 500 | | @{ |
| | 0 | 501 | | var replacementArticle = item.ProductInfo?.ReplacementProduct?.Article; |
| | 0 | 502 | | var hasReplacement = !string.IsNullOrEmpty(replacementArticle); |
| | | 503 | | } |
| | 0 | 504 | | @if (item.ProductInfoId.HasValue) |
| | | 505 | | { |
| | | 506 | | <span class="replacement-link-icon @(hasReplacement ? "active" : "")" |
| | 0 | 507 | | data-product-info-id="@item.ProductInfoId" |
| | 0 | 508 | | data-original-article="@item.Article" |
| | 0 | 509 | | @(hasReplacement ? Html.Raw("data-replacement-article=\"" + System |
| | | 510 | | style="font-size: 0.875rem; line-height: 1; min-width: 16px; curso |
| | | 511 | | title="@(hasReplacement ? "Связано с: " + replacementArticle + ". |
| | | 512 | | <i class="bi bi-link-45deg" aria-hidden="true"></i> |
| | | 513 | | </span> |
| | | 514 | | } |
| | | 515 | | <div role="button" |
| | | 516 | | title="Нажмите, чтобы скопировать" |
| | | 517 | | onclick="copyArticle(this)"> |
| | 0 | 518 | | @if (item.Article != null) |
| | | 519 | | { |
| | 0 | 520 | | @Html.DisplayFor(modelItem => item.Article) |
| | | 521 | | } |
| | | 522 | | </div> |
| | | 523 | | </div> |
| | | 524 | | <div class="replacement-article-display" |
| | | 525 | | style="display: @(hasReplacement ? "block" : "none");" |
| | | 526 | | aria-hidden="@(hasReplacement ? "false" : "true")"> |
| | | 527 | | <span class="replacement-arrow">↳</span> |
| | 0 | 528 | | <span class="replacement-article-text">@(replacementArticle ?? "")</span> |
| | | 529 | | </div> |
| | | 530 | | </div> |
| | | 531 | | } |
| | | 532 | | </div> |
| | | 533 | | <script> |
| | | 534 | | document.addEventListener('DOMContentLoaded', function () { |
| | | 535 | | var articleFilter = document.getElementById('articleFilter'); |
| | | 536 | | var articleFilter2 = document.getElementById('articleFilter2'); |
| | | 537 | | |
| | | 538 | | if (articleFilter && articleFilter2) { |
| | | 539 | | articleFilter.addEventListener('input', function () { |
| | | 540 | | if (articleFilter2.value !== articleFilter.value) articleFilter2.value = art |
| | | 541 | | }); |
| | | 542 | | articleFilter.addEventListener('change', function () { |
| | | 543 | | if (articleFilter2.value !== articleFilter.value) articleFilter2.value = art |
| | | 544 | | }); |
| | | 545 | | articleFilter2.addEventListener('input', function () { |
| | | 546 | | if (articleFilter.value !== articleFilter2.value) { |
| | | 547 | | articleFilter.value = articleFilter2.value; |
| | | 548 | | articleFilter.dispatchEvent(new Event('input', {bubbles: true})); |
| | | 549 | | } |
| | | 550 | | }); |
| | | 551 | | articleFilter2.addEventListener('change', function () { |
| | | 552 | | if (articleFilter.value !== articleFilter2.value) { |
| | | 553 | | articleFilter.value = articleFilter2.value; |
| | | 554 | | articleFilter.dispatchEvent(new Event('change', {bubbles: true})); |
| | | 555 | | } |
| | | 556 | | }); |
| | | 557 | | } |
| | | 558 | | }); |
| | | 559 | | </script> |
| | | 560 | | |
| | 0 | 561 | | @if (L("ProductName", "Наименование товара")) |
| | | 562 | | { |
| | 0 | 563 | | var productName = item.ProductName; |
| | 0 | 564 | | var truncatedName = string.IsNullOrEmpty(productName) |
| | 0 | 565 | | ? productName |
| | 0 | 566 | | : (productName.Length > 110 ? productName.Substring(0, 110) + "..." : productName); |
| | | 567 | | |
| | | 568 | | <div id="thProductName" class="text-muted" title="@productName" style="overflow: hidden; |
| | | 569 | | <a href="http://studio-web-client.interparts.ru/info?producerId=@item.EtProducer?.Id |
| | | 570 | | target="_blank" |
| | | 571 | | class="custom-product-link"> |
| | 0 | 572 | | @truncatedName |
| | | 573 | | </a> |
| | | 574 | | </div> |
| | | 575 | | } |
| | | 576 | | </div> |
| | | 577 | | </div> |
| | | 578 | | </td> |
| | | 579 | | |
| | | 580 | | <td class="checkbox-column-cell" data-column="checkboxActions"> |
| | | 581 | | <div class="form-check"> |
| | 0 | 582 | | <input type="checkbox" class="form-check-input record-checkbox" data-id="@item.Id"> |
| | | 583 | | </div> |
| | | 584 | | </td> |
| | | 585 | | |
| | | 586 | | <td class="status-column-cell" data-column="status"> |
| | 0 | 587 | | @if (L("ClientStatus", "Статус клиента")) |
| | | 588 | | { |
| | | 589 | | <div |
| | | 590 | | class="nowrap" |
| | | 591 | | title="@(item.ClientStatus?.Name ?? "")"> |
| | 0 | 592 | | @if (item.ClientStatus?.Name != null) |
| | | 593 | | { |
| | 0 | 594 | | string status = truncateStatus(item.ClientStatus.Name); |
| | | 595 | | |
| | 0 | 596 | | var bgClientStatusColor = item.ClientStatus?.StatusColor?.ColorCode ?? "transparent"; |
| | 0 | 597 | | var textColor = StatusColorContrast.GetTextColor(bgClientStatusColor); |
| | | 598 | | |
| | | 599 | | <div class="status-color-badge orders-chip" |
| | | 600 | | style="--status-badge-bg:@bgClientStatusColor; --status-badge-fg:@textColor; backgr |
| | 0 | 601 | | @status |
| | | 602 | | </div> |
| | 0 | 603 | | var cancellation = item.LatestOzonPostingCancellation; |
| | 0 | 604 | | var isCancelledClientStatus = item.ClientStatus?.Name is "Отменен" or "Отменён" or "Отме |
| | 0 | 605 | | var hasCancellationInfo = cancellation != null && |
| | 0 | 606 | | (!string.IsNullOrWhiteSpace(cancellation.ReasonTitle) || !string.IsNullOrWhiteSpace( |
| | 0 | 607 | | var cancellationTooltip = hasCancellationInfo |
| | 0 | 608 | | ? $"Причина отмены: {cancellation!.ReasonTitle ?? cancellation.ReasonMessage}" + |
| | 0 | 609 | | (cancellation.ReasonId.HasValue ? $" (ID: {cancellation.ReasonId})" : string.Empty |
| | 0 | 610 | | (!string.IsNullOrWhiteSpace(cancellation.ReasonTypeId) ? $"; инициатор: {cancellat |
| | 0 | 611 | | (!string.IsNullOrWhiteSpace(cancellation.ReasonMessage) && cancellation.ReasonMess |
| | 0 | 612 | | : string.Empty; |
| | 0 | 613 | | @if (isCancelledClientStatus && hasCancellationInfo) |
| | | 614 | | { |
| | 0 | 615 | | <span class="ozon-cancellation-info" title="@cancellationTooltip" data-app-tooltip=" |
| | | 616 | | <i class="fas fa-circle-info" aria-hidden="true"></i> |
| | | 617 | | </span> |
| | | 618 | | } |
| | | 619 | | } |
| | | 620 | | else |
| | | 621 | | { |
| | | 622 | | <div></div> |
| | | 623 | | } |
| | | 624 | | <span class="shipment-totals-badge" |
| | 0 | 625 | | data-order-id="@item.Id" |
| | | 626 | | style="font-size: 13px; margin-left: 4px; display: inline-block; cursor: pointer; flex |
| | | 627 | | onclick="showOrderShipmentTotals(@item.Id);" |
| | | 628 | | title="Открыть итоги по отправлению"></span> |
| | | 629 | | </div> |
| | | 630 | | } |
| | | 631 | | |
| | 0 | 632 | | @if (L("AppStatus", "Статус")) |
| | | 633 | | { |
| | | 634 | | <div |
| | | 635 | | class="text-muted nowrap app-status-line" |
| | | 636 | | title="@item.AppStatus?.Name"> |
| | | 637 | | @{ |
| | 0 | 638 | | string appStatus = truncateStatus(item.AppStatus?.Name); |
| | | 639 | | |
| | 0 | 640 | | var bgAppStatusColor = item.AppStatus?.StatusColor?.ColorCode ?? "transparent"; |
| | 0 | 641 | | var textColor = StatusColorContrast.GetTextColor(bgAppStatusColor); |
| | | 642 | | } |
| | | 643 | | |
| | | 644 | | <a asp-area="Studio2" |
| | | 645 | | asp-controller="OrderStatusFlow" |
| | | 646 | | asp-action="OrderStatus" |
| | 0 | 647 | | asp-route-id="@item.Id" |
| | | 648 | | target="_blank" |
| | | 649 | | style="text-decoration: none; display: inline-block;"> |
| | | 650 | | <div class="nowrap status-color-badge orders-chip orders-chip--action" |
| | | 651 | | style="--status-badge-bg:@bgAppStatusColor; --status-badge-fg:@textColor; backgroun |
| | 0 | 652 | | @appStatus |
| | | 653 | | </div> |
| | | 654 | | </a> |
| | | 655 | | </div> |
| | | 656 | | } |
| | | 657 | | |
| | | 658 | | @* Третья строка ячейки — состояние заказа у API-поставщика (OrderSupplierStates). |
| | | 659 | | Показывается под системным статусом: в корзине, заказан, затем живой статус от API. |
| | | 660 | | Доступ гейтится тем же правом, что и системный статус. *@ |
| | 0 | 661 | | @if (L("AppStatus", "Статус") && !string.IsNullOrWhiteSpace(item.SupplierStatusName)) |
| | | 662 | | { |
| | 0 | 663 | | var bgSupplierColor = item.SupplierStatusColor ?? "transparent"; |
| | 0 | 664 | | var supplierTextColor = StatusColorContrast.GetTextColor(bgSupplierColor); |
| | 0 | 665 | | var supplierName = item.SupplierStateSupplierName ?? item.SupplierStateProviderKey; |
| | 0 | 666 | | var supplierTooltip = $"{supplierName}: {item.SupplierStatusName}"; |
| | 0 | 667 | | if (!string.IsNullOrWhiteSpace(item.SupplierStateExternalOrderNumber)) |
| | | 668 | | { |
| | 0 | 669 | | supplierTooltip += $" · заказ {item.SupplierStateExternalOrderNumber}"; |
| | | 670 | | } |
| | 0 | 671 | | var supplierCartUrl = $"/SupplierCart?provider={Uri.EscapeDataString((item.SupplierStateProvider |
| | | 672 | | |
| | | 673 | | <div class="text-muted nowrap supplier-status-line" title="@supplierTooltip"> |
| | | 674 | | <a href="@supplierCartUrl" |
| | | 675 | | target="_blank" |
| | | 676 | | style="text-decoration: none; display: inline-block;"> |
| | | 677 | | <div class="nowrap status-color-badge supplier-status-badge orders-chip orders-chip--act |
| | | 678 | | style="--status-badge-bg:@bgSupplierColor; --status-badge-fg:@supplierTextColor; ba |
| | 0 | 679 | | @truncateStatus(item.SupplierStatusName) |
| | | 680 | | </div> |
| | | 681 | | </a> |
| | | 682 | | </div> |
| | | 683 | | } |
| | | 684 | | |
| | | 685 | | @{ |
| | 0 | 686 | | var returnStatusText = item.ReturnStatusDisplayName; |
| | 0 | 687 | | if (string.IsNullOrWhiteSpace(returnStatusText)) |
| | | 688 | | { |
| | 0 | 689 | | returnStatusText = item.ReturnStatusSysName; |
| | | 690 | | } |
| | 0 | 691 | | returnStatusText = truncateStatus(returnStatusText); |
| | 0 | 692 | | var showReturnStatusRow = item.IsReturned |
| | 0 | 693 | | || !string.IsNullOrWhiteSpace(returnStatusText) |
| | 0 | 694 | | || !string.IsNullOrWhiteSpace(item.ReturnWarehouseName); |
| | | 695 | | } |
| | 0 | 696 | | @if (showReturnStatusRow) |
| | | 697 | | { |
| | | 698 | | <div class="text-muted nowrap" |
| | | 699 | | title="@(string.IsNullOrWhiteSpace(item.ReturnStatusDisplayName) ? item.ReturnStatusSysName |
| | | 700 | | <a href="@Url.Action("Index", "OrderReturns", new { area = "Studio2", orderId = item.Id })" |
| | | 701 | | target="_blank" |
| | | 702 | | title="Возврат — открыть детали" |
| | | 703 | | class="orders-chip orders-chip--action orders-chip--return">Возв.</a> |
| | 0 | 704 | | @if (!string.IsNullOrWhiteSpace(returnStatusText)) |
| | | 705 | | { |
| | 0 | 706 | | <span class="orders-chip orders-chip--neutral">@returnStatusText</span> |
| | | 707 | | } |
| | | 708 | | </div> |
| | | 709 | | } |
| | 0 | 710 | | @if (item.CartItemStatus != null) |
| | | 711 | | { |
| | 0 | 712 | | var cartStatus = truncateStatus(item.CartItemStatus.Name); |
| | | 713 | | |
| | 0 | 714 | | var bgColor = item.CartItemStatusColor?.ColorCode ?? "transparent"; |
| | 0 | 715 | | var cartTextColor = StatusColorContrast.GetTextColor(bgColor); |
| | | 716 | | |
| | | 717 | | <a href="@Url.Action("CartStatuses", "OrderCartItemStatus", new { area = "Studio2", highlightId |
| | | 718 | | class="text-muted nowrap custom-product-link status-color-badge orders-chip orders-chip--acti |
| | | 719 | | style="--status-badge-bg:@bgColor; --status-badge-fg:@cartTextColor; background-color:@bgColo |
| | | 720 | | title="@item.CartItemStatus.Name"> |
| | 0 | 721 | | @cartStatus |
| | | 722 | | </a> |
| | | 723 | | } |
| | | 724 | | </td> |
| | | 725 | | |
| | | 726 | | <td> |
| | 0 | 727 | | @if (L("ShipmentWarehouse", "Склад отгрузки")) |
| | | 728 | | { |
| | 0 | 729 | | var zasyl = item.OrderExtensions? |
| | 0 | 730 | | .FirstOrDefault(e => e.Key == "Засыл")?.Value; |
| | | 731 | | <div |
| | | 732 | | class="@(item.UpdatedColumns?.Contains("ShipmentWarehouseId") == true ? "bg-warning" : null) |
| | 0 | 733 | | @(item.ShipmentWarehouse?.Name ?? "") |
| | 0 | 734 | | @if (!string.IsNullOrEmpty(zasyl)) |
| | | 735 | | { |
| | 0 | 736 | | <span class="text-muted"> [@zasyl]</span> |
| | | 737 | | } |
| | | 738 | | </div> |
| | | 739 | | } |
| | | 740 | | |
| | 0 | 741 | | @if (L("ShipmentWarehouse", "Склад отгрузки")) |
| | | 742 | | { |
| | | 743 | | <div |
| | | 744 | | class="text-muted" |
| | | 745 | | title="@item.FullFormattedLastStatusChangeDate"> |
| | 0 | 746 | | @Html.DisplayFor(modelItem => item.FormattedLastStatusChangeDate) |
| | | 747 | | </div> |
| | | 748 | | } |
| | 0 | 749 | | @if (!string.IsNullOrWhiteSpace(item.ReturnWarehouseName)) |
| | | 750 | | { |
| | | 751 | | <div class="text-muted nowrap" title="@item.ReturnWarehouseName"> |
| | 0 | 752 | | @item.ReturnWarehouseName |
| | | 753 | | </div> |
| | | 754 | | } |
| | | 755 | | </td> |
| | | 756 | | <td data-column="supplier"> |
| | | 757 | | <div class="two-row-cell"> |
| | 0 | 758 | | @if (L("Supplier", "Поставщик")) |
| | | 759 | | { |
| | | 760 | | <div class="cell-row@(upd("SupplierId"))"> |
| | 0 | 761 | | @(item.Supplier?.Name ?? "") |
| | | 762 | | </div> |
| | | 763 | | } |
| | | 764 | | |
| | 0 | 765 | | @if (!string.IsNullOrWhiteSpace(item.SupplierStateDirection)) |
| | | 766 | | { |
| | | 767 | | <div class="cell-row text-muted" style="padding-left: 10px;" title="@($"Направление поставщи |
| | 0 | 768 | | <span aria-hidden="true">↳</span> @item.SupplierStateDirection |
| | | 769 | | </div> |
| | | 770 | | } |
| | | 771 | | |
| | 0 | 772 | | @if (L("SupplierOrderNumber", "Номер заказа поставщику")) |
| | | 773 | | { |
| | | 774 | | <div class="cell-row text-muted supplier-status-provider-row@(upd("OrderNumberToSupplier"))" |
| | | 775 | | <span class="supplier-order-number-text" |
| | | 776 | | title="@item.OrderNumberToSupplier" |
| | 0 | 777 | | data-placement="top">@Html.DisplayFor(modelItem => item.OrderNumberToSupplier)</sp |
| | | 778 | | </div> |
| | | 779 | | } |
| | | 780 | | </div> |
| | | 781 | | </td> |
| | | 782 | | |
| | | 783 | | <td> |
| | | 784 | | <div class="two-row-cell"> |
| | 0 | 785 | | @if (L("Category", "Категория")) |
| | | 786 | | { |
| | | 787 | | <div class="cell-row@(upd("ProductInfoId"))"> |
| | 0 | 788 | | @if (!string.IsNullOrEmpty(item.ProductInfo?.CommercialCategory)) |
| | | 789 | | { |
| | 0 | 790 | | string сommercialCategory = item.ProductInfo.CommercialCategory; |
| | 0 | 791 | | if (сommercialCategory.Length > 10) |
| | | 792 | | { |
| | 0 | 793 | | сommercialCategory = сommercialCategory.Substring(0, 10) + "..."; |
| | | 794 | | } |
| | | 795 | | |
| | 0 | 796 | | @сommercialCategory |
| | | 797 | | } |
| | | 798 | | |
| | | 799 | | </div> |
| | | 800 | | } |
| | | 801 | | |
| | 0 | 802 | | @if (L("VolumeWeight", "Объемный вес")) |
| | | 803 | | { |
| | | 804 | | <div class="cell-row justify-content-end text-muted@(upd("ProductInfoId"))"> |
| | 0 | 805 | | @if (item.ProductInfo != null) |
| | | 806 | | { |
| | 0 | 807 | | @Html.DisplayFor(modelItem => item.ProductInfo.VolumetricWeight) |
| | 0 | 808 | | @if (item.ProductInfo.Weight != 0) |
| | | 809 | | { |
| | | 810 | | <div style="white-space: nowrap; margin: 0; font-size: 10px; text-align: right"> |
| | 0 | 811 | | @Html.DisplayFor(modelItem => item.ProductInfo.Weight) |
| | | 812 | | </div> |
| | | 813 | | } |
| | | 814 | | } |
| | | 815 | | </div> |
| | | 816 | | } |
| | | 817 | | </div> |
| | | 818 | | </td> |
| | | 819 | | |
| | | 820 | | <td data-column="sitePrice"> |
| | | 821 | | <div class="two-row-cell"> |
| | 0 | 822 | | @if (L("SitePrice", "Цена сайта")) |
| | | 823 | | { |
| | | 824 | | <div |
| | | 825 | | class="cell-row justify-content-end@(upd("ProductInfoId"))"> |
| | 0 | 826 | | @if (item.ProductInfo != null) |
| | | 827 | | { |
| | 0 | 828 | | @OrderCellFormat.Money(item.ProductInfo.CurrentPriceWithDiscount) |
| | | 829 | | } |
| | | 830 | | </div> |
| | | 831 | | } |
| | | 832 | | |
| | 0 | 833 | | @if (L("Quantity", "Количество")) |
| | | 834 | | { |
| | | 835 | | <div |
| | | 836 | | class="cell-row justify-content-end text-muted quantity-cell" |
| | | 837 | | id="quantity_@item.Id"> |
| | | 838 | | <span class="@(item.Quantity > 1 ? "qty-value--multi" : "")"> |
| | 0 | 839 | | @Html.DisplayFor(modelItem => item.Quantity) |
| | | 840 | | </span> |
| | | 841 | | </div> |
| | | 842 | | } |
| | | 843 | | </div> |
| | | 844 | | </td> |
| | | 845 | | |
| | | 846 | | <td data-column="price"> |
| | | 847 | | <div class="two-row-cell"> |
| | 0 | 848 | | @if (L("Price", "Цена")) |
| | | 849 | | { |
| | | 850 | | <div class="cell-row justify-content-end"> |
| | 0 | 851 | | @if (item.OzonPromotionSnapshot?.HasAnyPromotion == true) |
| | | 852 | | { |
| | | 853 | | <span class="ozon-promotion-icon" |
| | 0 | 854 | | data-app-tooltip-html="@GetPromotionTooltip(item.OzonPromotionSnapshot)" |
| | | 855 | | data-app-tooltip-variant="promotion" |
| | | 856 | | aria-label="Товар участвует в акции Ozon"> |
| | | 857 | | <i class="fas fa-tags" aria-hidden="true"></i> |
| | | 858 | | </span> |
| | | 859 | | } |
| | | 860 | | <span class="@GetPriceTextColorClass(item.OrderPrice?.PriceIndexes?.ColorIndex)" |
| | 0 | 861 | | data-app-tooltip-html="@GetPriceTooltip(item)" |
| | | 862 | | data-app-tooltip-variant="price"> |
| | 0 | 863 | | @OrderCellFormat.Money(item.Price) |
| | | 864 | | </span> |
| | | 865 | | </div> |
| | | 866 | | } |
| | | 867 | | |
| | 0 | 868 | | @if (L("ShipmentTotal", "Сумма отправления")) |
| | | 869 | | { |
| | | 870 | | <div |
| | | 871 | | class="cell-row justify-content-end@(upd("ShipmentAmount"))"> |
| | 0 | 872 | | @OrderCellFormat.Money(item.ShipmentAmount) |
| | | 873 | | </div> |
| | | 874 | | } |
| | | 875 | | </div> |
| | | 876 | | </td> |
| | | 877 | | |
| | | 878 | | <td data-column="purchasePrice"> |
| | | 879 | | <div class="two-row-cell"> |
| | 0 | 880 | | @if (L("PurchasePrice", "Цена закупки")) |
| | | 881 | | { |
| | | 882 | | <div |
| | | 883 | | class="cell-row align-items-сenter justify-content-end@(upd("PurchasePrice"))" |
| | | 884 | | style="white-space: nowrap; width: 100%" |
| | 0 | 885 | | data-app-tooltip-html="@GetPurchasePriceTooltip(item)" |
| | | 886 | | data-app-tooltip-variant="purchase-price" |
| | | 887 | | > |
| | | 888 | | <p class="align-self-end m-0"> |
| | 0 | 889 | | @OrderCellFormat.Money(item.PurchasePrice) |
| | | 890 | | </p> |
| | | 891 | | </div> |
| | | 892 | | } |
| | | 893 | | |
| | 0 | 894 | | @if (L("Cost", "Себестоимость")) |
| | | 895 | | { |
| | 0 | 896 | | var hasExtraExpenses = item.ExtraExpenses != null && item.ExtraExpenses.Any(e => e.ExpenseTy |
| | 0 | 897 | | var extraExpensesTotal = hasExtraExpenses ? (item.ExtraExpenses ?? Enumerable.Empty<OrderExt |
| | 0 | 898 | | var validExtraExpenses = hasExtraExpenses ? (item.ExtraExpenses ?? Enumerable.Empty<OrderExt |
| | | 899 | | <div |
| | | 900 | | class="cell-row justify-content-end align-items-start@(upd("CostPrice"))" |
| | | 901 | | style="position: relative;"> |
| | | 902 | | <span style="position: relative; display: inline-block;" |
| | | 903 | | class="cost-price-with-expenses"> |
| | 0 | 904 | | @OrderCellFormat.Money(item.CostPrice) |
| | 0 | 905 | | @if (hasExtraExpenses) |
| | | 906 | | { |
| | | 907 | | <span class="extra-expenses-indicator" |
| | | 908 | | style="position: absolute; top: -2px; right: -8px; color: #dc3545; font-si |
| | | 909 | | data-app-tooltip-variant="expenses" |
| | 0 | 910 | | data-app-tooltip-html="<div style='text-align: left; padding: 4px;'><b>Доп |
| | | 911 | | ▼ |
| | | 912 | | </span> |
| | | 913 | | } |
| | | 914 | | </span> |
| | | 915 | | </div> |
| | | 916 | | } |
| | | 917 | | </div> |
| | | 918 | | </td> |
| | 0 | 919 | | @if (L("OzonCommission", "Комиссия ОЗОН")) |
| | | 920 | | { |
| | | 921 | | <td data-column="commission"> |
| | | 922 | | <div |
| | | 923 | | class="two-row-cell@(upd("MinOzonCommission", "MaxOzonCommission"))"> |
| | | 924 | | <div class="cell-row"> |
| | 0 | 925 | | @OrderCellFormat.Money(item.MinOzonCommission) |
| | | 926 | | </div> |
| | | 927 | | <div class="cell-row"> |
| | 0 | 928 | | @OrderCellFormat.Money(item.MaxOzonCommission) |
| | | 929 | | </div> |
| | | 930 | | </div> |
| | | 931 | | </td> |
| | | 932 | | } |
| | | 933 | | |
| | 0 | 934 | | @if (L("Profit", "Прибыль")) |
| | | 935 | | { |
| | | 936 | | <td data-column="profit"> |
| | | 937 | | <div |
| | | 938 | | class="two-row-cell@(upd("MinProfit", "MaxProfit"))"> |
| | | 939 | | <div class="cell-row"> |
| | 0 | 940 | | <span class="@OrderCellFormat.SignClass(item.MaxProfit)">@OrderCellFormat.Money(item.Max |
| | | 941 | | </div> |
| | | 942 | | <div class="cell-row"> |
| | 0 | 943 | | <span class="@OrderCellFormat.SignClass(item.MinProfit)">@OrderCellFormat.Money(item.Min |
| | | 944 | | </div> |
| | | 945 | | </div> |
| | | 946 | | @* 13,5% от суммы отправления — только при наведении, как доставка в номере заказа *@ |
| | 0 | 947 | | @if (item.TargetShipmentProfit135.HasValue) |
| | | 948 | | { |
| | 0 | 949 | | var targetText = OrderCellFormat.Money(item.TargetShipmentProfit135); |
| | 0 | 950 | | var targetSlashPrefix = item.MaxProfit.HasValue && item.MaxProfit.Value != 0 ? "/ " : ""; |
| | | 951 | | <div class="orders-profit-target-detail text-muted" |
| | | 952 | | title="Целевая прибыль 13,5% от суммы отправления" |
| | | 953 | | style="white-space: nowrap; font-size: 0.75em;"> |
| | 0 | 954 | | @targetSlashPrefix@targetText |
| | | 955 | | </div> |
| | | 956 | | } |
| | | 957 | | </td> |
| | | 958 | | } |
| | | 959 | | |
| | 0 | 960 | | @if (L("MarkupPercent", "Наценка %")) |
| | | 961 | | { |
| | | 962 | | <td data-column="markup"> |
| | | 963 | | <div |
| | | 964 | | class="two-row-cell@(upd("MinDiscount"))"> |
| | | 965 | | <div class="cell-row"> |
| | 0 | 966 | | <span class="@OrderCellFormat.SignClass(item.MaxDiscount)">@OrderCellFormat.Money(item.M |
| | 0 | 967 | | @if (item.MaxDiscount.HasValue) |
| | | 968 | | { |
| | | 969 | | <span class="orders-unit">%</span> |
| | | 970 | | } |
| | | 971 | | </div> |
| | | 972 | | <div class="cell-row"> |
| | 0 | 973 | | <span class="@OrderCellFormat.SignClass(item.MinDiscount)">@OrderCellFormat.Money(item.M |
| | 0 | 974 | | @if (item.MinDiscount.HasValue) |
| | | 975 | | { |
| | | 976 | | <span class="orders-unit">%</span> |
| | | 977 | | } |
| | | 978 | | </div> |
| | | 979 | | </div> |
| | | 980 | | </td> |
| | | 981 | | } |
| | | 982 | | </tr> |
| | 0 | 983 | | } |
| | 0 | 984 | | @if (!Model.Items.Any()) |
| | | 985 | | { |
| | | 986 | | <tr class="orders-empty-row"> |
| | | 987 | | <td colspan="100"> |
| | | 988 | | <div class="orders-empty-state d-flex flex-column align-items-center justify-content-center py-5"> |
| | | 989 | | <svg width="184" height="152" viewBox="0 0 184 152" xmlns="http://www.w3.org/2000/svg" |
| | | 990 | | class="mb-3"> |
| | | 991 | | <g fill="none" fill-rule="evenodd"> |
| | | 992 | | <g transform="translate(24 31.67)"> |
| | | 993 | | <ellipse fill-opacity=".8" fill="#F5F5F7" cx="67.797" cy="106.89" rx="67.797" |
| | | 994 | | ry="12.668"/> |
| | | 995 | | <path |
| | | 996 | | d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1. |
| | | 997 | | fill="#AEB8C2"/> |
| | | 998 | | <path |
| | | 999 | | d="M101.537 86.214L80.63 61.102c-1.001-1.207-2.507-1.867-4.048-1.867H31.724c-1.5 |
| | | 1000 | | fill="url(#linearGradient-1)" transform="translate(13.56)"/> |
| | | 1001 | | <path |
| | | 1002 | | d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 |
| | | 1003 | | fill="#F5F5F7"/> |
| | | 1004 | | <path |
| | | 1005 | | d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V1 |
| | | 1006 | | fill="#DCE0E6"/> |
| | | 1007 | | </g> |
| | | 1008 | | <path |
| | | 1009 | | d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6. |
| | | 1010 | | fill="#DCE0E6"/> |
| | | 1011 | | <g transform="translate(149.65 15.383)" fill="#FFF"> |
| | | 1012 | | <ellipse cx="20.654" cy="3.167" rx="2.849" ry="2.815"/> |
| | | 1013 | | <path d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z"/> |
| | | 1014 | | </g> |
| | | 1015 | | </g> |
| | | 1016 | | </svg> |
| | | 1017 | | <p class="text-muted fs-5 mb-1">Ничего не найдено</p> |
| | | 1018 | | <p class="text-muted small">Попробуйте изменить параметры фильтрации</p> |
| | | 1019 | | </div> |
| | | 1020 | | </td> |
| | | 1021 | | </tr> |
| | | 1022 | | } |
| | | 1023 | | </tbody> |
| | | 1024 | | |
| | | 1025 | | @functions { |
| | | 1026 | | |
| | | 1027 | | public string GetPriceTextColorClass(string? colorIndex) |
| | | 1028 | | { |
| | 0 | 1029 | | return colorIndex?.ToUpper() switch |
| | 0 | 1030 | | { |
| | 0 | 1031 | | "SUPER" => "text-success fw-bold", |
| | 0 | 1032 | | "GREEN" => "text-success", |
| | 0 | 1033 | | "YELLOW" => "text-warning", |
| | 0 | 1034 | | "RED" => "text-danger", |
| | 0 | 1035 | | "INDEX" => "text-secondary", |
| | 0 | 1036 | | "UNSPECIFIED" => "text-muted", |
| | 0 | 1037 | | _ => "" |
| | 0 | 1038 | | }; |
| | | 1039 | | } |
| | | 1040 | | |
| | | 1041 | | public string GetClientTooltip(OzonClient? client) |
| | | 1042 | | { |
| | 0 | 1043 | | if (client == null) |
| | 0 | 1044 | | return "Информация о клиенте отсутствует"; |
| | | 1045 | | |
| | 0 | 1046 | | var tooltipLines = new List<string>(); |
| | | 1047 | | |
| | | 1048 | | // Тип клиента |
| | 0 | 1049 | | string clientTypeText = client.ClientType switch |
| | 0 | 1050 | | { |
| | 0 | 1051 | | ClientType.OZON => "Ozon", |
| | 0 | 1052 | | ClientType.YANDEX => "Yandex", |
| | 0 | 1053 | | _ => "Другое" |
| | 0 | 1054 | | }; |
| | | 1055 | | |
| | 0 | 1056 | | string clientTypeColor = client.ClientType switch |
| | 0 | 1057 | | { |
| | 0 | 1058 | | ClientType.OZON => "#007bff", |
| | 0 | 1059 | | ClientType.YANDEX => "#dc3545", |
| | 0 | 1060 | | _ => "#6c757d" |
| | 0 | 1061 | | }; |
| | | 1062 | | |
| | 0 | 1063 | | tooltipLines.Add($"<div style='font-weight: bold; margin-bottom: 8px;'>Тип клиента: <span style='color: {clientT |
| | | 1064 | | |
| | | 1065 | | // ИНН |
| | 0 | 1066 | | if (!string.IsNullOrEmpty(client.INNCode)) |
| | | 1067 | | { |
| | 0 | 1068 | | tooltipLines.Add($"<div style='margin: 4px 0;'><b>ИНН:</b> {System.Net.WebUtility.HtmlEncode(client.INNCode) |
| | | 1069 | | } |
| | | 1070 | | else |
| | | 1071 | | { |
| | 0 | 1072 | | tooltipLines.Add($"<div style='margin: 4px 0; color: #6c757d;'><b>ИНН:</b> не указан</div>"); |
| | | 1073 | | } |
| | | 1074 | | |
| | | 1075 | | // Склад |
| | 0 | 1076 | | if (!string.IsNullOrEmpty(client.WarehouseName)) |
| | | 1077 | | { |
| | 0 | 1078 | | tooltipLines.Add($"<div style='margin: 4px 0;'><b>Склад:</b> {System.Net.WebUtility.HtmlEncode(client.Wareho |
| | | 1079 | | } |
| | | 1080 | | else |
| | | 1081 | | { |
| | 0 | 1082 | | tooltipLines.Add($"<div style='margin: 4px 0; color: #6c757d;'><b>Склад:</b> не указан</div>"); |
| | | 1083 | | } |
| | | 1084 | | |
| | | 1085 | | // Валюта |
| | 0 | 1086 | | tooltipLines.Add($"<div style='margin: 4px 0;'><b>Валюта:</b> {client.CurrencyCode}</div>"); |
| | | 1087 | | |
| | | 1088 | | // Обертка для светлого фона и ширины |
| | 0 | 1089 | | return $@" |
| | 0 | 1090 | | <div style=' |
| | 0 | 1091 | | |
| | 0 | 1092 | | background: white; |
| | 0 | 1093 | | color: #333; |
| | 0 | 1094 | | padding: 12px; |
| | 0 | 1095 | | border-radius: 6px; |
| | 0 | 1096 | | border: 1px solid #ddd; |
| | 0 | 1097 | | box-shadow: 0 4px 12px rgba(0,0,0,0.15); |
| | 0 | 1098 | | min-width: 200px; |
| | 0 | 1099 | | font-family: Arial, sans-serif; |
| | 0 | 1100 | | font-size: 13px; |
| | 0 | 1101 | | line-height: 1.4; |
| | 0 | 1102 | | '> |
| | 0 | 1103 | | {string.Join("", tooltipLines)} |
| | 0 | 1104 | | </div>"; |
| | | 1105 | | } |
| | | 1106 | | |
| | | 1107 | | |
| | | 1108 | | // Метод для получения цвета индекса на основе значения |
| | | 1109 | | public string GetIndexColor(decimal indexValue) |
| | | 1110 | | { |
| | 0 | 1111 | | return indexValue switch |
| | 0 | 1112 | | { |
| | 0 | 1113 | | > 1.2m => "#28a745", // Зеленый - выгодно |
| | 0 | 1114 | | > 0.8m => "#ffc107", // Желтый - умеренно |
| | 0 | 1115 | | _ => "#dc3545" // Красный - невыгодно |
| | 0 | 1116 | | }; |
| | | 1117 | | } |
| | | 1118 | | |
| | | 1119 | | // Метод для получения HEX цвета индекса |
| | | 1120 | | public string GetColorIndexHex(string? colorIndex) |
| | | 1121 | | { |
| | 0 | 1122 | | return colorIndex?.ToUpper() switch |
| | 0 | 1123 | | { |
| | 0 | 1124 | | "SUPER" => "#198754", // Темно-зеленый |
| | 0 | 1125 | | "GREEN" => "#28a745", // Зеленый |
| | 0 | 1126 | | "YELLOW" => "#ffc107", // Желтый |
| | 0 | 1127 | | "RED" => "#dc3545", // Красный |
| | 0 | 1128 | | "INDEX" => "#6c757d", // Серый |
| | 0 | 1129 | | "UNSPECIFIED" => "#6c757d", // Серый |
| | 0 | 1130 | | _ => "#6c757d" // Серый по умолчанию |
| | 0 | 1131 | | }; |
| | | 1132 | | } |
| | | 1133 | | |
| | | 1134 | | |
| | | 1135 | | public string GetColorIndexDescription(string? colorIndex) |
| | | 1136 | | { |
| | 0 | 1137 | | return colorIndex?.ToUpper() switch |
| | 0 | 1138 | | { |
| | 0 | 1139 | | "SUPER" => "Супервыгодный", |
| | 0 | 1140 | | "GREEN" => "Выгодный", |
| | 0 | 1141 | | "YELLOW" => "Умеренный", |
| | 0 | 1142 | | "RED" => "Невыгодный", |
| | 0 | 1143 | | "INDEX" => "Отсутствует", |
| | 0 | 1144 | | "UNSPECIFIED" => "Не определён", |
| | 0 | 1145 | | _ => "Не определён" |
| | 0 | 1146 | | }; |
| | | 1147 | | } |
| | | 1148 | | |
| | | 1149 | | public string GetPriceTooltip(Order item) |
| | | 1150 | | { |
| | 0 | 1151 | | var price = item.OrderPrice; |
| | 0 | 1152 | | if (price == null) |
| | 0 | 1153 | | return "Информация о проценке Ozon отсутствует"; |
| | | 1154 | | |
| | 0 | 1155 | | var snapshot = item.OzonPromotionSnapshot; |
| | 0 | 1156 | | var indexes = price.PriceIndexes; |
| | 0 | 1157 | | var currency = price.CurrencyCode; |
| | 0 | 1158 | | var actionPrice = snapshot?.MarketingSellerPrice ?? price.MarketingSellerPrice; |
| | 0 | 1159 | | var netPrice = snapshot?.NetPrice ?? price.NetPrice; |
| | 0 | 1160 | | var retailPrice = snapshot?.RetailPrice ?? price.RetailPrice; |
| | 0 | 1161 | | var vat = snapshot?.Vat ?? price.Vat; |
| | 0 | 1162 | | var acquiring = snapshot?.Acquiring; |
| | 0 | 1163 | | var indexColor = GetColorIndexHex(indexes?.ColorIndex); |
| | 0 | 1164 | | var indexDescription = GetColorIndexDescription(indexes?.ColorIndex); |
| | | 1165 | | |
| | 0 | 1166 | | static string Money(decimal value, string currencyCode) => $"{value:N2} {System.Net.WebUtility.HtmlEncode(curren |
| | | 1167 | | static string Row(string title, string apiField, string value) => |
| | 0 | 1168 | | $"<div class='price-tooltip-row'><span title='{System.Net.WebUtility.HtmlEncode(apiField)}'>{System.Net.WebU |
| | | 1169 | | |
| | 0 | 1170 | | var priceRows = string.Join(string.Empty, |
| | 0 | 1171 | | [ |
| | 0 | 1172 | | Row("Текущая цена", "price", Money(price.Price, currency)), |
| | 0 | 1173 | | Row("Маркетинговая цена", "marketing_price", Money(price.MarketingPrice, currency)), |
| | 0 | 1174 | | Row("Цена с акциями", "marketing_seller_price", Money(actionPrice, currency)), |
| | 0 | 1175 | | Row("Минимальная цена", "min_price", Money(price.MinPrice, currency)), |
| | 0 | 1176 | | Row("Старая цена", "old_price", Money(price.OldPrice, currency)), |
| | 0 | 1177 | | Row("Розничная цена", "retail_price", Money(retailPrice, currency)), |
| | 0 | 1178 | | Row("Чистая цена", "net_price", Money(netPrice, currency)) |
| | 0 | 1179 | | ]); |
| | | 1180 | | |
| | 0 | 1181 | | var indexRows = indexes == null |
| | 0 | 1182 | | ? "<div class='price-tooltip-empty'>Индексы Ozon не получены.</div>" |
| | 0 | 1183 | | : string.Join(string.Empty, |
| | 0 | 1184 | | [ |
| | 0 | 1185 | | Row("Ozon", "ozon_index_data", $"{Money(indexes.OzonMinPrice, indexes.OzonMinPriceCurrency)} · <em>{inde |
| | 0 | 1186 | | Row("Внешние конкуренты", "external_index_data", $"{Money(indexes.ExternalMinPrice, indexes.ExternalMinP |
| | 0 | 1187 | | Row("Собственные МП", "self_marketplaces_index_data", $"{Money(indexes.SelfMarketplacesMinPrice, indexes |
| | 0 | 1188 | | ]); |
| | | 1189 | | |
| | 0 | 1190 | | var serviceRows = string.Join(string.Empty, |
| | 0 | 1191 | | [ |
| | 0 | 1192 | | Row("НДС", "vat", $"{vat:N2}%"), |
| | 0 | 1193 | | Row("Автоакции", "auto_action_enabled", price.AutoActionEnabled ? "Включены" : "Выключены"), |
| | 0 | 1194 | | Row("Автодобавление", "auto_add_to_ozon_actions_list_enabled", price.AutoAddToOzonActionsListEnabled ? "Вклю |
| | 0 | 1195 | | acquiring.HasValue ? Row("Эквайринг", "acquiring", Money(acquiring.Value, currency)) : string.Empty |
| | 0 | 1196 | | ]); |
| | | 1197 | | |
| | 0 | 1198 | | return $@" |
| | 0 | 1199 | | <div class='price-tooltip-box'> |
| | 0 | 1200 | | <div class='price-tooltip-title'><i class='fas fa-ruble-sign'></i> Проценка Ozon</div> |
| | 0 | 1201 | | <div class='price-tooltip-index'>Ценовой индекс: <b style='color: {indexColor};'>{indexDescription}</b>< |
| | 0 | 1202 | | <div class='price-tooltip-section'>Цены из <code>/v5/product/info/prices</code></div> |
| | 0 | 1203 | | <div class='price-tooltip-rows'>{priceRows}</div> |
| | 0 | 1204 | | <div class='price-tooltip-section'>Индексы: минимальная цена · индекс</div> |
| | 0 | 1205 | | <div class='price-tooltip-rows'>{indexRows}</div> |
| | 0 | 1206 | | <div class='price-tooltip-section'>Параметры</div> |
| | 0 | 1207 | | <div class='price-tooltip-rows'>{serviceRows}</div> |
| | 0 | 1208 | | </div>"; |
| | | 1209 | | } |
| | | 1210 | | |
| | | 1211 | | public string GetPromotionTooltip(OrderOzonPromotionSnapshot snapshot) |
| | | 1212 | | { |
| | 0 | 1213 | | static string Value(decimal value) => value.ToString("N2"); |
| | 0 | 1214 | | static string Text(string? value) => System.Net.WebUtility.HtmlEncode(value ?? "—"); |
| | 0 | 1215 | | static string Date(DateTime? value) => value?.ToLocalTime().ToString("dd.MM.yyyy HH:mm") ?? "—"; |
| | | 1216 | | |
| | 0 | 1217 | | var conditionLines = new List<string>(); |
| | 0 | 1218 | | foreach (var condition in snapshot.Conditions) |
| | | 1219 | | { |
| | 0 | 1220 | | var valueLine = condition.Value.HasValue |
| | 0 | 1221 | | ? string.Format("<div>Значение условия: <b>{0}</b></div>", Value(condition.Value.Value)) |
| | 0 | 1222 | | : string.Empty; |
| | 0 | 1223 | | var actionPriceLine = condition.ActionPrice.HasValue |
| | 0 | 1224 | | ? string.Format("<div>Акционная цена: <b>{0} {1}</b></div>", Value(condition.ActionPrice.Value), Text(sn |
| | 0 | 1225 | | : string.Empty; |
| | | 1226 | | |
| | 0 | 1227 | | conditionLines.Add(string.Format( |
| | 0 | 1228 | | "<div class='promotion-tooltip-condition'><div class='promotion-tooltip-condition-title'>{0}</div><div c |
| | 0 | 1229 | | Text(condition.Title), |
| | 0 | 1230 | | Text(condition.SourceType), |
| | 0 | 1231 | | Date(condition.DateFrom), |
| | 0 | 1232 | | Date(condition.DateTo), |
| | 0 | 1233 | | valueLine, |
| | 0 | 1234 | | actionPriceLine)); |
| | | 1235 | | } |
| | | 1236 | | |
| | 0 | 1237 | | var conditions = conditionLines.Count == 0 |
| | 0 | 1238 | | ? "<div class='promotion-tooltip-empty'>Ozon подтвердил участие товара в акции.</div>" |
| | 0 | 1239 | | : string.Join(string.Empty, conditionLines); |
| | | 1240 | | |
| | 0 | 1241 | | return $@" |
| | 0 | 1242 | | <div class='promotion-tooltip-box'> |
| | 0 | 1243 | | <div class='promotion-tooltip-title'><i class='fas fa-tags'></i> Акции Ozon</div> |
| | 0 | 1244 | | <div class='promotion-tooltip-prices'> |
| | 0 | 1245 | | <div><span>Цена</span><b>{Value(snapshot.Price)} {Text(snapshot.CurrencyCode)}</b></div> |
| | 0 | 1246 | | <div><span>Цена продавца</span><b>{Value(snapshot.MarketingSellerPrice)} {Text(snapshot.CurrencyCode |
| | 0 | 1247 | | <div><span>Мин. цена</span><b>{Value(snapshot.MinPrice)} {Text(snapshot.CurrencyCode)}</b></div> |
| | 0 | 1248 | | <div><span>Старая цена</span><b>{Value(snapshot.OldPrice)} {Text(snapshot.CurrencyCode)}</b></div> |
| | 0 | 1249 | | </div> |
| | 0 | 1250 | | <div class='promotion-tooltip-stats'> |
| | 0 | 1251 | | <span>Комиссия FBS: <b>{Value(snapshot.FbsCommissionPercent)}%</b></span> |
| | 0 | 1252 | | <span>Эквайринг: <b>{Value(snapshot.Acquiring)} {Text(snapshot.CurrencyCode)}</b></span> |
| | 0 | 1253 | | <span>Индекс Ozon: <b>{snapshot.OzonPriceIndex:N2}</b></span> |
| | 0 | 1254 | | </div> |
| | 0 | 1255 | | <div class='promotion-tooltip-conditions'>{conditions}</div> |
| | 0 | 1256 | | </div>"; |
| | | 1257 | | } |
| | | 1258 | | |
| | | 1259 | | public string GetPurchasePriceTooltip(Order item) |
| | | 1260 | | { |
| | 0 | 1261 | | if (item == null) |
| | 0 | 1262 | | return "Цена закупки недоступна"; |
| | | 1263 | | |
| | 0 | 1264 | | if (item.Supplier?.CurrencyCode == CurrencyCode.RUB) |
| | 0 | 1265 | | return "Цена закупки в рублях"; |
| | | 1266 | | |
| | 0 | 1267 | | var tooltipLines = new List<string>(); |
| | 0 | 1268 | | var supplierCurrency = item.Supplier?.CurrencyCode.ToString() ?? "RUB"; |
| | 0 | 1269 | | var originalPurchasePrice = item.OriginalPurchasePrice ?? 0m; |
| | | 1270 | | |
| | 0 | 1271 | | tooltipLines.Add($"<div style='font-weight: bold; margin-bottom: 8px;'>Цена закупки: </div>"); |
| | | 1272 | | |
| | 0 | 1273 | | tooltipLines.Add($@" |
| | 0 | 1274 | | <div style='margin-left: 12px;'> |
| | 0 | 1275 | | <b>{originalPurchasePrice:N2} {supplierCurrency}</b> |
| | 0 | 1276 | | </div> |
| | 0 | 1277 | | "); |
| | | 1278 | | |
| | 0 | 1279 | | return $@" |
| | 0 | 1280 | | <div class='tooltip-box'> |
| | 0 | 1281 | | {string.Join("", tooltipLines)} |
| | 0 | 1282 | | </div>"; |
| | | 1283 | | } |
| | | 1284 | | |
| | | 1285 | | } |