< Summary

Information
Class: Services.ApiServices._1CApi.Nomenclature.ProducerValue
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/ApiServices/1CApi/Nomenclature/ProducerValueResolver.cs
Line coverage
100%
Covered lines: 1
Uncovered lines: 0
Coverable lines: 1
Total lines: 91
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_RefKey()100%11100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/ApiServices/1CApi/Nomenclature/ProducerValueResolver.cs

#LineLine coverage
 1using OzonDomains.Nomenclature;
 2using Services.ApiServices._1CApi.OData;
 3using Services.ApiServices._1CApi.OData.Models;
 4
 5namespace Services.ApiServices._1CApi.Nomenclature;
 6
 7/// <summary>Значение реквизита «Производитель» в 1С.</summary>
 1538public sealed record ProducerValue(Guid RefKey, string Name);
 9
 10/// <summary>
 11/// Подбирает существующее значение реквизита «Производитель» для группы бренда, чтобы новая карточка получила его,
 12/// а не отдельное значение с другим написанием (Febi рядом с FEBI BILSTEIN, KYB рядом с Kayaba — так появлялись дубли).
 13/// Значения читаются из 1С один раз в час.
 14/// </summary>
 15public sealed class ProducerValueResolver(IOneCODataClient client, IProducerDirectoryProvider directoryProvider, TimeSpa
 16{
 17    private readonly TimeSpan _ttl = ttl ?? TimeSpan.FromHours(1);
 18    private readonly SemaphoreSlim _gate = new(1, 1);
 19    private IReadOnlyList<ProducerValue>? _values;
 20    private DateTime _expiresUtc = DateTime.MinValue;
 21
 22    /// <param name="groups">Группы бренда заказа.</param>
 23    /// <param name="usageByProducerNorm">Сколько карточек у каждого написания (ключ — <c>ArticleKey.Key(имя)</c>): берё
 24    /// <param name="preferredName">Имя из заказа — решает ничью.</param>
 25    /// <returns>null — в 1С нет значения этой группы (нужно создавать новое прежним способом).</returns>
 26    public async Task<ProducerValue?> FindForGroupsAsync(
 27        IReadOnlySet<string> groups,
 28        IReadOnlyDictionary<string, int>? usageByProducerNorm = null,
 29        string? preferredName = null,
 30        CancellationToken cancellationToken = default)
 31    {
 32        if (groups.Count == 0)
 33            return null;
 34
 35        var directory = await directoryProvider.GetAsync(cancellationToken);
 36        var values = await GetValuesAsync(cancellationToken);
 37        var preferredNorm = ArticleKey.Key(preferredName);
 38
 39        return values
 40            .Where(v => directory.GroupsOfName(v.Name).Overlaps(groups))
 41            .OrderByDescending(v => usageByProducerNorm != null && usageByProducerNorm.TryGetValue(ArticleKey.Key(v.Name
 42            .ThenByDescending(v => preferredNorm.Length > 0 && ArticleKey.Key(v.Name) == preferredNorm)
 43            .ThenBy(v => v.Name, StringComparer.OrdinalIgnoreCase)
 44            .FirstOrDefault();
 45    }
 46
 47    /// <summary>Значение по имени (для ручной связи брендов): точное совпадение ключа имени.</summary>
 48    public async Task<ProducerValue?> FindByNameAsync(string? name, CancellationToken cancellationToken = default)
 49    {
 50        var norm = ArticleKey.Key(name);
 51        if (norm.Length == 0)
 52            return null;
 53
 54        var values = await GetValuesAsync(cancellationToken);
 55        return values.FirstOrDefault(v => ArticleKey.Key(v.Name) == norm);
 56    }
 57
 58    public void Invalidate() => _expiresUtc = DateTime.MinValue;
 59
 60    private async Task<IReadOnlyList<ProducerValue>> GetValuesAsync(CancellationToken cancellationToken)
 61    {
 62        var current = _values;
 63        if (current != null && DateTime.UtcNow < _expiresUtc)
 64            return current;
 65
 66        await _gate.WaitAsync(cancellationToken);
 67        try
 68        {
 69            if (_values != null && DateTime.UtcNow < _expiresUtc)
 70                return _values;
 71
 72            var raw = await client.GetListAsync<ODataPropertyValue>(OneCNomenclatureService.PropertyValuesEndpoint,
 73                new Dictionary<string, string>
 74                {
 75                    ["$select"] = "Ref_Key,Description,Owner_Key,DeletionMark",
 76                    ["$filter"] = $"Owner_Key eq guid'{OneCNomenclatureService.ProducerPropertyKey}' and DeletionMark eq
 77                }, cancellationToken);
 78
 79            _values = raw
 80                .Where(v => v.RefKey != Guid.Empty && !string.IsNullOrWhiteSpace(v.Description))
 81                .Select(v => new ProducerValue(v.RefKey, v.Description!.Trim()))
 82                .ToList();
 83            _expiresUtc = DateTime.UtcNow + _ttl;
 84            return _values;
 85        }
 86        finally
 87        {
 88            _gate.Release();
 89        }
 90    }
 91}

Methods/Properties

get_RefKey()