< Summary

Information
Class: Services.BackgroundJobsServices.WarehouseResolution
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/BackgroundJobsServices/ShippedToCommissionerWarehouseResolver.cs
Line coverage
100%
Covered lines: 1
Uncovered lines: 0
Coverable lines: 1
Total lines: 136
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Warehouse()100%11100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/BackgroundJobsServices/ShippedToCommissionerWarehouseResolver.cs

#LineLine coverage
 1using OzonDomains.Models;
 2
 3namespace Services.BackgroundJobsServices;
 4
 5/// <summary>
 6/// Склад-отправитель и признак того, что имя пришло из WarehouseMapping, а не взято как есть.
 7/// </summary>
 738public readonly record struct WarehouseResolution(string? Warehouse, bool FromMapping);
 9public readonly record struct TransitWarehouseResolution(string? SenderWarehouse, string? TransitWarehouse);
 10
 11/// <summary>
 12/// Склад отправителя для документов 1С: BitrixWarehouseName из WarehouseMapping по клиенту Ozon,
 13/// приоритетному ключу «Засыл» либо складу заказа и, если он задан в маппинге, типу доставки.
 14/// </summary>
 15public static class ShippedToCommissionerWarehouseResolver
 16{
 17    public const string ZasylKey = "Засыл";
 18    private const string EmexClientName = "Эмекс";
 19
 20    public static string? ResolveSenderWarehouse(Order order, IReadOnlyList<WarehouseMapping> mappings)
 21    {
 22        return ResolveSender(order, mappings).Warehouse;
 23    }
 24
 25    /// <summary>
 26    /// Тот же резолв, но с возможностью подставить засыл извне (значение поля формы, ещё не
 27    /// сохранённое в OrderExtensions) и узнать, нашлась ли связь складов.
 28    /// </summary>
 29    public static WarehouseResolution ResolveSender(
 30        Order order,
 31        IReadOnlyList<WarehouseMapping> mappings,
 32        string? zasylOverride = null)
 33    {
 34        if (order == null)
 35        {
 36            return new WarehouseResolution(null, false);
 37        }
 38
 39        var zasyl = !string.IsNullOrWhiteSpace(zasylOverride)
 40            ? zasylOverride.Trim()
 41            : order.OrderExtensions?
 42                .FirstOrDefault(e => string.Equals(e.Key, ZasylKey, StringComparison.OrdinalIgnoreCase))
 43                ?.Value?
 44                .Trim();
 45        var shipmentWarehouseName = order.ShipmentWarehouse?.Name?.Trim();
 46        var warehouseMappingName = GetWarehouseMappingName(zasyl, shipmentWarehouseName);
 47        if (!order.OzonClientId.HasValue || string.IsNullOrWhiteSpace(warehouseMappingName))
 48        {
 49            return new WarehouseResolution(warehouseMappingName, false);
 50        }
 51
 52        var best = ResolveBestMapping(order, mappings, warehouseMappingName);
 53
 54        if (best != null)
 55        {
 56            return new WarehouseResolution(best.BitrixWarehouseName.Trim(), true);
 57        }
 58
 59        return new WarehouseResolution(warehouseMappingName, false);
 60    }
 61
 62    /// <summary>Транзитный склад берётся только из выбранной связи WarehouseMapping.</summary>
 63    public static TransitWarehouseResolution ResolveTransitWarehouse(
 64        Order order,
 65        IReadOnlyList<WarehouseMapping> mappings)
 66    {
 67        var sender = ResolveSender(order, mappings);
 68        if (order == null || !order.OzonClientId.HasValue || string.IsNullOrWhiteSpace(sender.Warehouse))
 69            return new TransitWarehouseResolution(sender.Warehouse, null);
 70
 71        var mappingName = GetWarehouseMappingName(
 72            order.OrderExtensions?
 73                .FirstOrDefault(e => string.Equals(e.Key, ZasylKey, StringComparison.OrdinalIgnoreCase))
 74                ?.Value?.Trim(),
 75            order.ShipmentWarehouse?.Name?.Trim());
 76        var mapping = string.IsNullOrWhiteSpace(mappingName)
 77            ? null
 78            : ResolveBestMapping(order, mappings, mappingName);
 79        return new TransitWarehouseResolution(
 80            sender.Warehouse,
 81            string.IsNullOrWhiteSpace(mapping?.TransitBitrixWarehouseName)
 82                ? null
 83                : mapping.TransitBitrixWarehouseName.Trim());
 84    }
 85
 86    private static string? GetWarehouseMappingName(string? zasyl, string? shipmentWarehouseName) =>
 87        WarehouseNameNormalizer.Normalize(!string.IsNullOrWhiteSpace(zasyl) ? zasyl : shipmentWarehouseName);
 88
 89    private static WarehouseMapping? ResolveBestMapping(
 90        Order order,
 91        IReadOnlyList<WarehouseMapping> mappings,
 92        string warehouseMappingName)
 93    {
 94        var candidates = mappings
 95            .Where(m => m.OzonClientId == order.OzonClientId &&
 96                        !string.IsNullOrWhiteSpace(m.OzonWarehouseName) &&
 97                        !string.IsNullOrWhiteSpace(m.BitrixWarehouseName) &&
 98                        string.Equals(WarehouseNameNormalizer.Normalize(m.OzonWarehouseName), warehouseMappingName,
 99                            StringComparison.OrdinalIgnoreCase))
 100            .ToList();
 101
 102        var deliveryName = GetMappingDeliveryType(order);
 103        if (!string.IsNullOrWhiteSpace(deliveryName))
 104        {
 105            var exactDeliveryCandidates = candidates
 106                .Where(m => string.Equals(m.DeliveryType?.Trim(), deliveryName, StringComparison.OrdinalIgnoreCase))
 107                .ToList();
 108            candidates = exactDeliveryCandidates.Count > 0
 109                ? exactDeliveryCandidates
 110                : candidates.Where(m => string.IsNullOrWhiteSpace(m.DeliveryType)).ToList();
 111        }
 112        else
 113        {
 114            // Для клиентов кроме Эмекс сначала используем общую связь без типа доставки.
 115            // Если её нет, сохраняем доступную связь (например, Interparts RU + M1 + Ozon).
 116            var genericCandidates = candidates
 117                .Where(m => string.IsNullOrWhiteSpace(m.DeliveryType))
 118                .ToList();
 119            if (genericCandidates.Count > 0)
 120                candidates = genericCandidates;
 121        }
 122
 123        return candidates.OrderBy(m => m.Priority).ThenBy(m => m.Id).FirstOrDefault();
 124    }
 125
 126    /// <summary>
 127    /// Тип доставки, который резолвер учитывает при отборе связей: заполнен только для Эмекс.
 128    /// Нужен и снаружи — чтобы предложенная пользователю новая связь действительно применилась.
 129    /// </summary>
 130    public static string? GetMappingDeliveryType(Order? order)
 131    {
 132        var isEmexOrder = string.Equals(
 133            order?.OzonClient?.Name?.Trim(), EmexClientName, StringComparison.OrdinalIgnoreCase);
 134        return isEmexOrder ? order?.Delivery?.Name?.Trim() : null;
 135    }
 136}

Methods/Properties

get_Warehouse()