< Summary

Information
Class: Services.SupplierStatusServices.SupplierVerificationStatus
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/SupplierStatusServices/SupplierOrderVerification.cs
Line coverage
10%
Covered lines: 1
Uncovered lines: 9
Coverable lines: 10
Total lines: 172
Line coverage: 10%
Branch coverage
37%
Covered branches: 6
Total branches: 16
Branch coverage: 37.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
IsProblem(...)100%66100%
Title(...)0%110100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/SupplierStatusServices/SupplierOrderVerification.cs

#LineLine coverage
 1using System.Text.Encodings.Web;
 2using System.Text.Json;
 3using System.Text.Unicode;
 4
 5namespace Services.SupplierStatusServices;
 6
 7/// <summary>Итог независимой проверки заказа у API-поставщика после проведения транзакции.</summary>
 8public static class SupplierVerificationStatus
 9{
 10    /// <summary>Поставщик подтвердил позицию/заказ повторным запросом в реальный API.</summary>
 11    public const string Confirmed = "Confirmed";
 12
 13    /// <summary>Поставщик ответил, но данные не сошлись (нет номера, отказ по количеству, другой номер).</summary>
 14    public const string Mismatch = "Mismatch";
 15
 16    /// <summary>Поставщик не вернул позицию/заказ или позиции нет в его корзине.</summary>
 17    public const string NotFound = "NotFound";
 18
 19    /// <summary>API недоступен или вернул ошибку — проверить не удалось.</summary>
 20    public const string ApiError = "ApiError";
 21
 22    /// <summary>Проверить нельзя: у поставщика ещё ничего нет (локальная корзина) либо нет провайдера чтения.</summary>
 23    public const string NotVerifiable = "NotVerifiable";
 24
 2025    public static bool IsProblem(string status) => status is Mismatch or NotFound or ApiError;
 26
 027    public static string Title(string status) => status switch
 028    {
 029        Confirmed => "Подтверждено у поставщика",
 030        Mismatch => "Расхождение с поставщиком",
 031        NotFound => "Не найдено у поставщика",
 032        ApiError => "Поставщик не ответил",
 033        NotVerifiable => "Проверка невозможна",
 034        _ => status
 035    };
 36}
 37
 38/// <summary>Результат проверки одного заказа Studio2 у одного поставщика.</summary>
 39public sealed record SupplierVerificationRow(
 40    int OrderId,
 41    string? ShipmentNumber,
 42    string? Article,
 43    string? ProductName,
 44    string ProviderKey,
 45    string ProviderName,
 46    string Stage,
 47    string Status,
 48    string Summary,
 49    string? SupplierOrderNumber,
 50    string? SupplierStatusName,
 51    IReadOnlyList<string> Checks,
 52    DateTime VerifiedAt)
 53{
 54    public bool IsProblem => SupplierVerificationStatus.IsProblem(Status);
 55}
 56
 57/// <summary>Сводка проверки по всему проведению.</summary>
 58public sealed record SupplierVerificationSummary(IReadOnlyList<SupplierVerificationRow> Rows)
 59{
 60    public static readonly SupplierVerificationSummary Empty = new(Array.Empty<SupplierVerificationRow>());
 61
 62    public bool IsEmpty => Rows.Count == 0;
 63
 64    public int ConfirmedCount => Rows.Count(x => x.Status == SupplierVerificationStatus.Confirmed);
 65
 66    public int ProblemCount => Rows.Count(x => x.IsProblem);
 67
 68    public int NotVerifiableCount => Rows.Count(x => x.Status == SupplierVerificationStatus.NotVerifiable);
 69
 70    public bool HasProblems => ProblemCount > 0;
 71
 72    /// <summary>Короткая строка для toast: «Поставщики: подтверждено 3, проблем 1».</summary>
 73    public string ToShortText()
 74    {
 75        if (IsEmpty) return string.Empty;
 76        var parts = new List<string> { $"подтверждено {ConfirmedCount}" };
 77        if (ProblemCount > 0) parts.Add($"проблем {ProblemCount}");
 78        if (NotVerifiableCount > 0) parts.Add($"не проверяется {NotVerifiableCount}");
 79        return "Проверка у поставщиков: " + string.Join(", ", parts);
 80    }
 81}
 82
 83/// <summary>
 84/// Запись в orders_history о проверке заказа у поставщика. Пишется системой (ChangedBy = «System: …»),
 85/// JSON лежит в NewValue. Поля после <see cref="Summary"/> обязаны оставаться nullable со значением по умолчанию:
 86/// уже записанные строки не должны ломаться при расширении формата.
 87/// </summary>
 88public sealed record SupplierVerificationHistoryEntry(
 89    string ProviderKey,
 90    string SupplierDisplayName,
 91    string Stage,
 92    string Status,
 93    string Summary,
 94    string? SupplierOrderNumber = null,
 95    string? SupplierStatusName = null,
 96    IReadOnlyList<string>? Checks = null,
 97    string? ShipmentNumber = null,
 98    DateTime? VerifiedAt = null)
 99{
 100    /// <summary>Маркер в orders_history.ColumnName — по нему таймлайн истории рисует проверку поставщика.
 101    /// Rollback игнорирует нераспознанные ColumnName, поэтому откат такие записи не затрагивает.</summary>
 102    public const string HistoryColumnName = "SupplierOrderVerification";
 103
 104    /// <summary>Автор записи: содержит «system», поэтому история показывает её как системную.</summary>
 105    public const string SystemAuthor = "System: проверка поставщика";
 106
 107    // По умолчанию System.Text.Json экранирует кириллицу как \uXXXX — в БД должен лежать читаемый текст.
 108    public static readonly JsonSerializerOptions JsonOptions = new()
 109    {
 110        Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
 111    };
 112
 113    private static readonly JsonSerializerOptions ReadOptions = new() { PropertyNameCaseInsensitive = true };
 114
 115    public static SupplierVerificationHistoryEntry FromRow(SupplierVerificationRow row) => new(
 116        row.ProviderKey, row.ProviderName, row.Stage, row.Status, row.Summary, row.SupplierOrderNumber,
 117        row.SupplierStatusName, row.Checks, row.ShipmentNumber, row.VerifiedAt);
 118
 119    public string ToJson() => JsonSerializer.Serialize(this, JsonOptions);
 120
 121    public static SupplierVerificationHistoryEntry? TryParse(string? json)
 122    {
 123        if (string.IsNullOrWhiteSpace(json)) return null;
 124        try
 125        {
 126            return JsonSerializer.Deserialize<SupplierVerificationHistoryEntry>(json, ReadOptions);
 127        }
 128        catch (JsonException)
 129        {
 130            return null;
 131        }
 132    }
 133
 134    /// <summary>Плоский текст для поиска по истории заказа.</summary>
 135    public string BuildFilterText() => string.Join(" ", new[]
 136        {
 137            SupplierDisplayName, SupplierVerificationStatus.Title(Status), Summary, SupplierOrderNumber,
 138            SupplierStatusName, ShipmentNumber
 139        }
 140        .Where(x => !string.IsNullOrWhiteSpace(x)));
 141}
 142
 143/// <summary>Русские названия провайдеров для сводки и истории.</summary>
 144public static class SupplierProviderNames
 145{
 146    public static string Describe(string providerKey) => providerKey switch
 147    {
 148        "Armtek" => "Армтек RU",
 149        "ArmtekBy" => "Армтек РБ",
 150        "Avd" => "АВД",
 151        "MlAuto" => "ML-Auto РБ",
 152        "MlAutoRu" => "ML-Auto RU",
 153        "ForumAuto" => "Форум-Авто (Союз)",
 154        "ForumAutoInterparts" => "Форум-Авто (Интерпартс)",
 155        "ForumAutoPiter" => "Форум-Авто (Питер)",
 156        "ForumAutoRostov" => "Форум-Авто (Ростов)",
 157        "ForumAutoIstra" => "Форум-Авто (Истра)",
 158        "FavoritParts" => "Фаворит Союз",
 159        "FavoritPartsIstra" => "Фаворит Истра",
 160        "FavoritPartsRostov" => "Фаворит Ростов",
 161        "ProfitLiga" => "Профит-Лига",
 162        "ZZapMoscow" => "ZZap Москва",
 163        "Berg" => "Берг",
 164        "Japarts" => "Japarts",
 165        "Motex" => "МоТехС",
 166        "MoskvorechieIstra" => "Москворечье Истра",
 167        "ShateM" => "Шате-М",
 168        "MikadoMskHod20" => "Микадо МскХод20",
 169        "Nikei" => "Nikei",
 170        _ => string.IsNullOrWhiteSpace(providerKey) ? "API-поставщик" : providerKey
 171    };
 172}