< Summary

Information
Class: Services.ApiServices._1CApi.OneCReceiptWarehouseHistoryResolver
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/ApiServices/1CApi/OneCReceiptWarehouseHistoryResolver.cs
Line coverage
24%
Covered lines: 11
Uncovered lines: 34
Coverable lines: 45
Total lines: 81
Line coverage: 24.4%
Branch coverage
50%
Covered branches: 4
Total branches: 8
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
ResolveByArticlesAsync()100%210%
<ResolveByArticlesAsync()0%2040%
SelectLatestPostedReceipts(...)100%44100%

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>
 09public sealed class OneCReceiptWarehouseHistoryResolver(
 010    OneCDataManager oneCDataManager,
 011    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    {
 019        var normalizedArticles = articles
 20            .Where(article => !string.IsNullOrWhiteSpace(article))
 21            .Select(article => article!.Trim())
 022            .Distinct(StringComparer.OrdinalIgnoreCase)
 023            .ToList();
 024        var allowedWarehouses = new HashSet<string>(
 25            availableWarehouses.Where(warehouse => !string.IsNullOrWhiteSpace(warehouse)).Select(warehouse => warehouse!
 026            StringComparer.OrdinalIgnoreCase);
 027        var result = new Dictionary<string, IReadOnlyList<OneCReceiptWarehouseSuggestion>>(StringComparer.OrdinalIgnoreC
 28
 029        using var semaphore = new SemaphoreSlim(MaxConcurrentRequests);
 030        var tasks = normalizedArticles.Select(async article =>
 031        {
 032            await semaphore.WaitAsync();
 033            try
 034            {
 035                var movementMap = await oneCDataManager.GetMovementMapAsync(article);
 036                var suggestions = SelectLatestPostedReceipts(movementMap?.Movements ?? [], allowedWarehouses);
 037                lock (result)
 038                    result[article] = suggestions;
 039            }
 040            catch (Exception ex)
 041            {
 042                logger.LogWarning(ex, "[ReturnToSupplier] Не удалось получить историю приходов 1С для {Article}", articl
 043                lock (result)
 044                    result[article] = [];
 045            }
 046            finally
 047            {
 048                semaphore.Release();
 049            }
 050        });
 51
 052        await Task.WhenAll(tasks);
 053        return result;
 054    }
 55
 56    /// <summary>
 57    /// Оставляет по одному самому свежему проведённому приходу для каждого доступного склада 1С.
 58    /// </summary>
 59    public static IReadOnlyList<OneCReceiptWarehouseSuggestion> SelectLatestPostedReceipts(
 60        IEnumerable<MovementItem> movements,
 61        ISet<string> availableWarehouses)
 62    {
 563        return movements
 1064            .Where(movement => movement.Kind == MovementKind.Receipt && movement.IsPosted && !movement.IsDeleted)
 765            .Where(movement => !string.IsNullOrWhiteSpace(movement.WarehouseTo))
 766            .Where(movement => availableWarehouses.Contains(movement.WarehouseTo!))
 667            .OrderByDescending(movement => movement.Date)
 668            .ThenByDescending(movement => movement.DocNumber, StringComparer.OrdinalIgnoreCase)
 669            .GroupBy(movement => movement.WarehouseTo!, StringComparer.OrdinalIgnoreCase)
 570            .Select(group => group.First())
 571            .Select(movement => new OneCReceiptWarehouseSuggestion(
 572                movement.WarehouseTo!, movement.Date, movement.DocNumber, movement.RefKey))
 573            .ToList();
 74    }
 75}
 76
 77public sealed record OneCReceiptWarehouseSuggestion(
 78    string Warehouse,
 79    DateTime Date,
 80    string DocumentNumber,
 81    Guid DocumentRefKey);