< Summary

Information
Class: OzonOrdersWeb.Areas.Studio2.Helpers.OneCStockGate
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Helpers/OneCStockGate.cs
Line coverage
30%
Covered lines: 13
Uncovered lines: 30
Coverable lines: 43
Total lines: 123
Line coverage: 30.2%
Branch coverage
23%
Covered branches: 9
Total branches: 38
Branch coverage: 23.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%210%
RunAsync(...)0%2040%
RunAsync()0%2040%
ReportAsync()0%7280%
TrySkipOneC(...)0%620%
GetBlockingOrderIds(...)100%11100%
PartialPostingNotice(...)66.66%6681.81%
NoDocumentNotice(...)0%620%
SkippedNotice(...)0%620%
OrdersWord(...)50%1010100%

File(s)

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

#LineLine coverage
 1using Microsoft.AspNetCore.Mvc.ViewFeatures;
 2using OzonOrdersWeb.Areas.Studio2.Models.Transactions;
 3using Services.ApiServices._1CApi.Stock;
 4
 5namespace OzonOrdersWeb.Areas.Studio2.Helpers;
 6
 7
 8/// <summary>
 9/// Единая точка вызова предварительной проверки остатков 1С из контроллеров транзакций.
 10/// Ограничивает ожидание: с ретраями на 5xx (2/4/8 с) при таймауте клиента 60 с проверка иначе
 11/// могла бы задержать создание документа на минуты.
 12/// </summary>
 13public static class OneCStockGate
 14{
 015    private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(15);
 16
 17    /// <summary>
 18    /// Сервис резолвится из HttpContext, а не через конструктор: гейт врезается в шесть контроллеров
 19    /// транзакций, у каждого из которых конструктор уже на два десятка зависимостей. В юнит-тестах,
 20    /// где сервисов нет, гейт просто ничего не проверяет.
 21    /// </summary>
 22    public static Task<StockCheckResult> RunAsync(HttpContext? http, IReadOnlyList<StockCheckLine> lines) =>
 023        RunAsync(http?.RequestServices?.GetService<IOneCStockAvailabilityService>(), lines);
 24
 25    public static async Task<StockCheckResult> RunAsync(
 26        IOneCStockAvailabilityService? service,
 27        IReadOnlyList<StockCheckLine> lines)
 28    {
 029        if (service == null || lines.Count == 0)
 030            return StockCheckResult.Empty;
 31
 032        using var cts = new CancellationTokenSource(Timeout);
 033        return await service.CheckAsync(lines, cts.Token);
 034    }
 35
 36    /// <summary>Сигнал только для фактического проведения, не для предпросмотра таблицы.</summary>
 37    public static async Task ReportAsync(HttpContext? http, StockCheckResult result, string? operation = null)
 38    {
 039        var reporter = http?.RequestServices?.GetService<IOneCStockAlertReporter>();
 040        if (reporter == null)
 041            return;
 42
 43        try
 44        {
 045            await reporter.ReportAsync(result, operation, http?.RequestAborted ?? CancellationToken.None);
 046        }
 047        catch (OperationCanceledException) when (http?.RequestAborted.IsCancellationRequested == true)
 48        {
 049            throw;
 50        }
 051        catch
 52        {
 53            // Уведомление не должно менять результат проведения документа.
 054        }
 055    }
 56
 57    /// <summary>
 58    /// Нехватка остатка отключает только проведение в 1С — статусы заказов и сама транзакция
 59    /// создаются, как при снятой галке «Провести в 1С». Возвращает true, если проведение отменено.
 60    ///
 61    /// Результат идёт в отдельный канал TempData["TransactionWarning"]: «Успешно» здесь врёт
 62    /// (документа нет), а «Ошибка операции» — тоже (статусы сменились штатно).
 63    /// </summary>
 64    public static bool TrySkipOneC(ITempDataDictionary tempData, StockCheckResult result)
 65    {
 066        if (!result.HasBlocking)
 067            return false;
 68
 069        tempData["TransactionErrorCode"] = TransactionErrorCode.OneCInsufficientStock.ToString();
 070        tempData["TransactionWarning"] = OneCStockShortageHtmlBuilder.Build(result);
 071        return true;
 72    }
 73
 74    /// <summary>
 75    /// Заказы, которые нельзя включать в документ 1С: одна нехватка может относиться к нескольким
 76    /// заказам, если потребность была агрегирована по одной карточке и складу.
 77    /// </summary>
 78    public static HashSet<int> GetBlockingOrderIds(StockCheckResult result) =>
 379        result.Blocking.SelectMany(line => line.OrderIds).ToHashSet();
 80
 81    /// <summary>
 82    /// Результат частичного проведения: доступные позиции уже ушли отдельными документами 1С,
 83    /// а дефицитные требуют ручного оприходования и повторного проведения.
 84    /// </summary>
 85    public static string PartialPostingNotice(StockCheckResult result, int postedOrdersCount, bool draftsCreated = false
 86    {
 287        if (!result.HasBlocking)
 088            return string.Empty;
 89
 290        if (draftsCreated)
 091            return OneCStockShortageHtmlBuilder.Build(result, createdAsDraft: true);
 92
 293        var prefix = postedOrdersCount > 0
 294            ? $"<div class='alert alert-warning'><strong>Частичное проведение в 1С</strong><br/>" +
 295              $"В документы 1С включены {postedOrdersCount} {OrdersWord(postedOrdersCount)} с достаточным остатком. " +
 296              "Позиции из списка ниже не включены.</div>"
 297            : "<div class='alert alert-warning'><strong>Документ 1С не создан</strong><br/>" +
 298              "Все позиции имеют нехватку остатка.</div>";
 99
 2100        return prefix + OneCStockShortageHtmlBuilder.Build(result);
 101    }
 102
 103    /// <summary>
 104    /// Заглушка «документов в 1С не требовалось» для ветки без проведения. Если проведение отменил
 105    /// гейт, заглушку не показываем: документ как раз требовался, и причина уже описана в
 106    /// предупреждении — иначе рядом висят два противоречащих друг другу сообщения.
 107    /// </summary>
 108    public static string NoDocumentNotice(StockCheckResult result) =>
 0109        result.HasBlocking
 0110            ? string.Empty
 0111            : "<div class='alert alert-info'><strong>ℹ️ Информация</strong><br/>Создание документов в 1С не требовалось<
 112
 113    /// <summary>Приписка к сообщению об успехе, когда 1С не ответила и проверку пришлось пропустить.</summary>
 114    public static string SkippedNotice(StockCheckResult result) =>
 0115        result.CheckSkipped
 0116            ? "<div class='alert alert-warning mt-2'>Проверка остатков 1С не выполнена (1С недоступна) — документ создан
 0117            : string.Empty;
 118
 119    private static string OrdersWord(int count) =>
 1120        count % 100 is >= 11 and <= 14
 1121            ? "заказов"
 1122            : count % 10 == 1 ? "заказ" : count % 10 is >= 2 and <= 4 ? "заказа" : "заказов";
 123}