< Summary

Information
Class: OzonOrdersWeb.WebServices.Transactions.ArmtekReceiptOriginService
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/Transactions/ArmtekReceiptOriginService.cs
Line coverage
27%
Covered lines: 14
Uncovered lines: 37
Coverable lines: 51
Total lines: 91
Line coverage: 27.4%
Branch coverage
37%
Covered branches: 30
Total branches: 80
Branch coverage: 37.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
PreviewAsync()0%1980440%
Matches(...)90%2020100%
CountryName(...)75%88100%
DeclarationNumber(...)100%44100%
Digits(...)50%22100%
Normalize(...)50%22100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/Transactions/ArmtekReceiptOriginService.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using OzonDomains.Models;
 3using OzonDomains.Models.ArmtekEtp;
 4using OzonRepositories.Context;
 5using Services.ApiServices._1CApi.OData;
 6
 7namespace OzonOrdersWeb.WebServices.Transactions;
 8
 9public sealed record ReceiptOriginRow(int OrderId, string Article, string? SourceCountry,
 10    string? Country, string? SourceGtd, string? Gtd, string? Error);
 11public sealed record ReceiptOriginPreview(bool Applicable, string? Error, IReadOnlyList<ReceiptOriginRow> Rows)
 12{
 13    public bool Ready => Applicable && Error == null && Rows.Count > 0 && Rows.All(r => r.Error == null);
 14}
 15
 016public sealed class ArmtekReceiptOriginService(ApplicationDbContext db, IOneCODataReferenceService references)
 17{
 18    /// <summary>Номер ГТД для товара без декларации (страна ЕАЭС, внутренние поставки): в фактуре «- -».</summary>
 19    public const string DefaultGtd = OneCODataConstants.DefaultGtdNumber;
 20
 21    /// <param name="manualDefaultGtd">
 22    /// Заказы, где пользователь сам вписал номер по умолчанию: он важнее фактуры и проверки справочника не требует.
 23    /// </param>
 24    public async Task<ReceiptOriginPreview> PreviewAsync(string? number, int[] ids, CancellationToken ct,
 25        IReadOnlySet<int>? manualDefaultGtd = null)
 26    {
 027        var links = await db.ArmtekOrderItems.AsNoTracking().Include(x => x.ArmtekOrder)
 028            .Where(x => ids.Contains(x.StudioOrderId)).ToListAsync(ct);
 029        if (links.Count == 0) return new(false, null, []);
 030        if (string.IsNullOrWhiteSpace(number)) return new(true, "Введите номер фактуры, УПД или ТТН Армтека.", []);
 031        var key = number.Trim();
 032        var invoices = await db.ArmtekInvoices.AsNoTracking().Include(x => x.Items)
 033            .Where(x => x.Vbeln == key || x.UpdNumber == key || x.TtnNumber == key).ToListAsync(ct);
 034        var rows = new List<ReceiptOriginRow>();
 035        foreach (var id in ids.Distinct())
 36        {
 037            var orderLinks = links.Where(x => x.StudioOrderId == id).ToList();
 038            var matches = invoices.SelectMany(invoice => invoice.Items.Where(item =>
 039                orderLinks.Any(link => Matches(link, invoice, item)))).ToList();
 040            if (matches.Count != 1)
 41            {
 042                rows.Add(new(id, orderLinks.FirstOrDefault()?.Pin ?? id.ToString(), null, null, null, null,
 043                    matches.Count == 0 ? "Позиция заказа не найдена в фактуре. Проверьте номер и загрузку фактуры."
 044                        : "В фактуре несколько подходящих позиций. Требуется уточнить партию."));
 045                continue;
 46            }
 047            var item = matches[0];
 048            var country = CountryName(item.Country);
 049            var declaredGtd = manualDefaultGtd?.Contains(id) == true ? null : DeclarationNumber(item.Gtd);
 050            var gtd = declaredGtd ?? DefaultGtd;
 051            var countryKey = await references.GetCountryGuidByNameAsync(country);
 052            var gtdKey = await references.GetGtdGuidByNumberAsync(gtd);
 53            // «Без ГТД» не требует записи в справочнике: приходная получит пустую ссылку.
 054            var gtdResolved = gtdKey is { } found && found != Guid.Empty || declaredGtd == null;
 055            var errors = new List<string>();
 056            if (countryKey == null || countryKey == Guid.Empty)
 057                errors.Add($"Страна «{item.Country}» не найдена в справочнике 1С");
 058            if (!gtdResolved)
 059                errors.Add($"ГТД «{gtd}» не найдена в справочнике 1С");
 060            rows.Add(new(id, item.Article ?? string.Empty, item.Country,
 061                countryKey.HasValue && countryKey != Guid.Empty ? country : null, item.Gtd,
 062                gtdResolved ? gtd : null,
 063                errors.Count == 0 ? null : string.Join(". ", errors)));
 064        }
 065        return new(true, null, rows);
 066    }
 67
 68    public static bool Matches(ArmtekOrderItem link, ArmtekInvoice invoice, ArmtekInvoiceItem item)
 569        => link.ArmtekOrder != null
 570           && Digits(link.ArmtekOrder.Kunrg) == Digits(invoice.Kunnr)
 571           && Digits(link.ArmtekOrder.Vbeln).Length > 0
 572           && Digits(link.ArmtekOrder.Vbeln) == Digits(item.OrderNumber)
 573           && Normalize(link.Pin).Length > 0 && Normalize(link.Pin) == Normalize(item.Article)
 574           && Normalize(link.Brand).Length > 0 && Normalize(link.Brand) == Normalize(item.Brand)
 575           && (string.IsNullOrWhiteSpace(link.Posnr) || string.IsNullOrWhiteSpace(item.OrderPosition)
 576               || Digits(link.Posnr) == Digits(item.OrderPosition));
 77
 78    public static string CountryName(string? value)
 79    {
 1580        var text = value?.Trim() ?? string.Empty;
 81        // Формат Армтека: (CN, 156) Китай. Без названия код страны не угадываем.
 1582        return text.StartsWith('(') && text.IndexOf(')') is var end && end >= 0
 1583            ? text[(end + 1)..].Trim() : text;
 84    }
 85
 86    public static string? DeclarationNumber(string? value)
 587        => string.IsNullOrWhiteSpace(value) || !value.Any(char.IsDigit) ? null : value.Trim();
 2688    private static string Digits(string? value) => (value ?? string.Empty).Trim().TrimStart('0');
 89    private static string Normalize(string? value)
 1890        => string.Concat((value ?? string.Empty).Where(char.IsLetterOrDigit)).ToUpperInvariant();
 91}