< Summary

Information
Class: OzonOrdersWeb.Areas.Studio2.Helpers.OneCStockShortageHtmlBuilder
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Helpers/OneCStockShortageHtmlBuilder.cs
Line coverage
100%
Covered lines: 56
Uncovered lines: 0
Coverable lines: 56
Total lines: 104
Line coverage: 100%
Branch coverage
96%
Covered branches: 31
Total branches: 32
Branch coverage: 96.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Build(...)100%2222100%
PositionsWord(...)100%66100%
H(...)50%22100%
Format(...)100%22100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Helpers/OneCStockShortageHtmlBuilder.cs

#LineLine coverage
 1using System.Globalization;
 2using System.Net;
 3using System.Text;
 4using Services.ApiServices._1CApi.Stock;
 5
 6namespace OzonOrdersWeb.Areas.Studio2.Helpers;
 7
 8/// <summary>
 9/// Панель «не хватает остатка в 1С» для TempData["ErorrResult"].
 10/// Разметка и классы те же, что у предпросмотра на странице транзакции
 11/// (_OneCStockCheckScript.cshtml), стили — .tx-stock-gap* в TransactionsTable.css.
 12/// </summary>
 13public static class OneCStockShortageHtmlBuilder
 14{
 15    private const int MaxRows = 30;
 16
 17    /// <param name="statusesChanged">
 18    /// true — транзакция уже проведена по статусам и пропущено только создание документа 1С
 19    /// (серверный сценарий); false — предупреждение показывается до отправки формы.
 20    /// </param>
 21    public static string Build(StockCheckResult? result, bool statusesChanged = true, bool createdAsDraft = false)
 22    {
 1623        var shortages = result?.Blocking;
 1624        if (shortages == null || shortages.Count == 0)
 225            return string.Empty;
 26
 1427        var ordered = shortages
 7928            .OrderBy(s => s.WarehouseName, StringComparer.OrdinalIgnoreCase)
 7929            .ThenBy(s => s.Article, StringComparer.OrdinalIgnoreCase)
 1430            .ToList();
 31
 1432        var sb = new StringBuilder();
 1433        sb.Append("<div class='tx-stock-gap'>");
 34
 1435        var headline = createdAsDraft
 1436            ? $"По {ordered.Count} {PositionsWord(ordered.Count)} создан черновик 1С — проведение требует оприходования"
 1437            : statusesChanged
 1438            ? $"Статусы заказов изменены, но документ в 1С не создан — не хватает остатка по {ordered.Count} {PositionsW
 1439            : $"Не хватает остатка в 1С по {ordered.Count} {PositionsWord(ordered.Count)} — документ не будет создан";
 40
 1441        sb.Append("<div class='tx-stock-gap-head'>");
 1442        sb.Append("<i class='fas fa-triangle-exclamation'></i>");
 1443        sb.Append($"<span>{headline}</span>");
 1444        sb.Append("</div>");
 45
 1446        if (createdAsDraft)
 147            sb.Append("<details class='tx-stock-gap-details'><summary>Позиции с нехваткой</summary>");
 48
 1449        sb.Append("<div class='tx-stock-gap-table-wrap'><table class='tx-stock-gap-table'>");
 1450        sb.Append("<thead><tr>");
 1451        sb.Append("<th>Артикул</th><th>Наименование</th><th>Склад</th>");
 1452        sb.Append("<th class='tx-stock-gap-num'>Нужно</th><th class='tx-stock-gap-num'>Остаток</th>");
 1453        sb.Append("</tr></thead><tbody>");
 54
 17655        foreach (var row in ordered.Take(MaxRows))
 56        {
 7457            sb.Append("<tr>");
 7458            sb.Append($"<td class='tx-stock-gap-article'>{H(row.Article)}</td>");
 7459            sb.Append($"<td class='tx-stock-gap-name'>{H(row.ProductName)}</td>");
 7460            sb.Append($"<td><span class='tx-stock-gap-wh'>{H(row.WarehouseName)}</span></td>");
 7461            sb.Append($"<td class='tx-stock-gap-num'>{Format(row.Required)}</td>");
 7462            sb.Append($"<td class='tx-stock-gap-num tx-stock-gap-zero'>{Format(row.Available)}</td>");
 7463            sb.Append("</tr>");
 64        }
 65
 1466        sb.Append("</tbody></table></div>");
 67
 1468        var hidden = ordered.Count - MaxRows;
 1469        if (hidden > 0)
 170            sb.Append($"<div class='tx-stock-gap-hint'>…и ещё {hidden} {PositionsWord(hidden)}</div>");
 71
 1472        sb.Append("<div class='tx-stock-gap-hint'>");
 1473        sb.Append(createdAsDraft
 1474            ? "Черновик содержит позиции с нехваткой. Проведите приход в 1С, затем откройте и проведите документ вручную
 1475            : statusesChanged
 1476            ? "Проведите приход в 1С и создайте документ вручную либо повторите транзакцию после оприходования."
 1477            : "Уменьшите количество, смените склад отгрузки или проведите приход в 1С.");
 1478        sb.Append("</div>");
 79
 1480        if (createdAsDraft)
 181            sb.Append("</details>");
 82
 1483        sb.Append("</div>");
 1484        return sb.ToString();
 85    }
 86
 87    private static string PositionsWord(int count)
 88    {
 1589        var lastTwo = count % 100;
 1590        if (lastTwo is >= 11 and <= 14)
 191            return "позициям";
 92
 1493        return count % 10 == 1 ? "позиции" : "позициям";
 94    }
 95
 22296    private static string H(string? value) => WebUtility.HtmlEncode(value ?? string.Empty);
 97
 98    // Инвариантная культура: в ru-RU дробный разделитель — запятая, а число попадает в HTML-таблицу
 99    // рядом с числами из 1С, где разделитель точка.
 100    private static string Format(decimal value) =>
 148101        value == decimal.Truncate(value)
 148102            ? ((long)value).ToString(CultureInfo.InvariantCulture)
 148103            : value.ToString("0.###", CultureInfo.InvariantCulture);
 104}