< Summary

Information
Class: Services.OrderDailySummaryServices.OrderDailySummaryService
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/OrderDailySummaryServices/OrderDailySummaryService.cs
Line coverage
78%
Covered lines: 72
Uncovered lines: 20
Coverable lines: 92
Total lines: 124
Line coverage: 78.2%
Branch coverage
50%
Covered branches: 1
Total branches: 2
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)50%22100%
GetYesterdayAsync()100%11100%
GetMissingSystemStatusOrdersAsync()100%210%
MissingSystemStatusQuery(...)100%11100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/OrderDailySummaryServices/OrderDailySummaryService.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using OzonDomains.Models;
 3using OzonRepositories.Context;
 4using Services.BackgroundJobsServices;
 5
 6namespace Services.OrderDailySummaryServices;
 7
 8public sealed class OrderDailySummaryService : IOrderDailySummaryService
 9{
 110    private static readonly HashSet<string> CancelledClientStatuses = new(StringComparer.Ordinal)
 111    {
 112        "Отменён",
 113        "Отменен во время доставки",
 114        "Отменен во время обработки",
 115        "Отменен до начала обработки",
 116        "Отменен покупателем",
 117        "Отменен при доставке",
 118        "Отменен при обработке",
 119        "Отменен продавцом",
 120        "Отменен. Продавец отменил заказ после подтверждения.",
 121        "Заказ отменен до обработки",
 122        "Заказ отменен покупателем",
 123        "Заказ отменен продавцом"
 124    };
 25
 126    private static readonly HashSet<string> DeliveredClientStatuses = new(StringComparer.Ordinal)
 127    {
 128        "Доставлен",
 129        "Доставлен в пункт выдачи",
 130        "Доставлен в пункт самовывоза",
 131        "Доставлен на пункт выдачи",
 132        "Доставлен покупателю",
 133        "Доставлено",
 134        "Заказ выполнен",
 135        "Заказ получен",
 136        "Получен покупателем",
 137        "Получено",
 138        "Условно доставлено"
 139    };
 40
 41    private readonly ApplicationDbContext _db;
 42    private readonly TimeProvider _timeProvider;
 43
 344    public OrderDailySummaryService(ApplicationDbContext db, TimeProvider? timeProvider = null)
 45    {
 346        _db = db;
 347        _timeProvider = timeProvider ?? TimeProvider.System;
 348    }
 49
 50    public async Task<OrderDailySummary> GetYesterdayAsync(CancellationToken cancellationToken = default)
 51    {
 352        var nowUtc = _timeProvider.GetUtcNow().UtcDateTime;
 353        var localNow = TimeZoneInfo.ConvertTimeFromUtc(nowUtc, RussianTimeZoneHelper.Zone);
 354        var localStart = localNow.Date.AddDays(-1);
 355        var localEnd = localStart.AddDays(1);
 56
 357        var acceptedForProcessingCount = await _db.Orders.AsNoTracking()
 358            .Where(x => x.ProcessingDate >= localStart && x.ProcessingDate < localEnd)
 359            .Select(x => x.Id)
 360            .Distinct()
 361            .CountAsync(cancellationToken);
 62
 63        // Order.LastStatusChangeDate обновляется не во всех путях смены ClientStatusId
 64        // (например, SaveClientStatusUpdatesAsync делает ExecuteUpdateAsync только по ClientStatusId),
 65        // поэтому окно "за сутки" строим только по ProcessingDate — как и для acceptedForProcessingCount.
 366        var processedYesterday = _db.Orders.AsNoTracking()
 367            .Where(x => x.ClientStatus != null && x.ProcessingDate >= localStart && x.ProcessingDate < localEnd);
 368        var cancelledCount = await processedYesterday
 369            .Where(x => CancelledClientStatuses.Contains(x.ClientStatus!.Name))
 370            .Select(x => x.Id)
 371            .Distinct()
 372            .CountAsync(cancellationToken);
 373        var deliveredCount = await processedYesterday
 374            .Where(x => DeliveredClientStatuses.Contains(x.ClientStatus!.Name))
 375            .Select(x => x.Id)
 376            .Distinct()
 377            .CountAsync(cancellationToken);
 378        var activeOrderControlTasksCount = await _db.OrderControlTasks.AsNoTracking()
 379            .CountAsync(x => OrderControlTaskStatus.Active.Contains(x.Status), cancellationToken);
 380        var overdueOrderControlTasksCount = await _db.OrderControlTasks.AsNoTracking()
 381            .CountAsync(x => x.Status == OrderControlTaskStatus.Overdue, cancellationToken);
 382        var missingSystemStatusCount = await MissingSystemStatusQuery(localEnd)
 383            .CountAsync(cancellationToken);
 84
 385        return new OrderDailySummary(
 386            DateOnly.FromDateTime(localStart),
 387            acceptedForProcessingCount,
 388            cancelledCount,
 389            deliveredCount,
 390            missingSystemStatusCount,
 391            activeOrderControlTasksCount,
 392            overdueOrderControlTasksCount);
 393    }
 94
 95    public async Task<IReadOnlyList<MissingSystemStatusOrder>> GetMissingSystemStatusOrdersAsync(
 96        int take = 200,
 97        CancellationToken cancellationToken = default)
 98    {
 099        var localNow = TimeZoneInfo.ConvertTimeFromUtc(_timeProvider.GetUtcNow().UtcDateTime, RussianTimeZoneHelper.Zone
 0100        var endOfYesterday = localNow.Date;
 0101        take = Math.Clamp(take, 1, 500);
 102
 0103        return await MissingSystemStatusQuery(endOfYesterday)
 0104            .OrderBy(x => x.ProcessingDate)
 0105            .Select(x => new MissingSystemStatusOrder(
 0106                x.Id,
 0107                x.ShipmentNumber ?? $"Заказ #{x.Id}",
 0108                x.ProductName,
 0109                x.Article,
 0110                x.EtProducer != null ? x.EtProducer.Name : x.Manufacturer != null ? x.Manufacturer.Name : null,
 0111                x.Supplier != null ? x.Supplier.Name : null,
 0112                x.ClientStatus != null ? x.ClientStatus.Name : null,
 0113                x.ClientStatus != null && x.ClientStatus.StatusColor != null ? x.ClientStatus.StatusColor.ColorCode : nu
 0114                x.ProcessingDate,
 0115                x.ProductInfo != null ? x.ProductInfo.ImageUrl : null,
 0116                x.OzonClient != null ? x.OzonClient.Name : null))
 0117            .Take(take)
 0118            .ToListAsync(cancellationToken);
 0119    }
 120
 3121    private IQueryable<Order> MissingSystemStatusQuery(DateTime beforeProcessingDate) => _db.Orders.AsNoTracking()
 3122        .Where(x => x.ProcessingDate < beforeProcessingDate &&
 3123                    (x.AppStatusId == null || (x.AppStatus != null && x.AppStatus.Name == "Не указан")));
 124}