| | | 1 | | using Services.ApiServices.AvdApi; |
| | | 2 | | using OzonDomains.Models; |
| | | 3 | | |
| | | 4 | | namespace OzonOrdersWeb.Areas.Studio2.ViewModels.SupplierCart; |
| | | 5 | | |
| | | 6 | | public sealed record class SupplierCartSection |
| | | 7 | | { |
| | | 8 | | public string ProviderKey { get; init; } = string.Empty; |
| | | 9 | | public string DisplayName { get; init; } = string.Empty; |
| | | 10 | | public string LogoUrl { get; init; } = string.Empty; |
| | | 11 | | public string CurrencyCode { get; init; } = "RUB"; |
| | | 12 | | public IReadOnlyList<SupplierCartRow> Rows { get; init; } = Array.Empty<SupplierCartRow>(); |
| | | 13 | | public IReadOnlyList<AvdOrder> Orders { get; init; } = Array.Empty<AvdOrder>(); |
| | | 14 | | public IReadOnlyList<ArmtekOrder> ArmtekOrders { get; init; } = Array.Empty<ArmtekOrder>(); |
| | | 15 | | public IReadOnlyList<ArmtekApiOrderRow> ArmtekApiOrders { get; init; } = Array.Empty<ArmtekApiOrderRow>(); |
| | | 16 | | public IReadOnlyList<MlAutoOrder> MlAutoOrders { get; init; } = Array.Empty<MlAutoOrder>(); |
| | | 17 | | public IReadOnlyList<MlAutoApiOrderRow> MlAutoApiOrders { get; init; } = Array.Empty<MlAutoApiOrderRow>(); |
| | | 18 | | public IReadOnlyList<ForumAutoOrder> ForumAutoOrders { get; init; } = Array.Empty<ForumAutoOrder>(); |
| | | 19 | | public IReadOnlyList<ForumAutoApiOrderRow> ForumAutoApiOrders { get; init; } = Array.Empty<ForumAutoApiOrderRow>(); |
| | | 20 | | public IReadOnlyList<FavoritPartsApiOrderRow> FavoritPartsApiOrders { get; init; } = Array.Empty<FavoritPartsApiOrde |
| | | 21 | | public IReadOnlyList<MoskvorechieIstraApiOrderRow> MoskvorechieIstraOrders { get; init; } = Array.Empty<Moskvorechie |
| | | 22 | | public IReadOnlyList<MotexApiOrderRow> MotexOrders { get; init; } = Array.Empty<MotexApiOrderRow>(); |
| | | 23 | | public IReadOnlyList<ShateMApiOrderRow> ShateMOrders { get; init; } = Array.Empty<ShateMApiOrderRow>(); |
| | | 24 | | public IReadOnlyList<ProfitLigaApiOrderRow> ProfitLigaOrders { get; init; } = Array.Empty<ProfitLigaApiOrderRow>(); |
| | | 25 | | public IReadOnlyList<NikeiApiOrderRow> NikeiOrders { get; init; } = Array.Empty<NikeiApiOrderRow>(); |
| | | 26 | | public IReadOnlyList<JapartsApiOrderRow> JapartsOrders { get; init; } = Array.Empty<JapartsApiOrderRow>(); |
| | | 27 | | public IReadOnlyList<BergApiOrderRow> BergOrders { get; init; } = Array.Empty<BergApiOrderRow>(); |
| | | 28 | | public IReadOnlyList<ZZapApiOrderRow> ZZapOrders { get; init; } = Array.Empty<ZZapApiOrderRow>(); |
| | | 29 | | /// <summary>Связанные заказы Studio2 по номеру оформленного заказа поставщика.</summary> |
| | | 30 | | public IReadOnlyDictionary<string, IReadOnlyList<SupplierCartStudioOrderLink>> StudioOrderLinksBySupplierOrder { get |
| | | 31 | | = new Dictionary<string, IReadOnlyList<SupplierCartStudioOrderLink>>(StringComparer.Ordinal); |
| | | 32 | | public IReadOnlyList<MlAutoDeliveryOption> MlAutoDeliveryOptions { get; init; } = Array.Empty<MlAutoDeliveryOption>( |
| | | 33 | | /// <summary>Пары «покупатель + адрес доставки» Armtek для выбора в строке корзины.</summary> |
| | | 34 | | public IReadOnlyList<ArmtekDeliveryOption> ArmtekDeliveryOptions { get; init; } = Array.Empty<ArmtekDeliveryOption>( |
| | | 35 | | public string? ArmtekDeliveryOptionsError { get; init; } |
| | | 36 | | public string? Error { get; init; } |
| | | 37 | | public string? OrdersError { get; init; } |
| | | 38 | | |
| | | 39 | | public static SupplierCartSection Failed(string providerKey, string displayName, string logoUrl, string error) => |
| | | 40 | | new() { ProviderKey = providerKey, DisplayName = displayName, LogoUrl = logoUrl, Error = error }; |
| | | 41 | | } |
| | | 42 | | |
| | | 43 | | public sealed record MlAutoDeliveryOption(string Id, string DisplayName); |
| | | 44 | | |
| | | 45 | | public sealed record SupplierCartStudioOrderLink(int OrderId, string? ShipmentNumber, string? ClientName); |
| | | 46 | | public sealed record MoskvorechieIstraApiOrderRow(long Id, string? Number, DateTime CreatedAt, string Status, int ItemsC |
| | 0 | 47 | | public sealed record MotexApiOrderRow(long Id, string? ExternalId, string ClientOrderNum, string Status, DateTime Create |
| | | 48 | | public sealed record JapartsApiOrderRow(string GlobalId, string OrderNumber, string Brand, string Article, string Produc |
| | | 49 | | decimal QuantityOrdered, decimal QuantityBought, decimal? QuantityArrived, decimal? QuantityShipped, decimal Price, |
| | | 50 | | string Status, string Country, DateTime? LastUpdated, string Comment); |
| | | 51 | | /// <summary>Заказ Berg из ordering/states: статус живёт на строке, поэтому строки вложены.</summary> |
| | | 52 | | public sealed record BergApiOrderRow(long Id, long? Reference, bool IsTest, DateTime? CreatedAt, DateTime? DispatchAt, |
| | | 53 | | string? ShipmentAddress, string? Comment, IReadOnlyList<BergApiOrderItemRow> Items) |
| | | 54 | | { |
| | | 55 | | public decimal Amount => Items.Sum(x => (x.Price ?? 0m) * (x.Quantity ?? 0m)); |
| | | 56 | | |
| | | 57 | | /// <summary>Сводный статус шапки: пока строки в разных состояниях, показываем «смешанный».</summary> |
| | | 58 | | public string Status => Items.Select(x => x.StateName).Distinct().Count() == 1 |
| | | 59 | | ? Items[0].StateName ?? string.Empty |
| | | 60 | | : "Разные статусы"; |
| | | 61 | | } |
| | | 62 | | |
| | | 63 | | public sealed record BergApiOrderItemRow(int Sequence, string? Article, string? Brand, string? Name, |
| | | 64 | | string? WarehouseName, decimal? Quantity, decimal? Price, int? StateId, string? StateName, |
| | | 65 | | DateTime? AssuredTime, string? Comment, int? StudioOrderId, string? StudioOrderNumber); |
| | | 66 | | |
| | | 67 | | public sealed record ShateMApiOrderRow(long Id, string? ExternalId, string Status, DateTime CreatedAt, int ItemsCount, d |
| | | 68 | | |
| | | 69 | | /// <summary>Заказ ZZap: одна позиция = один заказ у продавца (code_order). Статус — живой из GET /orders, |
| | | 70 | | /// связь с заказом Studio2 — из зеркала ZZapCartItems по тому же code_order.</summary> |
| | | 71 | | public sealed record ZZapApiOrderRow(long CodeOrder, DateTime? CreatedAt, string? Article, string? Brand, string? Produc |
| | | 72 | | string? SellerName, string? SellerPhone, decimal? Price, string? Quantity, int? StatusCode, string Status, string? M |
| | | 73 | | DateTime? StatusChangedAt, int? StudioOrderId, string? StudioOrderNumber, string? StudioClientName) |
| | | 74 | | { |
| | | 75 | | /// <summary>Отменить из Studio2 можно только свой заказ и только пока продавец его не закрыл.</summary> |
| | | 76 | | public bool CanCancel => StudioOrderId.HasValue && StatusCode is not (11 or 153 or 10 or 142 or 169); |
| | | 77 | | } |
| | | 78 | | |
| | | 79 | | /// <summary>Заказ Профит-Лиги: номер из orders[], состав и статусы позиций — из GET /orders/list.</summary> |
| | | 80 | | public sealed record ProfitLigaApiOrderRow(long Id, string OrderNumber, string Status, DateTime CreatedAt, |
| | | 81 | | string? PointName, string? DeliveryDate, int ItemsCount, decimal Amount, |
| | | 82 | | IReadOnlyList<ProfitLigaApiOrderItemRow> Items); |
| | | 83 | | |
| | | 84 | | public sealed record ProfitLigaApiOrderItemRow(string Article, string Brand, string? ProductName, |
| | | 85 | | decimal Quantity, decimal? Price, string? Status, string? WarehouseName, string? ShowDate); |
| | | 86 | | |
| | | 87 | | public sealed record NikeiApiOrderRow(string Invoice, int? Id, string Article, string Brand, string? ProductName, |
| | | 88 | | decimal? Quantity, decimal? Price, string Status, string? ShippingName, string? ShippingAddress, DateTime? CreatedAt |
| | | 89 | | int? StudioOrderId, string? StudioOrderNumber); |
| | | 90 | | |
| | | 91 | | /// <summary>Заказ ML-Auto: шапка собирается из GetMyOrders, состав и статусы — из GetOrderByNumber.</summary> |
| | | 92 | | public sealed record MlAutoApiOrderRow( |
| | | 93 | | string Number, |
| | | 94 | | DateTime? CreatedAt, |
| | | 95 | | string Status, |
| | | 96 | | string PaymentType, |
| | | 97 | | int ItemsCount, |
| | | 98 | | decimal Quantity, |
| | | 99 | | decimal? Amount, |
| | | 100 | | DateTime? DeliveryFrom, |
| | | 101 | | DateTime? DeliveryTo, |
| | | 102 | | string Comment, |
| | | 103 | | IReadOnlyList<MlAutoApiOrderItemRow> Items, |
| | | 104 | | string? DetailsError) |
| | | 105 | | { |
| | | 106 | | public string DeliveryRange => DeliveryFrom is null |
| | | 107 | | ? "—" |
| | | 108 | | : DeliveryTo is null || DeliveryFrom.Value.Date == DeliveryTo.Value.Date |
| | | 109 | | ? DeliveryFrom.Value.ToString("dd.MM.yyyy") |
| | | 110 | | : $"{DeliveryFrom.Value:dd.MM.yyyy} — {DeliveryTo.Value:dd.MM.yyyy}"; |
| | | 111 | | |
| | | 112 | | public string SearchText => string.Join(' ', |
| | | 113 | | new[] { Number, Comment, Status, PaymentType } |
| | | 114 | | .Concat(Items.Select(x => $"{x.Brand} {x.Pin} {x.Comment}"))); |
| | | 115 | | } |
| | | 116 | | |
| | | 117 | | public sealed record MlAutoApiOrderItemRow( |
| | | 118 | | string Pin, |
| | | 119 | | string Brand, |
| | | 120 | | decimal Quantity, |
| | | 121 | | decimal? Price, |
| | | 122 | | decimal? Sum, |
| | | 123 | | string Status, |
| | | 124 | | string StatusCode, |
| | | 125 | | DateTime? EstimatedDelivery, |
| | | 126 | | string Comment, |
| | | 127 | | bool Rejected); |
| | | 128 | | |
| | | 129 | | public sealed class SupplierCartRow |
| | | 130 | | { |
| | | 131 | | public long? LocalCartItemId { get; init; } |
| | | 132 | | public string ApiItemId { get; init; } = string.Empty; |
| | | 133 | | public string ApiCatalogName { get; init; } = string.Empty; |
| | | 134 | | public string ApiItemName { get; init; } = string.Empty; |
| | | 135 | | public string ApiItemNumber { get; init; } = string.Empty; |
| | | 136 | | public decimal? ApiPrice { get; init; } |
| | | 137 | | public decimal? ApiQuantity { get; init; } |
| | | 138 | | public string ApiRegion { get; init; } = string.Empty; |
| | | 139 | | public string ApiSupplierName { get; init; } = string.Empty; |
| | | 140 | | public int? ApiPeriod { get; init; } |
| | | 141 | | public string ApiComment { get; init; } = string.Empty; |
| | | 142 | | public DateTime? AddedAt { get; init; } |
| | | 143 | | |
| | | 144 | | public int? StudioOrderId { get; init; } |
| | | 145 | | public string? StudioOrderNumber { get; init; } |
| | | 146 | | public string? StudioProductName { get; init; } |
| | | 147 | | public string? StudioArticle { get; init; } |
| | | 148 | | public string? StudioManufacturer { get; init; } |
| | | 149 | | public string? StudioProductImageUrl { get; init; } |
| | | 150 | | public string? StudioClientName { get; init; } |
| | | 151 | | |
| | | 152 | | /// <summary>KUNRG/KUNZA, зафиксированные при добавлении в корзину; уходят в createOrder как есть.</summary> |
| | | 153 | | public string? ArmtekBuyerKunnr { get; init; } |
| | | 154 | | public string? ArmtekDeliveryAddressKunnr { get; init; } |
| | | 155 | | public string? ArmtekDeliveryAddressName { get; init; } |
| | | 156 | | public bool ArmtekDeliveryMappingLinked { get; init; } |
| | | 157 | | public string? MlAutoDeliveryAddressId { get; init; } |
| | | 158 | | public string? MlAutoDeliveryAddressName { get; init; } |
| | | 159 | | public string? MlAutoShipmentWarehouseName { get; init; } |
| | | 160 | | |
| | | 161 | | /// <summary>Торговая точка Профит-Лиги, в которую уйдёт заказ по этой строке.</summary> |
| | | 162 | | public string? ProfitLigaPointName { get; init; } |
| | | 163 | | public bool MlAutoDeliveryMappingLinked { get; init; } |
| | | 164 | | |
| | | 165 | | /// <summary>ZZap: code_order позиции у поставщика и её живое состояние из GET /cart (null — API недоступен).</summa |
| | | 166 | | public long? ZZapCodeOrder { get; init; } |
| | | 167 | | public bool? ZZapInApiCart { get; init; } |
| | | 168 | | public decimal? ZZapApiPrice { get; init; } |
| | | 169 | | public string? ZZapApiDeliveryText { get; init; } |
| | | 170 | | |
| | | 171 | | public decimal? Sum => ApiPrice.HasValue && ApiQuantity.HasValue ? ApiPrice.Value * ApiQuantity.Value : null; |
| | | 172 | | } |
| | | 173 | | |
| | | 174 | | /// <summary>Вариант доставки Armtek: покупатель <c>RG_TAB.KUNNR</c> и его адрес <c>ZA_TAB.KUNNR</c>.</summary> |
| | | 175 | | public sealed record ArmtekDeliveryOption(string BuyerKunnr, string BuyerName, string AddressKunnr, string AddressName) |
| | | 176 | | { |
| | | 177 | | public string Value => $"{BuyerKunnr}|{AddressKunnr}"; |
| | | 178 | | public string DisplayName => $"{BuyerName} · {AddressName}"; |
| | | 179 | | } |