< Summary

Information
Class: Services.StockSyncServices.MatchResult<T>
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/StockSyncServices/WarehouseStockLookupMatcher.cs
Line coverage
100%
Covered lines: 2
Uncovered lines: 0
Coverable lines: 2
Total lines: 59
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Item()100%11100%
get_ManufacturerMatched()100%11100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/StockSyncServices/WarehouseStockLookupMatcher.cs

#LineLine coverage
 1namespace Services.StockSyncServices;
 2
 3public sealed class MatchResult<T> where T : class
 4{
 305    public T Item { get; init; } = null!;
 196    public bool ManufacturerMatched { get; init; }
 7}
 8
 9/// <summary>
 10/// Сопоставление артикула заказа со строкой WarehouseStock (exact → prefix).
 11/// </summary>
 12public static class WarehouseStockLookupMatcher
 13{
 14    public static string Normalize(string? s) =>
 15        string.IsNullOrEmpty(s) ? "" :
 16        new string(s.Where(char.IsLetterOrDigit).ToArray()).ToUpperInvariant();
 17
 18    public static MatchResult<T>? FindMatch<T>(
 19        IEnumerable<T> candidates,
 20        string normalizedArticle,
 21        string normalizedManufacturer,
 22        Func<T, string?> getArticle,
 23        Func<T, string?> getManufacturer) where T : class
 24    {
 25        var list = candidates.ToList();
 26        if (list.Count == 0 || string.IsNullOrEmpty(normalizedArticle))
 27            return null;
 28
 29        bool ManufacturerMatches(T item) =>
 30            string.IsNullOrEmpty(normalizedManufacturer) ||
 31            Normalize(getManufacturer(item)) == normalizedManufacturer;
 32
 33        var scoped = list.Where(ManufacturerMatches).ToList();
 34        var manufacturerMatched = scoped.Count > 0;
 35        if (scoped.Count == 0)
 36            scoped = list;
 37        if (scoped.Count == 0)
 38            return null;
 39
 40        var exact = scoped.FirstOrDefault(x => Normalize(getArticle(x)) == normalizedArticle);
 41        if (exact != null)
 42            return new MatchResult<T> { Item = exact, ManufacturerMatched = manufacturerMatched };
 43
 44        var prefixMatches = scoped
 45            .Select(x => (Item: x, Norm: Normalize(getArticle(x))))
 46            .Where(t => t.Norm.StartsWith(normalizedArticle, StringComparison.Ordinal) ||
 47                        normalizedArticle.StartsWith(t.Norm, StringComparison.Ordinal))
 48            .OrderBy(t => t.Norm.StartsWith(normalizedArticle, StringComparison.Ordinal) ? 0 : 1)
 49            .ThenBy(t => Math.Abs(t.Norm.Length - normalizedArticle.Length))
 50            .ThenBy(t => t.Norm.Length)
 51            .Select(t => t.Item)
 52            .ToList();
 53
 54        if (prefixMatches.Count == 1)
 55            return new MatchResult<T> { Item = prefixMatches[0], ManufacturerMatched = manufacturerMatched };
 56
 57        return null;
 58    }
 59}