< Summary

Information
Class: OzonOrdersWeb.WebServices.Orders.Grid.OrdersGridFeedPriceService
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/Orders/Grid/OrdersGridFeedPriceService.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 79
Coverable lines: 79
Total lines: 141
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 22
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
GetAsync()0%110100%
GetExplanationAsync()0%4260%
GetBatchAsync()0%4260%
NoAccess()100%210%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/Orders/Grid/OrdersGridFeedPriceService.cs

#LineLine coverage
 1using System.Security.Claims;
 2using Microsoft.EntityFrameworkCore;
 3using OzonRepositories.Context;
 4using Services.FeedPriceServices;
 5
 6namespace OzonOrdersWeb.WebServices.Orders.Grid;
 7
 8public interface IOrdersGridFeedPriceService
 9{
 10    /// <summary>
 11    /// Цена и остатки артикула заказа в фиде; null — заказ не найден или недоступен пользователю.
 12    /// <paramref name="currentOnly"/> — без истории цены (колонки грида), в ответе заполнен <c>ForOrder</c>.
 13    /// </summary>
 14    Task<FeedPriceResult?> GetAsync(int orderId, ClaimsPrincipal principal, CancellationToken cancellationToken, bool cu
 15
 16    /// <summary>
 17    /// Как получилась цена позиции заказа: расшифровка по сборкам кабинетов и выбранная по клиенту заказа.
 18    /// <paramref name="runId"/> — конкретная сборка; без него последняя сборка каждого кабинета с планом. null — заказ 
 19    /// </summary>
 20    Task<FeedOrderExplanation?> GetExplanationAsync(int orderId, ClaimsPrincipal principal, long? runId, CancellationTok
 21
 22    /// <summary>
 23    /// Строка фида для каждого заказа пачки (колонки Feed в гридах): один запрос доступа, одна выборка заказов и пачки
 24    /// ключей в feed API вместо запроса на каждый заказ. Недоступные пользователю заказы получают статус <c>noAccess</c
 25    /// </summary>
 26    Task<IReadOnlyList<FeedBatchItem>> GetBatchAsync(IReadOnlyCollection<int> orderIds, ClaimsPrincipal principal, Cance
 27}
 28
 29/// <summary>Модалка «Фид: цена и остатки»: проверяет доступ к заказу тем же RLS-запросом, что и список, и собирает ключ
 30public sealed class OrdersGridFeedPriceService : IOrdersGridFeedPriceService
 31{
 32    private readonly ApplicationDbContext _context;
 33    private readonly IOrdersGridScopedQuery _scoped;
 34    private readonly IFeedPriceService _feedPrice;
 35    private readonly IFeedExplanationService _explanation;
 36
 037    public OrdersGridFeedPriceService(ApplicationDbContext context, IOrdersGridScopedQuery scoped, IFeedPriceService fee
 038        IFeedExplanationService explanation)
 39    {
 040        _context = context;
 041        _scoped = scoped;
 042        _feedPrice = feedPrice;
 043        _explanation = explanation;
 044    }
 45
 46    public async Task<FeedPriceResult?> GetAsync(int orderId, ClaimsPrincipal principal, CancellationToken cancellationT
 47        bool currentOnly = false)
 48    {
 049        var user = await _scoped.ResolveUserAsync(principal);
 050        if (user is null) return null;
 051        var accessible = await _scoped.FilterAccessibleAsync(user, [orderId], includeArchived: true, cancellationToken);
 052        if (accessible.Count == 0) return null;
 53
 054        var order = await _context.Orders.AsNoTracking()
 055            .Where(o => o.Id == orderId)
 056            .Select(o => new
 057            {
 058                o.ProductKey,
 059                o.Article,
 060                MarketPrefix = o.EtProducer != null ? o.EtProducer.MarketPrefix : null,
 061                Client = o.OzonClient != null ? o.OzonClient.Name : null,
 062                Warehouse = o.ShipmentWarehouse != null ? o.ShipmentWarehouse.Name : null
 063            })
 064            .FirstOrDefaultAsync(cancellationToken);
 065        if (order is null) return null;
 66
 067        var keys = FeedArticleKey.Build(order.ProductKey, order.Article, order.MarketPrefix);
 068        var result = currentOnly
 069            ? await _feedPrice.GetCurrentAsync(keys, cancellationToken)
 070            : await _feedPrice.GetAsync(keys, cancellationToken);
 71
 72        // При ошибке фида строку выбирать не из чего: клиент покажет Error.
 073        return result.Error is null
 074            ? result.WithOrder(FeedOrderRowPicker.Pick(result.Current, keys, order.Client, order.Warehouse))
 075            : result;
 076    }
 77
 78    public async Task<FeedOrderExplanation?> GetExplanationAsync(int orderId, ClaimsPrincipal principal, long? runId,
 79        CancellationToken cancellationToken)
 80    {
 081        var user = await _scoped.ResolveUserAsync(principal);
 082        if (user is null) return null;
 083        var accessible = await _scoped.FilterAccessibleAsync(user, [orderId], includeArchived: true, cancellationToken);
 084        if (accessible.Count == 0) return null;
 85
 086        var order = await _context.Orders.AsNoTracking()
 087            .Where(o => o.Id == orderId)
 088            .Select(o => new
 089            {
 090                o.ProductKey,
 091                o.Article,
 092                MarketPrefix = o.EtProducer != null ? o.EtProducer.MarketPrefix : null,
 093                Client = o.OzonClient != null ? o.OzonClient.Name : null
 094            })
 095            .FirstOrDefaultAsync(cancellationToken);
 096        if (order is null) return null;
 97
 098        var keys = FeedArticleKey.Build(order.ProductKey, order.Article, order.MarketPrefix);
 099        var result = await _explanation.GetAsync(keys, runId, cancellationToken);
 0100        return FeedOrderExplanationPicker.Pick(result, keys.Count, order.Client);
 0101    }
 102
 103    public async Task<IReadOnlyList<FeedBatchItem>> GetBatchAsync(IReadOnlyCollection<int> orderIds, ClaimsPrincipal pri
 104        CancellationToken cancellationToken)
 105    {
 0106        var ids = orderIds.Distinct().ToArray();
 107        IReadOnlyList<FeedBatchItem> NoAccess(IEnumerable<int> missing) =>
 0108            missing.Select(id => new FeedBatchItem { Id = id, Status = FeedBatchItem.NoAccess }).ToArray();
 109
 0110        var user = await _scoped.ResolveUserAsync(principal);
 0111        if (user is null || ids.Length == 0) return NoAccess(ids);
 0112        var accessible = (await _scoped.FilterAccessibleAsync(user, ids, includeArchived: true, cancellationToken)).ToHa
 0113        if (accessible.Count == 0) return NoAccess(ids);
 114
 0115        var accessibleIds = accessible.ToArray();
 0116        var rows = await _context.Orders.AsNoTracking()
 0117            .Where(o => accessibleIds.Contains(o.Id))
 0118            .Select(o => new
 0119            {
 0120                o.Id,
 0121                o.ProductKey,
 0122                o.Article,
 0123                MarketPrefix = o.EtProducer != null ? o.EtProducer.MarketPrefix : null,
 0124                Client = o.OzonClient != null ? o.OzonClient.Name : null,
 0125                Warehouse = o.ShipmentWarehouse != null ? o.ShipmentWarehouse.Name : null
 0126            })
 0127            .ToListAsync(cancellationToken);
 128
 0129        var orders = rows
 0130            .Select(o => new FeedBatchOrder(o.Id, FeedArticleKey.Build(o.ProductKey, o.Article, o.MarketPrefix), o.Clien
 0131            .ToList();
 0132        var keys = orders.SelectMany(o => o.Keys).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
 0133        var feedRows = await _feedPrice.GetCurrentRowsAsync(keys, cancellationToken);
 134
 0135        var items = FeedBatchPicker.Build(orders, feedRows).ToList();
 136        // Заказы, которых нет среди доступных или найденных, отдаются как недоступные — клиент не должен ждать их беско
 0137        var found = items.Select(i => i.Id).ToHashSet();
 0138        items.AddRange(NoAccess(ids.Where(id => !found.Contains(id))));
 0139        return items;
 0140    }
 141}