< Summary

Information
Class: Services.ApiServices._1CApi.OneCReceiptWarehouseSuggestion
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/ApiServices/1CApi/OneCReceiptWarehouseHistoryResolver.cs
Line coverage
80%
Covered lines: 4
Uncovered lines: 1
Coverable lines: 5
Total lines: 81
Line coverage: 80%
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
.ctor(...)100%11100%
get_Warehouse()100%11100%
get_Date()100%210%
get_DocumentNumber()100%11100%
get_DocumentRefKey()100%11100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/ApiServices/1CApi/OneCReceiptWarehouseHistoryResolver.cs

#LineLine coverage
 1using Microsoft.Extensions.Logging;
 2using Services.ApiServices._1CApi.Models;
 3
 4namespace Services.ApiServices._1CApi;
 5
 6/// <summary>
 7/// Подбирает склады-получатели для возврата поставщику по последним проведённым приходам 1С.
 8/// </summary>
 9public sealed class OneCReceiptWarehouseHistoryResolver(
 10    OneCDataManager oneCDataManager,
 11    ILogger<OneCReceiptWarehouseHistoryResolver> logger)
 12{
 13    private const int MaxConcurrentRequests = 4;
 14
 15    public async Task<IReadOnlyDictionary<string, IReadOnlyList<OneCReceiptWarehouseSuggestion>>> ResolveByArticlesAsync
 16        IEnumerable<string?> articles,
 17        IEnumerable<string?> availableWarehouses)
 18    {
 19        var normalizedArticles = articles
 20            .Where(article => !string.IsNullOrWhiteSpace(article))
 21            .Select(article => article!.Trim())
 22            .Distinct(StringComparer.OrdinalIgnoreCase)
 23            .ToList();
 24        var allowedWarehouses = new HashSet<string>(
 25            availableWarehouses.Where(warehouse => !string.IsNullOrWhiteSpace(warehouse)).Select(warehouse => warehouse!
 26            StringComparer.OrdinalIgnoreCase);
 27        var result = new Dictionary<string, IReadOnlyList<OneCReceiptWarehouseSuggestion>>(StringComparer.OrdinalIgnoreC
 28
 29        using var semaphore = new SemaphoreSlim(MaxConcurrentRequests);
 30        var tasks = normalizedArticles.Select(async article =>
 31        {
 32            await semaphore.WaitAsync();
 33            try
 34            {
 35                var movementMap = await oneCDataManager.GetMovementMapAsync(article);
 36                var suggestions = SelectLatestPostedReceipts(movementMap?.Movements ?? [], allowedWarehouses);
 37                lock (result)
 38                    result[article] = suggestions;
 39            }
 40            catch (Exception ex)
 41            {
 42                logger.LogWarning(ex, "[ReturnToSupplier] Не удалось получить историю приходов 1С для {Article}", articl
 43                lock (result)
 44                    result[article] = [];
 45            }
 46            finally
 47            {
 48                semaphore.Release();
 49            }
 50        });
 51
 52        await Task.WhenAll(tasks);
 53        return result;
 54    }
 55
 56    /// <summary>
 57    /// Оставляет по одному самому свежему проведённому приходу для каждого доступного склада 1С.
 58    /// </summary>
 59    public static IReadOnlyList<OneCReceiptWarehouseSuggestion> SelectLatestPostedReceipts(
 60        IEnumerable<MovementItem> movements,
 61        ISet<string> availableWarehouses)
 62    {
 63        return movements
 64            .Where(movement => movement.Kind == MovementKind.Receipt && movement.IsPosted && !movement.IsDeleted)
 65            .Where(movement => !string.IsNullOrWhiteSpace(movement.WarehouseTo))
 66            .Where(movement => availableWarehouses.Contains(movement.WarehouseTo!))
 67            .OrderByDescending(movement => movement.Date)
 68            .ThenByDescending(movement => movement.DocNumber, StringComparer.OrdinalIgnoreCase)
 69            .GroupBy(movement => movement.WarehouseTo!, StringComparer.OrdinalIgnoreCase)
 70            .Select(group => group.First())
 71            .Select(movement => new OneCReceiptWarehouseSuggestion(
 72                movement.WarehouseTo!, movement.Date, movement.DocNumber, movement.RefKey))
 73            .ToList();
 74    }
 75}
 76
 577public sealed record OneCReceiptWarehouseSuggestion(
 478    string Warehouse,
 079    DateTime Date,
 380    string DocumentNumber,
 581    Guid DocumentRefKey);