| | | 1 | | namespace Services.FeedPriceServices; |
| | | 2 | | |
| | | 3 | | /// <summary>Один согласованный снимок фида: текущие данные и история.</summary> |
| | | 4 | | public sealed record FeedPriceSnapshot( |
| | | 5 | | IReadOnlyList<FeedCurrentRow> Current, |
| | | 6 | | IReadOnlyList<FeedHistoryRow> History); |
| | | 7 | | |
| | | 8 | | /// <summary> |
| | | 9 | | /// Текущие строки фида по ключам одной пачки. Ключ без строк в <see cref="Rows"/> есть, но список пуст (его нет в фиде) |
| | | 10 | | /// ключи из <see cref="Failed"/> получить не удалось — строк по ним нет и им нельзя доверять как «нет в фиде». |
| | | 11 | | /// </summary> |
| | | 12 | | public sealed record FeedRowsResult( |
| | | 13 | | IReadOnlyDictionary<string, IReadOnlyList<FeedCurrentRow>> Rows, |
| | | 14 | | IReadOnlySet<string> Failed); |
| | | 15 | | |
| | | 16 | | /// <summary>Строка «сейчас»: склад кабинета с остатком и цена артикула в этом кабинете (цена общая для всех складов).</ |
| | | 17 | | public sealed class FeedCurrentRow |
| | | 18 | | { |
| | 107 | 19 | | public string Cabinet { get; init; } = string.Empty; |
| | 2 | 20 | | public string CabinetName => FeedCabinets.DisplayName(Cabinet); |
| | 111 | 21 | | public string Article { get; init; } = string.Empty; |
| | 80 | 22 | | public string? Warehouse { get; init; } |
| | 3 | 23 | | public int? Stock { get; init; } |
| | 3 | 24 | | public string? StockSupplier { get; init; } |
| | 47 | 25 | | public decimal? Price { get; init; } |
| | 8 | 26 | | public decimal? OldPrice { get; init; } |
| | 3 | 27 | | public decimal? MinPrice { get; init; } |
| | 56 | 28 | | public decimal? PurchasePrice { get; init; } |
| | 45 | 29 | | public string? PriceWarehouse { get; init; } |
| | 25 | 30 | | public string? PriceSupplier { get; init; } |
| | 4 | 31 | | public long? RunId { get; init; } |
| | 4 | 32 | | public DateTimeOffset UpdatedAt { get; init; } |
| | | 33 | | |
| | | 34 | | /// <summary>Склад, с которого рассчитана цена кабинета.</summary> |
| | | 35 | | public bool IsPriceWarehouse => |
| | 10 | 36 | | !string.IsNullOrEmpty(Warehouse) && string.Equals(Warehouse, PriceWarehouse, StringComparison.OrdinalIgnoreCase) |
| | | 37 | | } |
| | | 38 | | |
| | | 39 | | /// <summary>Строка истории: цена артикула в одном прогоне сборки кабинета.</summary> |
| | | 40 | | public sealed class FeedHistoryRow |
| | | 41 | | { |
| | | 42 | | public long RunId { get; init; } |
| | | 43 | | public DateTimeOffset AssembledAt { get; init; } |
| | | 44 | | public string AssembledBy { get; init; } = string.Empty; |
| | | 45 | | public string Cabinet { get; init; } = string.Empty; |
| | | 46 | | public string CabinetName => FeedCabinets.DisplayName(Cabinet); |
| | | 47 | | public string Article { get; init; } = string.Empty; |
| | | 48 | | public decimal? Price { get; init; } |
| | | 49 | | public decimal? OldPrice { get; init; } |
| | | 50 | | public long? TotalStock { get; init; } |
| | | 51 | | |
| | | 52 | | /// <summary>Изменение цены к предыдущему прогону этого кабинета; null, если сравнивать не с чем.</summary> |
| | | 53 | | public decimal? PriceDelta { get; set; } |
| | | 54 | | } |
| | | 55 | | |
| | | 56 | | /// <summary>Ответ для модалки: ошибка БД возвращается в модели, чтобы окно не падало.</summary> |
| | | 57 | | public sealed class FeedPriceResult |
| | | 58 | | { |
| | | 59 | | public IReadOnlyList<string> ArticleKeys { get; init; } = Array.Empty<string>(); |
| | | 60 | | public IReadOnlyList<FeedCurrentRow> Current { get; init; } = Array.Empty<FeedCurrentRow>(); |
| | | 61 | | public IReadOnlyList<FeedHistoryRow> History { get; init; } = Array.Empty<FeedHistoryRow>(); |
| | | 62 | | public string? Error { get; init; } |
| | | 63 | | public bool Found => Current.Count > 0; |
| | | 64 | | |
| | | 65 | | /// <summary>Строка, подходящая клиенту и складу заказа; заполняется только для запроса по заказу.</summary> |
| | | 66 | | public FeedOrderPick? ForOrder { get; init; } |
| | | 67 | | |
| | | 68 | | /// <summary>Копия ответа с выбранной строкой: закешированный общий ответ не меняется.</summary> |
| | | 69 | | public FeedPriceResult WithOrder(FeedOrderPick pick) => new() |
| | | 70 | | { |
| | | 71 | | ArticleKeys = ArticleKeys, |
| | | 72 | | Current = Current, |
| | | 73 | | History = History, |
| | | 74 | | Error = Error, |
| | | 75 | | ForOrder = pick |
| | | 76 | | }; |
| | | 77 | | } |