| | | 1 | | using System.Text.Json.Serialization; |
| | | 2 | | using OzonDomains.Models; |
| | | 3 | | |
| | | 4 | | namespace Services.FeedPriceServices; |
| | | 5 | | |
| | | 6 | | /// <summary>Результат выбора строки фида для заказа.</summary> |
| | | 7 | | [JsonConverter(typeof(JsonStringEnumConverter))] |
| | | 8 | | public enum FeedOrderState |
| | | 9 | | { |
| | | 10 | | /// <summary>Строка найдена, цена есть.</summary> |
| | | 11 | | Ok, |
| | | 12 | | |
| | | 13 | | /// <summary>У заказа нет ключа артикула.</summary> |
| | | 14 | | NoKey, |
| | | 15 | | |
| | | 16 | | /// <summary>Клиент заказа не сопоставлен с кабинетом фида.</summary> |
| | | 17 | | NoCabinet, |
| | | 18 | | |
| | | 19 | | /// <summary>В кабинете клиента нет такого артикула.</summary> |
| | | 20 | | NotInFeed, |
| | | 21 | | |
| | | 22 | | /// <summary>Артикул в фиде есть, но ни цены, ни закупки, ни старой цены.</summary> |
| | | 23 | | NoPrice, |
| | | 24 | | |
| | | 25 | | /// <summary>Фид-сервис недоступен (используется расшифровкой цены).</summary> |
| | | 26 | | Error |
| | | 27 | | } |
| | | 28 | | |
| | | 29 | | /// <summary>Строка фида, соответствующая клиенту и складу заказа.</summary> |
| | | 30 | | public sealed class FeedOrderPick |
| | | 31 | | { |
| | 34 | 32 | | public FeedOrderState State { get; init; } |
| | 34 | 33 | | public FeedCurrentRow? Row { get; init; } |
| | | 34 | | |
| | | 35 | | /// <summary>Кабинет клиента заказа (ключ фида); null, если клиент не сопоставлен.</summary> |
| | 28 | 36 | | public string? Cabinet { get; init; } |
| | | 37 | | |
| | 2 | 38 | | public string? CabinetName => Cabinet is null ? null : FeedCabinets.DisplayName(Cabinet); |
| | 23 | 39 | | public string? Client { get; init; } |
| | | 40 | | |
| | | 41 | | /// <summary>Склад отгрузки заказа.</summary> |
| | 22 | 42 | | public string? OrderWarehouse { get; init; } |
| | | 43 | | |
| | | 44 | | /// <summary>Склада заказа нет среди складов фида: показана строка склада цены кабинета.</summary> |
| | 27 | 45 | | public bool WarehouseFallback { get; init; } |
| | | 46 | | } |
| | | 47 | | |
| | | 48 | | /// <summary>Выбирает из снимка фида строку по кабинету клиента и складу заказа.</summary> |
| | | 49 | | public static class FeedOrderRowPicker |
| | | 50 | | { |
| | | 51 | | public static FeedOrderPick Pick( |
| | | 52 | | IReadOnlyCollection<FeedCurrentRow> current, |
| | | 53 | | IReadOnlyCollection<string> articleKeys, |
| | | 54 | | string? clientName, |
| | | 55 | | string? warehouseName) |
| | | 56 | | { |
| | | 57 | | var cabinet = FeedCabinets.ForClientName(clientName); |
| | | 58 | | FeedOrderPick Result(FeedOrderState state, FeedCurrentRow? row = null, bool fallback = false) => new() |
| | | 59 | | { |
| | | 60 | | State = state, |
| | | 61 | | Row = row, |
| | | 62 | | Cabinet = cabinet, |
| | | 63 | | Client = clientName, |
| | | 64 | | OrderWarehouse = warehouseName, |
| | | 65 | | WarehouseFallback = fallback |
| | | 66 | | }; |
| | | 67 | | |
| | | 68 | | if (articleKeys.Count == 0) return Result(FeedOrderState.NoKey); |
| | | 69 | | if (cabinet is null) return Result(FeedOrderState.NoCabinet); |
| | | 70 | | |
| | | 71 | | // Ключей до двух; берём первый, по которому в кабинете есть данные. |
| | | 72 | | List<FeedCurrentRow> rows = []; |
| | | 73 | | foreach (var key in articleKeys) |
| | | 74 | | { |
| | | 75 | | rows = current |
| | | 76 | | .Where(x => string.Equals(x.Cabinet, cabinet, StringComparison.OrdinalIgnoreCase) |
| | | 77 | | && string.Equals(x.Article, key, StringComparison.OrdinalIgnoreCase)) |
| | | 78 | | .ToList(); |
| | | 79 | | if (rows.Count > 0) break; |
| | | 80 | | } |
| | | 81 | | |
| | | 82 | | if (rows.Count == 0) return Result(FeedOrderState.NotInFeed); |
| | | 83 | | |
| | | 84 | | var wanted = WarehouseNameNormalizer.Normalize(warehouseName); |
| | | 85 | | var exact = wanted is null |
| | | 86 | | ? null |
| | | 87 | | : rows.FirstOrDefault(x => string.Equals(WarehouseNameNormalizer.Normalize(x.Warehouse), wanted, StringCompa |
| | | 88 | | |
| | | 89 | | // Цена кабинета общая для складов, но у склада без цены строка пустая: тогда берём склад цены. |
| | | 90 | | if (exact is not null && HasValues(exact)) return Result(FeedOrderState.Ok, exact); |
| | | 91 | | |
| | | 92 | | var fallback = rows.FirstOrDefault(x => x.IsPriceWarehouse && HasValues(x)) ?? rows.FirstOrDefault(HasValues); |
| | | 93 | | if (fallback is not null) return Result(FeedOrderState.Ok, fallback, fallback: true); |
| | | 94 | | |
| | | 95 | | return Result(FeedOrderState.NoPrice, exact ?? rows[0], fallback: exact is null); |
| | | 96 | | } |
| | | 97 | | |
| | | 98 | | private static bool HasValues(FeedCurrentRow row) => |
| | | 99 | | row.PurchasePrice is not null || row.Price is not null || row.OldPrice is not null; |
| | | 100 | | } |