< Summary

Information
Class: OzonOrdersWeb.WebServices.ArmtekEtp.SettlementSuppliersOverview
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/ArmtekEtp/SettlementSuppliersOverview.cs
Line coverage
17%
Covered lines: 9
Uncovered lines: 42
Coverable lines: 51
Total lines: 104
Line coverage: 17.6%
Branch coverage
50%
Covered branches: 18
Total branches: 36
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%11100%
get_ScheduleText()100%1818100%
GetArmtekSummaryAsync()0%342180%
GetTilesAsync()100%210%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/ArmtekEtp/SettlementSuppliersOverview.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using OzonRepositories.Context;
 3
 4namespace OzonOrdersWeb.WebServices.ArmtekEtp;
 5
 6/// <summary>Сводка взаиморасчётов одного поставщика: сальдо по последнему документу, объёмы и время обновления.</summar
 7public sealed record SettlementSupplierSummary(
 8    int SettlementsCount,
 9    int InvoicesCount,
 10    int ItemsCount,
 11    DateTime? LastUpdatedAtUtc,
 12    string? LastError,
 13    DateTime? LastErrorAtUtc,
 14    decimal? Balance,
 15    string? BalanceCurrency,
 16    DateTime? BalanceDate);
 17
 18/// <summary>Плашка поставщика на странице /Settlements.</summary>
 19public sealed record SettlementSupplierTile(
 20    string Key,
 21    string Name,
 22    string LogoUrl,
 23    string? LogoMark,
 24    string Description,
 25    string Url,
 26    SettlementSupplierSummary Summary,
 27    IReadOnlyList<(string Title, string Icon, string Url)> Links);
 28
 29/// <summary>
 30/// Поставщики раздела «Взаиморасчёты». Сейчас только Armtek (кабинет etp.armtek.ru); следующий поставщик добавляется
 31/// своей плашкой в <see cref="GetTilesAsync"/>.
 32/// </summary>
 433public sealed class SettlementSuppliersOverview(ApplicationDbContext db, IConfiguration configuration)
 34{
 35    /// <summary>Расписание автозагрузки для подписи на страницах: «каждый день в 07:00 (МСК)» или выражение cron как ес
 36    public string ScheduleText
 37    {
 38        get
 39        {
 440            if (!configuration.GetValue("ArmtekEtp:Enabled", false))
 141                return "автозагрузка выключена";
 342            var cron = configuration["ArmtekEtp:Cron"] is { Length: > 0 } c ? c : Services.ArmtekEtpServices.ArmtekEtpSy
 343            var parts = cron.Split(' ', StringSplitOptions.RemoveEmptyEntries);
 344            return parts.Length == 5 && parts[2] == "*" && parts[3] == "*" && parts[4] == "*"
 345                && int.TryParse(parts[0], out var minute) && int.TryParse(parts[1], out var hour)
 346                ? $"каждый день в {hour:00}:{minute:00} (МСК)"
 347                : $"по расписанию {cron} (МСК)";
 48        }
 49    }
 50
 51    public async Task<SettlementSupplierSummary> GetArmtekSummaryAsync(CancellationToken ct = default)
 52    {
 053        var last = await db.ArmtekSettlements.AsNoTracking()
 054            .Where(x => x.Balance != null)
 055            .OrderByDescending(x => x.DocDate).ThenByDescending(x => x.Id)
 056            .Select(x => new { x.Balance, x.Currency, x.DocDate })
 057            .FirstOrDefaultAsync(ct);
 58
 59        // «Обновлено» — время последней успешной загрузки, даже если новых документов не было.
 060        var lastSuccess = await db.ArmtekEtpSyncRuns.AsNoTracking()
 061            .Where(x => x.Success == true)
 062            .MaxAsync(x => x.FinishedAtUtc, ct);
 63        // Предупреждение — по последнему завершённому запуску: его ошибка или то, что взаиморасчёты из кабинета
 64        // не обновились (фактуры по API при этом загружены).
 065        var lastRun = await db.ArmtekEtpSyncRuns.AsNoTracking()
 066            .Where(x => x.FinishedAtUtc != null)
 067            .OrderByDescending(x => x.FinishedAtUtc)
 068            .Select(x => new { x.Success, x.Message, x.SettlementsError, x.FinishedAtUtc })
 069            .FirstOrDefaultAsync(ct);
 070        var lastFailure = lastRun == null ? null
 071            : lastRun.Success == false ? new { lastRun.Message, lastRun.FinishedAtUtc }
 072            : lastRun.SettlementsError != null ? new { Message = (string?)("Взаиморасчёты из кабинета не обновлены: " + 
 073            : null;
 74
 075        return new SettlementSupplierSummary(
 076            await db.ArmtekSettlements.CountAsync(ct),
 077            await db.ArmtekInvoices.CountAsync(ct),
 078            await db.ArmtekInvoiceItems.CountAsync(ct),
 079            lastSuccess ?? await db.ArmtekSettlements.MaxAsync(x => (DateTime?)x.UpdatedAtUtc, ct),
 080            lastFailure?.Message,
 081            lastFailure?.FinishedAtUtc,
 082            last?.Balance,
 083            last?.Currency,
 084            last?.DocDate);
 085    }
 86
 87    public async Task<IReadOnlyList<SettlementSupplierTile>> GetTilesAsync(CancellationToken ct = default)
 88    {
 89        const string armtek = "/Settlements/Armtek";
 090        return
 091        [
 092            new SettlementSupplierTile(
 093                "Armtek", "Армтек", "/images/armtek-logo.svg", "BY",
 094                "Взаиморасчёты, фактуры и позиции из личного кабинета etp.armtek.ru, автозагрузка " + ScheduleText,
 095                armtek,
 096                await GetArmtekSummaryAsync(ct),
 097                [
 098                    ("Взаиморасчёты", "fa-scale-balanced", armtek + "?tab=settlements"),
 099                    ("Фактуры", "fa-file-invoice", armtek + "?tab=invoices"),
 0100                    ("Позиции фактур", "fa-list", armtek + "?tab=items")
 0101                ])
 0102        ];
 0103    }
 104}