| | | 1 | | using Microsoft.AspNetCore.Mvc; |
| | | 2 | | using Newtonsoft.Json; |
| | | 3 | | using OServices.FiltersServices.FilterModels; |
| | | 4 | | using OzonDomains; |
| | | 5 | | using OzonDomains.Models; |
| | | 6 | | using OzonOrdersWeb.Areas.Studio2.Controllers.Base; |
| | | 7 | | using OzonOrdersWeb.Areas.Studio2.Models.Transactions; |
| | | 8 | | using Services.BackgroundJobsServices; |
| | | 9 | | using OzonOrdersWeb.ViewModels.OrderViewModels; |
| | | 10 | | using OzonRepositories.Context; |
| | | 11 | | using Services.ApiServices; |
| | | 12 | | using Services.ApiServices._1CApi; |
| | | 13 | | using Services.ApiServices.OzonApi; |
| | | 14 | | using Services.Authorization.Services; |
| | | 15 | | using Services.CacheServices.Cache.OzonOrdersCache; |
| | | 16 | | using Services.CacheServices.Cache.UserCacheService; |
| | | 17 | | using Services.DataServices; |
| | | 18 | | using OzonOrdersWeb.WebServices.Currency; |
| | | 19 | | using Services.TransactionUtilsServices; |
| | | 20 | | using Services.CacheServices.Cache; |
| | | 21 | | |
| | | 22 | | namespace OzonOrdersWeb.Areas.Studio2.Controllers.Transactions; |
| | | 23 | | |
| | | 24 | | /// <summary> |
| | | 25 | | /// Контроллер транзакции "Возврат покупателя" |
| | | 26 | | /// </summary> |
| | | 27 | | public class CustomerReturnTransactionController : BaseTransactionController |
| | | 28 | | { |
| | | 29 | | private readonly WarehouseSelectionResolver _warehouseSelectionResolver; |
| | | 30 | | |
| | | 31 | | public CustomerReturnTransactionController( |
| | | 32 | | ApplicationDbContext context, |
| | | 33 | | AppStatusDataServices appStatusServices, |
| | | 34 | | OrdersDataServices orderServices, |
| | | 35 | | TransactionManager transactionManager, |
| | | 36 | | TransactionCache transactionCache, |
| | | 37 | | SupplierDataServices supplierDataServices, |
| | | 38 | | IUserCacheService userCacheService, |
| | | 39 | | ICurrencyRatesViewBagService currencyRatesViewBagService, |
| | | 40 | | CacheUpdater<Order> cacheUpdater, |
| | | 41 | | OrderCache orderCache, |
| | | 42 | | WarehouseDataServices warehouseDataServices, |
| | | 43 | | ExpenseTypeDataServices expenseTypeDataServices, |
| | | 44 | | OrderExtraExpenseDataServices orderExtraExpenseDataServices, |
| | | 45 | | StatusTransitionDataServices statusTransitionDataServices, |
| | | 46 | | OneCDataManager oneCDataManager, |
| | | 47 | | OneCTransferManager oneCTransferManager, |
| | | 48 | | IScopeFilterValuesService scopeFilterValuesService, |
| | | 49 | | WarehouseSelectionResolver warehouseSelectionResolver, |
| | | 50 | | OzonOrderCancellationService? ozonCancellationService = null) |
| | 0 | 51 | | : base(context, appStatusServices, orderServices, transactionManager, transactionCache, |
| | 0 | 52 | | supplierDataServices, userCacheService, currencyRatesViewBagService, cacheUpdater, orderCache, |
| | 0 | 53 | | warehouseDataServices: warehouseDataServices, expenseTypeDataServices: expenseTypeDataServices, |
| | 0 | 54 | | orderExtraExpenseDataServices: orderExtraExpenseDataServices, |
| | 0 | 55 | | statusTransitionDataServices: statusTransitionDataServices, |
| | 0 | 56 | | oneCDataManager: oneCDataManager, |
| | 0 | 57 | | oneCTransferManager: oneCTransferManager, |
| | 0 | 58 | | scopeFilterValuesService: scopeFilterValuesService, |
| | 0 | 59 | | ozonCancellationService: ozonCancellationService) |
| | | 60 | | { |
| | 0 | 61 | | _warehouseSelectionResolver = warehouseSelectionResolver; |
| | 0 | 62 | | } |
| | | 63 | | |
| | | 64 | | protected override TransactionConfig GetTransactionConfig() |
| | | 65 | | { |
| | 0 | 66 | | if (Request.HasFormContentType && |
| | 0 | 67 | | string.Equals(Request.Form["bulkFromFile"].FirstOrDefault(), "true", StringComparison.OrdinalIgnoreCase)) |
| | | 68 | | { |
| | 0 | 69 | | return GetCustomerReturnFromFileTransactionConfig(); |
| | | 70 | | } |
| | | 71 | | |
| | 0 | 72 | | return GetStandardCustomerReturnTransactionConfig(); |
| | | 73 | | } |
| | | 74 | | |
| | 0 | 75 | | private static TransactionConfig GetStandardCustomerReturnTransactionConfig() => new() |
| | 0 | 76 | | { |
| | 0 | 77 | | TransactionType = TransactionType.CustomerReturn, |
| | 0 | 78 | | PrimaryStatusName = "Возврат покупателя", |
| | 0 | 79 | | UseStatusTransitions = false, |
| | 0 | 80 | | ViewName = "CreateCustomerReturnTransaction", |
| | 0 | 81 | | IncludeCancelledOrders = true, |
| | 0 | 82 | | SupportsOzonCancelPanel = true, |
| | 0 | 83 | | LoadWarehouseListForSelector = true, |
| | 0 | 84 | | UseCustomWarehousesFor1C = true, |
| | 0 | 85 | | OrderFilter = order => |
| | 0 | 86 | | IsMarketplaceCancelled(order) || |
| | 0 | 87 | | order.AppStatus?.Name is "Отгружен клиенту" or "Отгружен комиссионеру" or "Отменен", |
| | 0 | 88 | | CreateTransactionMethod = (tm, orders, userName, createAt, comment) => |
| | 0 | 89 | | tm.CreateCustomerReturnTransaction(orders, userName, createAt, comment) |
| | 0 | 90 | | }; |
| | | 91 | | |
| | | 92 | | /// <summary>Массовый сценарий из отчёта UploadFile: без фильтра по статусу, без складов и без 1С.</summary> |
| | 0 | 93 | | private static TransactionConfig GetCustomerReturnFromFileTransactionConfig() => new() |
| | 0 | 94 | | { |
| | 0 | 95 | | TransactionType = TransactionType.CustomerReturn, |
| | 0 | 96 | | PrimaryStatusName = "Возврат покупателя", |
| | 0 | 97 | | UseStatusTransitions = false, |
| | 0 | 98 | | ViewName = "CreateCustomerReturnTransaction", |
| | 0 | 99 | | IncludeCancelledOrders = true, |
| | 0 | 100 | | SupportsOzonCancelPanel = true, |
| | 0 | 101 | | LoadWarehouseListForSelector = false, |
| | 0 | 102 | | UseCustomWarehousesFor1C = false, |
| | 0 | 103 | | OrderFilter = _ => true, |
| | 0 | 104 | | SuccessMessageBuilder = (changeCount, cancelCount, dateTime, userName, comment, orders) => |
| | 0 | 105 | | { |
| | 0 | 106 | | var numbers = orders |
| | 0 | 107 | | .Select(o => o.ShipmentNumber) |
| | 0 | 108 | | .Where(x => !string.IsNullOrWhiteSpace(x)) |
| | 0 | 109 | | .Select(x => x!.Trim()) |
| | 0 | 110 | | .Distinct(StringComparer.OrdinalIgnoreCase) |
| | 0 | 111 | | .ToList(); |
| | 0 | 112 | | |
| | 0 | 113 | | var ordersHtml = numbers.Count > 0 |
| | 0 | 114 | | ? $"<div class='small mt-2'><strong>Проведённые заказы:</strong> {string.Join(", ", numbers)}</div>" |
| | 0 | 115 | | : "<div class='small mt-2 text-muted'>Проведённые заказы не определены.</div>"; |
| | 0 | 116 | | |
| | 0 | 117 | | return TransactionConfig.BuildStandardSuccessMessage( |
| | 0 | 118 | | changeCount, |
| | 0 | 119 | | cancelCount, |
| | 0 | 120 | | dateTime, |
| | 0 | 121 | | userName, |
| | 0 | 122 | | comment, |
| | 0 | 123 | | orders, |
| | 0 | 124 | | "Возврат покупателя", |
| | 0 | 125 | | additionalHtml: ordersHtml, |
| | 0 | 126 | | explicitOrderNumbers: numbers); |
| | 0 | 127 | | }, |
| | 0 | 128 | | CreateTransactionMethod = (tm, orders, userName, createAt, comment) => |
| | 0 | 129 | | tm.CreateCustomerReturnTransaction(orders, userName, createAt, comment) |
| | 0 | 130 | | }; |
| | | 131 | | |
| | | 132 | | private static bool IsMarketplaceCancelled(Order order) |
| | | 133 | | { |
| | 0 | 134 | | if (order == null) |
| | | 135 | | { |
| | 0 | 136 | | return false; |
| | | 137 | | } |
| | | 138 | | |
| | 0 | 139 | | var status = (order.Status ?? string.Empty).Trim(); |
| | 0 | 140 | | var clientStatus = (order.ClientStatus?.Name ?? string.Empty).Trim(); |
| | | 141 | | |
| | 0 | 142 | | bool cancelledInStatus = |
| | 0 | 143 | | status.Equals("Отменён", StringComparison.OrdinalIgnoreCase) || |
| | 0 | 144 | | status.Equals("Отменен", StringComparison.OrdinalIgnoreCase); |
| | | 145 | | |
| | 0 | 146 | | bool cancelledInClientStatus = |
| | 0 | 147 | | clientStatus.Equals("Отменён", StringComparison.OrdinalIgnoreCase) || |
| | 0 | 148 | | clientStatus.Equals("Отменен", StringComparison.OrdinalIgnoreCase); |
| | | 149 | | |
| | 0 | 150 | | return cancelledInStatus || cancelledInClientStatus; |
| | | 151 | | } |
| | | 152 | | |
| | | 153 | | /// <summary>Возврат покупателя после загрузки отчёта (UploadFile): все заказы одним списком, только смена статуса.< |
| | | 154 | | public async Task<IActionResult> CreateCustomerReturnTransactionFromFile(string ids, int page) |
| | | 155 | | { |
| | 0 | 156 | | ViewData["CustomerReturnBulkFromFile"] = true; |
| | 0 | 157 | | var result = await CreateTransactionBase(ids, page, GetCustomerReturnFromFileTransactionConfig()); |
| | 0 | 158 | | return ApplyAlreadyReturnedOrdersWarning(result); |
| | 0 | 159 | | } |
| | | 160 | | |
| | | 161 | | public async Task<IActionResult> CreateCustomerReturnTransaction(string ids, int page) |
| | | 162 | | { |
| | 0 | 163 | | var result = await CreateTransactionBase(ids, page); |
| | 0 | 164 | | if (result is not ViewResult { Model: MultiplayEditOrderViewModel pageViewModel } || pageViewModel.Orders.Count |
| | | 165 | | { |
| | 0 | 166 | | return result; |
| | | 167 | | } |
| | | 168 | | |
| | 0 | 169 | | result = ApplyAlreadyReturnedOrdersWarning(result); |
| | 0 | 170 | | if (result is not ViewResult { Model: MultiplayEditOrderViewModel filteredModel } || filteredModel.Orders.Count |
| | | 171 | | { |
| | 0 | 172 | | return result; |
| | | 173 | | } |
| | 0 | 174 | | pageViewModel = filteredModel; |
| | | 175 | | |
| | 0 | 176 | | var isAdmin = User.IsInRole("Admin"); |
| | 0 | 177 | | ViewData["WarehouseSelectorTitle"] = "Укажите склады для проведения возврата"; |
| | 0 | 178 | | ViewData["ForceSenderWarehouse"] = !isAdmin; |
| | | 179 | | |
| | 0 | 180 | | var bitrixWarehouseNames = (ViewData["BitrixWarehousesList"] as List<string>) ?? new List<string>(); |
| | 0 | 181 | | var senderResolution = await _warehouseSelectionResolver.ResolveSenderWarehouseAsync(pageViewModel.Orders); |
| | 0 | 182 | | var warehouseBlockingErrors = new List<string>(); |
| | | 183 | | |
| | 0 | 184 | | if (!string.IsNullOrWhiteSpace(senderResolution.SenderWarehouseName)) |
| | | 185 | | { |
| | 0 | 186 | | ViewData["FixedSenderWarehouse"] = senderResolution.SenderWarehouseName; |
| | 0 | 187 | | ViewData["PreferredSenderWarehouse"] = senderResolution.SenderWarehouseName; |
| | | 188 | | |
| | 0 | 189 | | if (!bitrixWarehouseNames.Contains(senderResolution.SenderWarehouseName, StringComparer.OrdinalIgnoreCase) & |
| | | 190 | | { |
| | 0 | 191 | | warehouseBlockingErrors.Add( |
| | 0 | 192 | | $"Склад отправителя '{senderResolution.SenderWarehouseName}', полученный из настроек клиента Ozon, н |
| | | 193 | | } |
| | | 194 | | } |
| | 0 | 195 | | else if (senderResolution.HasErrors && !isAdmin) |
| | | 196 | | { |
| | 0 | 197 | | warehouseBlockingErrors.AddRange(senderResolution.Errors); |
| | | 198 | | } |
| | | 199 | | |
| | 0 | 200 | | var recipientByOrderResolution = |
| | 0 | 201 | | await _warehouseSelectionResolver.ResolveCustomerReturnRecipientByOrderAsync(pageViewModel.Orders); |
| | | 202 | | // Для CustomerReturn ошибки автоподстановки по заказам не блокируют форму: |
| | | 203 | | // пользователь может выбрать конечный склад вручную из доступного списка. |
| | 0 | 204 | | if (recipientByOrderResolution.HasBlockingErrors) |
| | | 205 | | { |
| | 0 | 206 | | ViewData["WarehouseRecipientRuleInfo"] = string.Join(" ", recipientByOrderResolution.BlockingErrors); |
| | | 207 | | } |
| | | 208 | | |
| | 0 | 209 | | var preferredSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); |
| | 0 | 210 | | if (recipientByOrderResolution.RecipientByOrderId.Count > 0) |
| | | 211 | | { |
| | 0 | 212 | | preferredSet = recipientByOrderResolution.RecipientByOrderId.Values.ToHashSet(StringComparer.OrdinalIgnoreCa |
| | 0 | 213 | | bitrixWarehouseNames = bitrixWarehouseNames |
| | 0 | 214 | | .OrderBy(n => preferredSet.Contains(n) ? 0 : 1) |
| | | 215 | | .ThenBy(n => n) |
| | 0 | 216 | | .ToList(); |
| | | 217 | | } |
| | | 218 | | |
| | 0 | 219 | | if (!isAdmin) |
| | | 220 | | { |
| | 0 | 221 | | if (senderResolution.HasErrors) |
| | | 222 | | { |
| | 0 | 223 | | ViewData["WarehouseSenderRuleError"] = string.Join("<br/>", senderResolution.Errors); |
| | | 224 | | } |
| | | 225 | | |
| | 0 | 226 | | if (!string.IsNullOrWhiteSpace(senderResolution.SenderWarehouseName)) |
| | | 227 | | { |
| | 0 | 228 | | bitrixWarehouseNames = bitrixWarehouseNames |
| | 0 | 229 | | .Where(n => !string.Equals(n, senderResolution.SenderWarehouseName, StringComparison.OrdinalIgnoreCa |
| | | 230 | | .OrderBy(n => n) |
| | 0 | 231 | | .ToList(); |
| | | 232 | | } |
| | | 233 | | } |
| | | 234 | | |
| | 0 | 235 | | var preferredRecipient = bitrixWarehouseNames.FirstOrDefault(n => preferredSet.Contains(n)); |
| | 0 | 236 | | if (!string.IsNullOrWhiteSpace(preferredRecipient)) |
| | | 237 | | { |
| | 0 | 238 | | ViewData["PreferredRecipientWarehouse"] = preferredRecipient; |
| | | 239 | | } |
| | | 240 | | |
| | 0 | 241 | | if (warehouseBlockingErrors.Count > 0) |
| | | 242 | | { |
| | 0 | 243 | | ViewData["WarehouseSelectionBlocked"] = true; |
| | 0 | 244 | | ViewData["WarehouseSenderRuleError"] = string.Join("<br/>", |
| | 0 | 245 | | warehouseBlockingErrors.Distinct(StringComparer.OrdinalIgnoreCase)); |
| | | 246 | | } |
| | | 247 | | |
| | 0 | 248 | | ViewData["BitrixWarehousesList"] = bitrixWarehouseNames; |
| | 0 | 249 | | ViewData["RecipientByOrder"] = recipientByOrderResolution.RecipientByOrderId; |
| | 0 | 250 | | ViewData["RecipientExplanationByOrder"] = recipientByOrderResolution.RecipientExplanationByOrderId; |
| | 0 | 251 | | ViewData["ReturnWarehouseOrderIds"] = recipientByOrderResolution.ReturnWarehouseOrderIds; |
| | 0 | 252 | | return result; |
| | 0 | 253 | | } |
| | | 254 | | |
| | | 255 | | private IActionResult ApplyAlreadyReturnedOrdersWarning(IActionResult result) |
| | | 256 | | { |
| | 0 | 257 | | if (result is not ViewResult { Model: MultiplayEditOrderViewModel pageViewModel } || pageViewModel.Orders.Count |
| | 0 | 258 | | return result; |
| | | 259 | | |
| | 0 | 260 | | var alreadyReturned = pageViewModel.Orders |
| | 0 | 261 | | .Where(o => string.Equals(o.AppStatus?.Name, "Возврат покупателя", StringComparison.OrdinalIgnoreCase)) |
| | 0 | 262 | | .ToList(); |
| | 0 | 263 | | if (!alreadyReturned.Any()) |
| | 0 | 264 | | return result; |
| | | 265 | | |
| | 0 | 266 | | var alreadyReturnedIds = alreadyReturned.Select(o => o.Id).ToHashSet(); |
| | 0 | 267 | | pageViewModel.Orders = pageViewModel.Orders |
| | 0 | 268 | | .Where(o => !alreadyReturnedIds.Contains(o.Id)) |
| | 0 | 269 | | .ToList(); |
| | | 270 | | |
| | 0 | 271 | | var numbers = alreadyReturned |
| | 0 | 272 | | .Select(o => o.ShipmentNumber) |
| | 0 | 273 | | .Where(x => !string.IsNullOrWhiteSpace(x)) |
| | 0 | 274 | | .Select(x => x!.Trim()) |
| | 0 | 275 | | .Distinct(StringComparer.OrdinalIgnoreCase) |
| | 0 | 276 | | .ToList(); |
| | | 277 | | |
| | 0 | 278 | | if (numbers.Count > 0) |
| | | 279 | | { |
| | 0 | 280 | | ViewData["TransactionWarningMessage"] = |
| | 0 | 281 | | "<div class='alert alert-warning mb-2'><strong>Часть заказов пропущена</strong><br/>" + |
| | 0 | 282 | | "Уже находятся в статусе «Возврат покупателя» и не будут перепроведены.<br/>" + |
| | 0 | 283 | | $"Заказы: {string.Join(", ", numbers)}</div>"; |
| | | 284 | | } |
| | | 285 | | else |
| | | 286 | | { |
| | 0 | 287 | | ViewData["TransactionWarningMessage"] = |
| | 0 | 288 | | "<div class='alert alert-warning mb-2'><strong>Часть заказов пропущена</strong><br/>" + |
| | 0 | 289 | | "Уже находятся в статусе «Возврат покупателя» и не будут перепроведены.</div>"; |
| | | 290 | | } |
| | | 291 | | |
| | 0 | 292 | | return result; |
| | | 293 | | } |
| | | 294 | | |
| | | 295 | | [HttpPost] |
| | | 296 | | [ValidateAntiForgeryToken] |
| | | 297 | | public Task<IActionResult> CreateCustomerReturnTransaction( |
| | | 298 | | List<Order>? orders, string? userName, string? comment, |
| | | 299 | | int page, string? deletedOrders, Dictionary<int, string?>? orderSupplierMap, |
| | | 300 | | string? warehouseSender, string? warehouseRecipient, Dictionary<int, string>? recipientByOrder, |
| | | 301 | | bool processIn1C = false, bool bulkFromFile = false) |
| | | 302 | | { |
| | 0 | 303 | | if (bulkFromFile) |
| | | 304 | | { |
| | 0 | 305 | | processIn1C = false; |
| | | 306 | | } |
| | | 307 | | |
| | 0 | 308 | | var safeOrders = orders ?? new List<Order>(); |
| | 0 | 309 | | var safeUserName = userName ?? (User.Identity?.Name ?? "unknown"); |
| | 0 | 310 | | var safeComment = comment ?? string.Empty; |
| | 0 | 311 | | var safeDeletedOrders = deletedOrders ?? string.Empty; |
| | 0 | 312 | | var safeOrderSupplierMap = orderSupplierMap ?? new Dictionary<int, string?>(); |
| | | 313 | | |
| | 0 | 314 | | if (!processIn1C) |
| | | 315 | | { |
| | 0 | 316 | | return CreateTransactionPostBase( |
| | 0 | 317 | | safeOrders, safeUserName, safeComment, page, safeDeletedOrders, safeOrderSupplierMap, false, null, null) |
| | | 318 | | } |
| | | 319 | | |
| | 0 | 320 | | return CreateCustomerReturnTransactionWithWarehouseValidation( |
| | 0 | 321 | | safeOrders, safeUserName, safeComment, page, safeDeletedOrders, safeOrderSupplierMap, warehouseSender, wareh |
| | | 322 | | } |
| | | 323 | | |
| | | 324 | | /// <summary> |
| | | 325 | | /// Упрощённый POST для сценария UploadFile (возвраты): только смена статуса без 1С/складов. |
| | | 326 | | /// </summary> |
| | | 327 | | [HttpPost] |
| | | 328 | | [ValidateAntiForgeryToken] |
| | | 329 | | public Task<IActionResult> CreateCustomerReturnTransactionFromFileStatusOnly( |
| | | 330 | | List<Order>? orders, |
| | | 331 | | string? userName, |
| | | 332 | | string? comment, |
| | | 333 | | int page, |
| | | 334 | | string? deletedOrders) |
| | | 335 | | { |
| | 0 | 336 | | var safeOrders = orders ?? new List<Order>(); |
| | 0 | 337 | | var safeUserName = userName ?? (User.Identity?.Name ?? "unknown"); |
| | 0 | 338 | | var safeComment = comment ?? string.Empty; |
| | 0 | 339 | | var safeDeletedOrders = deletedOrders ?? string.Empty; |
| | 0 | 340 | | var deletedIds = DeletedOrderIds.Parse(safeDeletedOrders); |
| | 0 | 341 | | var ordersForFilter = deletedIds.Count == 0 |
| | 0 | 342 | | ? safeOrders |
| | 0 | 343 | | : safeOrders.Where(o => !deletedIds.Contains(o.Id)).ToList(); |
| | | 344 | | |
| | 0 | 345 | | ApplyShipmentFilterCookie(ordersForFilter); |
| | | 346 | | |
| | 0 | 347 | | return CreateTransactionPostBase( |
| | 0 | 348 | | safeOrders, |
| | 0 | 349 | | safeUserName, |
| | 0 | 350 | | safeComment, |
| | 0 | 351 | | page, |
| | 0 | 352 | | safeDeletedOrders, |
| | 0 | 353 | | new Dictionary<int, string?>(), |
| | 0 | 354 | | processIn1C: false, |
| | 0 | 355 | | warehouseSender: null, |
| | 0 | 356 | | warehouseRecipient: null); |
| | | 357 | | } |
| | | 358 | | |
| | | 359 | | private void ApplyShipmentFilterCookie(IEnumerable<Order>? orders) |
| | | 360 | | { |
| | 0 | 361 | | var shipmentNumbers = orders? |
| | 0 | 362 | | .Select(o => o.ShipmentNumber?.Trim()) |
| | 0 | 363 | | .Where(n => !string.IsNullOrWhiteSpace(n)) |
| | 0 | 364 | | .Select(n => n!) |
| | 0 | 365 | | .Distinct(StringComparer.OrdinalIgnoreCase) |
| | 0 | 366 | | .ToList() ?? new List<string>(); |
| | | 367 | | |
| | 0 | 368 | | var filterData = new OrderFilterModel |
| | 0 | 369 | | { |
| | 0 | 370 | | ShipmentNumber = shipmentNumbers.Any() ? string.Join(",", shipmentNumbers) : null, |
| | 0 | 371 | | GroupByShipment = true, |
| | 0 | 372 | | IncludeArchived = false, |
| | 0 | 373 | | SyncClientStatusesOnLoad = false, |
| | 0 | 374 | | OnlyWithZasyl = false |
| | 0 | 375 | | }; |
| | | 376 | | |
| | 0 | 377 | | Response.Cookies.Append("FilterData", JsonConvert.SerializeObject(filterData)); |
| | 0 | 378 | | } |
| | | 379 | | |
| | | 380 | | private async Task<IActionResult> CreateCustomerReturnTransactionWithWarehouseValidation( |
| | | 381 | | List<Order> orders, |
| | | 382 | | string userName, |
| | | 383 | | string? comment, |
| | | 384 | | int page, |
| | | 385 | | string? deletedOrders, |
| | | 386 | | Dictionary<int, string?>? orderSupplierMap, |
| | | 387 | | string? warehouseSender, |
| | | 388 | | string? warehouseRecipient, |
| | | 389 | | Dictionary<int, string>? recipientByOrder) |
| | | 390 | | { |
| | | 391 | | try |
| | | 392 | | { |
| | 0 | 393 | | if (OneCDataManager == null) |
| | 0 | 394 | | throw new TransactionException(TransactionErrorCode.OneCUnavailable, "Интеграция с 1С недоступна."); |
| | | 395 | | |
| | 0 | 396 | | var candidateOrders = orders ?? new List<Order>(); |
| | 0 | 397 | | if (!string.IsNullOrWhiteSpace(deletedOrders)) |
| | | 398 | | { |
| | 0 | 399 | | var deletedOrderIds = DeletedOrderIds.Parse(deletedOrders); |
| | 0 | 400 | | candidateOrders = candidateOrders.Where(o => !deletedOrderIds.Contains(o.Id)).ToList(); |
| | | 401 | | } |
| | | 402 | | |
| | 0 | 403 | | var bitrixWarehouses = await OneCDataManager.GetWarehouseListAsync(); |
| | 0 | 404 | | var bitrixWarehouseNames = bitrixWarehouses |
| | | 405 | | .Select(w => w.Name) |
| | | 406 | | .Where(n => !string.IsNullOrWhiteSpace(n)) |
| | | 407 | | .Select(n => n!) |
| | | 408 | | .OrderBy(n => n) |
| | 0 | 409 | | .ToList(); |
| | 0 | 410 | | bitrixWarehouseNames = await FilterBitrixWarehouseNamesByScope(bitrixWarehouseNames); |
| | | 411 | | |
| | 0 | 412 | | var isAdmin = User.IsInRole("Admin"); |
| | 0 | 413 | | var senderResolution = await _warehouseSelectionResolver.ResolveSenderWarehouseAsync(candidateOrders); |
| | 0 | 414 | | var blockingErrors = new List<string>(); |
| | | 415 | | |
| | 0 | 416 | | if (!isAdmin && (senderResolution.HasErrors || string.IsNullOrWhiteSpace(senderResolution.SenderWarehouseNam |
| | | 417 | | { |
| | 0 | 418 | | blockingErrors.AddRange(senderResolution.Errors); |
| | 0 | 419 | | if (!senderResolution.HasErrors) |
| | 0 | 420 | | blockingErrors.Add("Не удалось определить склад отправителя по правилу 'склад покупателя'."); |
| | | 421 | | } |
| | 0 | 422 | | else if (!isAdmin && !bitrixWarehouseNames.Contains(senderResolution.SenderWarehouseName, StringComparer.Ord |
| | | 423 | | { |
| | 0 | 424 | | blockingErrors.Add($"Склад отправителя '{senderResolution.SenderWarehouseName}', полученный из настроек |
| | | 425 | | } |
| | | 426 | | |
| | 0 | 427 | | if (blockingErrors.Count > 0) |
| | 0 | 428 | | throw new TransactionException(TransactionErrorCode.OneCSenderWarehouseMissing, string.Join("<br/>", blo |
| | | 429 | | |
| | 0 | 430 | | if (!isAdmin) |
| | 0 | 431 | | warehouseSender = senderResolution.SenderWarehouseName; |
| | | 432 | | |
| | 0 | 433 | | if (string.IsNullOrWhiteSpace(warehouseSender)) |
| | 0 | 434 | | throw new TransactionException(TransactionErrorCode.OneCSenderWarehouseMissing, "Не указан склад отправи |
| | | 435 | | |
| | 0 | 436 | | if (!bitrixWarehouseNames.Contains(warehouseSender.Trim(), StringComparer.OrdinalIgnoreCase)) |
| | 0 | 437 | | throw new TransactionException(TransactionErrorCode.OneCSenderWarehouseMissing, "Склад отправителя недос |
| | | 438 | | |
| | 0 | 439 | | var postedRecipientByOrder = recipientByOrder ?? new Dictionary<int, string>(); |
| | | 440 | | var candidateOrderIds = candidateOrders.Select(o => o.Id).ToHashSet(); |
| | 0 | 441 | | var invalidOrderIds = postedRecipientByOrder.Keys.Where(id => !candidateOrderIds.Contains(id)).ToList(); |
| | 0 | 442 | | if (invalidOrderIds.Count > 0) |
| | 0 | 443 | | throw new TransactionException(TransactionErrorCode.OneCRecipientWarehouseMissing, "Переданы некорректны |
| | | 444 | | |
| | 0 | 445 | | foreach (var order in candidateOrders) |
| | | 446 | | { |
| | 0 | 447 | | if (!postedRecipientByOrder.TryGetValue(order.Id, out var recipient) || string.IsNullOrWhiteSpace(recipi |
| | 0 | 448 | | throw new TransactionException(TransactionErrorCode.OneCRecipientWarehouseMissing, $"Не указан склад |
| | | 449 | | |
| | 0 | 450 | | if (!bitrixWarehouseNames.Contains(recipient, StringComparer.OrdinalIgnoreCase)) |
| | 0 | 451 | | throw new TransactionException(TransactionErrorCode.OneCRecipientWarehouseMissing, $"Склад получател |
| | | 452 | | |
| | 0 | 453 | | if (string.Equals(warehouseSender.Trim(), recipient.Trim(), StringComparison.OrdinalIgnoreCase)) |
| | 0 | 454 | | throw new TransactionException(TransactionErrorCode.OneCWarehousesMustDiffer, $"Склад отправителя и |
| | | 455 | | } |
| | | 456 | | |
| | 0 | 457 | | return await CreateTransactionPostBase( |
| | 0 | 458 | | orders ?? new List<Order>(), |
| | 0 | 459 | | userName, |
| | 0 | 460 | | comment, |
| | 0 | 461 | | page, |
| | 0 | 462 | | deletedOrders, |
| | 0 | 463 | | orderSupplierMap, |
| | 0 | 464 | | processIn1C: true, |
| | 0 | 465 | | warehouseSender: warehouseSender.Trim(), |
| | 0 | 466 | | warehouseRecipient: warehouseRecipient?.Trim(), |
| | 0 | 467 | | recipientWarehouseByOrder: postedRecipientByOrder); |
| | | 468 | | } |
| | 0 | 469 | | catch (TransactionException ex) |
| | | 470 | | { |
| | 0 | 471 | | TempData["TransactionErrorCode"] = ex.Code.ToString(); |
| | 0 | 472 | | TempData["ErorrResult"] = ex.Message; |
| | 0 | 473 | | return Json(new { redirectTo = Url.Action("Index", "Orders", new { sortOrder = GetSortStateCookie(), page }) |
| | | 474 | | } |
| | 0 | 475 | | } |
| | | 476 | | } |