< Summary

Information
Class: OzonOrdersWeb.Areas.Studio2.Controllers.Transactions.ShippedToCommissionerTransactionController
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/Transactions/ShippedToCommissionerTransactionController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 575
Coverable lines: 575
Total lines: 1007
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 282
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/Transactions/ShippedToCommissionerTransactionController.cs

#LineLine coverage
 1using Microsoft.AspNetCore.Authorization;
 2using Microsoft.AspNetCore.Mvc;
 3using Microsoft.AspNetCore.Mvc.Rendering;
 4using Microsoft.EntityFrameworkCore;
 5using Microsoft.Extensions.DependencyInjection;
 6using Newtonsoft.Json;
 7using OServices.FiltersServices.FilterModels;
 8using OzonDomains;
 9using OzonDomains.Models;
 10using OzonOrdersWeb.Areas.Studio2.Controllers.Base;
 11using OzonOrdersWeb.Areas.Studio2.Helpers;
 12using OzonOrdersWeb.Areas.Studio2.Models.Transactions;
 13using OzonOrdersWeb.Areas.Studio2.ViewModels;
 14using OzonOrdersWeb.ViewModels.OrderViewModels;
 15using OzonRepositories.Context;
 16using PartsInfo.HttpUtils;
 17using Services.ApiServices;
 18using Services.ApiServices._1CApi;
 19using Services.BackgroundJobsServices;
 20using Services.ApiServices._1CApi.Models;
 21using Services.ApiServices._1CApi.Stock;
 22using Services.CacheServices.Cache.OzonOrdersCache;
 23using Services.CacheServices.Cache.UserCacheService;
 24using Services.DataServices;
 25using Services.FiltersServices.SortModels;
 26using OzonOrdersWeb.WebServices.Currency;
 27using Services.SignalRServices;
 28using Services.TransactionUtilsServices;
 29using OzonOrdersWeb.TransactionServices;
 30using Services.CacheServices.Cache;
 31
 32namespace OzonOrdersWeb.Areas.Studio2.Controllers.Transactions;
 33
 34[Authorize(Roles = "User,Admin")]
 35[Area("Studio2")]
 36[Route("Studio2/ShippedToCommissionerTransaction")]
 37public class ShippedToCommissionerTransactionController : Controller
 38{
 39    private const string CommissionerStatusName = "Отгружен комиссионеру";
 40    private const string ShippedToSellerStatusName = "Отгружен реализатором";
 41    private const string CommissionerStatusColor = "#0d6efd";
 042    private static readonly TimeZoneInfo RussianTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time");
 43
 44    private readonly ApplicationDbContext _context;
 45    private readonly AppStatusDataServices _appStatusServices;
 46    private readonly OrdersDataServices _orderServices;
 47    private readonly TransactionManager _transaction;
 48    private readonly TransactionCache _transactionCache;
 49    private readonly SupplierDataServices _supplierDataServices;
 50    private readonly IUserCacheService _userCacheService;
 51    private readonly ICurrencyRatesViewBagService _currencyRatesViewBagService;
 52    private readonly CacheUpdater<Order> _cacheUpdater;
 53    private readonly OrderCache _cache;
 54    private readonly WarehouseMappingDataServices _warehouseMappingDataServices;
 55    private readonly OneCTransferManager _oneCTransferManager;
 56    private readonly ProxyHttpClientService _proxyHttpClientService;
 57    private readonly WarehouseDataServices _warehouseDataServices;
 58    private readonly OneCDataManager _oneCDataManager;
 59    private readonly StatusTransitionDataServices _statusTransitionDataServices;
 60
 061    public ShippedToCommissionerTransactionController(
 062        ApplicationDbContext context,
 063        AppStatusDataServices appStatusServices,
 064        OrdersDataServices orderServices,
 065        TransactionManager transaction,
 066        TransactionCache transactionCache,
 067        SupplierDataServices supplierDataServices,
 068        IUserCacheService userCacheService,
 069        ICurrencyRatesViewBagService currencyRatesViewBagService,
 070        CacheUpdater<Order> cacheUpdater,
 071        OrderCache cache,
 072        WarehouseMappingDataServices warehouseMappingDataServices,
 073        OneCTransferManager oneCTransferManager,
 074        ProxyHttpClientService proxyHttpClientService,
 075        WarehouseDataServices warehouseDataServices,
 076        OneCDataManager oneCDataManager,
 077        StatusTransitionDataServices statusTransitionDataServices)
 78    {
 079        _context = context;
 080        _appStatusServices = appStatusServices;
 081        _orderServices = orderServices;
 082        _transaction = transaction;
 083        _transactionCache = transactionCache;
 084        _supplierDataServices = supplierDataServices;
 085        _userCacheService = userCacheService;
 086        _currencyRatesViewBagService = currencyRatesViewBagService;
 087        _cacheUpdater = cacheUpdater;
 088        _cache = cache;
 089        _warehouseMappingDataServices = warehouseMappingDataServices;
 090        _oneCTransferManager = oneCTransferManager;
 091        _proxyHttpClientService = proxyHttpClientService;
 092        _warehouseDataServices = warehouseDataServices;
 093        _oneCDataManager = oneCDataManager;
 094        _statusTransitionDataServices = statusTransitionDataServices;
 095    }
 96
 97    [HttpGet]
 98    [Route("CreateShippedToCommissionerTransactionFromFile")]
 99    public async Task<IActionResult> CreateShippedToCommissionerTransactionFromFile(string ids, int page, DateTime? oneC
 100    {
 0101        var effectiveOneCOperationDate = GetEffectiveOneCOperationDate(oneCOperationDate);
 0102        var targetStatus = await EnsureCommissionerStatusAsync();
 0103        var cancelledStatus = await _appStatusServices.GetAppStatusAsync(new AppStatus { Name = "Отменен" });
 104
 0105        var filteredStatuses = new List<AppStatus> { targetStatus };
 0106        if (cancelledStatus != null)
 107        {
 0108            filteredStatuses.Add(cancelledStatus);
 109        }
 110
 111        filteredStatuses = filteredStatuses.GroupBy(s => s.Id).Select(g => g.First()).ToList();
 0112        ViewBag.AppStatuses = new SelectList(filteredStatuses, "Id", "Name");
 113        ViewBag.StatusColors = filteredStatuses.ToDictionary(s => s.Id.ToString(), s => s.GetStatusColor());
 114
 0115        if (string.IsNullOrWhiteSpace(ids))
 0116            return BadRequest("Не выбраны заказы для транзакции.");
 117
 0118        var idArray = ids.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();
 0119        if (idArray.Length == 0)
 0120            return BadRequest("Не выбраны заказы для транзакции.");
 121
 0122        var orders = await _orderServices.GetOrdersNoTracking(idArray);
 0123        var ordersToTransaction = new List<Order>();
 0124        var orderStatusWarnings = new Dictionary<int, string>();
 125
 0126        foreach (var order in orders)
 127        {
 0128            if (order == null)
 129            {
 130                continue;
 131            }
 132
 0133            if (order.Status != "Отменен покупателем")
 134            {
 0135                ordersToTransaction.Add(order);
 136            }
 137        }
 138
 0139        if (ordersToTransaction.Any())
 140        {
 0141            var groupedByWarehouse = ordersToTransaction
 142                .Select(o => new
 143                {
 144                    Order = o,
 145                    EffectiveWarehouse = TransactionWarehouseHelper.GetEffectiveWarehouseName(o)
 146                })
 147                .GroupBy(x => string.IsNullOrWhiteSpace(x.EffectiveWarehouse)
 148                    ? "Склад не определен"
 149                    : x.EffectiveWarehouse!)
 150                .OrderBy(g => g.Key)
 0151                .ToList();
 152
 0153            var pages = new List<WarehouseTransactionPage>();
 154
 0155            foreach (var group in groupedByWarehouse)
 156            {
 0157                var warehouseName = group.Key;
 158                var groupOrders = group.Select(x => x.Order).ToList();
 159                var groupIds = string.Join(",", groupOrders.Select(x => x.Id));
 0160                var missingWarehouse = warehouseName == "Склад не определен";
 0161                var warnings = new List<string>();
 0162                var availableOrders = groupOrders
 163                    .Where(o => !string.Equals(o.AppStatus?.Name, CommissionerStatusName, StringComparison.OrdinalIgnore
 0164                    .ToList();
 0165                var processedOrdersCount = groupOrders.Count - availableOrders.Count;
 166                var statusOnlyOrdersCount = availableOrders.Count(o => IsShippedToSeller(o.AppStatus?.Name));
 167                var availableGroupIds = string.Join(",", availableOrders.Select(x => x.Id));
 0168                if (missingWarehouse)
 169                {
 0170                    warnings.Add("У группы не определен эффективный склад");
 171                }
 172
 0173                if (processedOrdersCount > 0)
 174                {
 0175                    warnings.Add(processedOrdersCount == groupOrders.Count
 0176                        ? "Все заказы в группе уже имеют статус 'Отгружен комиссионеру'"
 0177                        : $"Уже проведено заказов: {processedOrdersCount}. В страницу попадут только остальные.");
 178                }
 179
 0180                if (statusOnlyOrdersCount > 0)
 181                {
 0182                    warnings.Add(statusOnlyOrdersCount == availableOrders.Count
 0183                        ? "Все доступные заказы в статусе 'Отгружен реализатором': они только сменят статус без проведен
 0184                        : $"Без проведения 'Отгружен комиссионеру' будут только сменены статусы заказов: {statusOnlyOrde
 185                }
 186
 0187                var canOpen = !missingWarehouse && availableOrders.Count > 0;
 0188                var url = canOpen
 0189                    ? $"/Studio2/ShippedToCommissionerTransaction/CreateShippedToCommissionerTransaction?ids={Uri.Escape
 0190                    : string.Empty;
 191
 0192                pages.Add(new WarehouseTransactionPage
 0193                {
 0194                    WarehouseName = warehouseName,
 0195                    Url = url,
 0196                    OrdersCount = groupOrders.Count,
 0197                    AvailableOrdersCount = availableOrders.Count,
 0198                    ProcessedOrdersCount = processedOrdersCount,
 0199                    StatusOnlyOrdersCount = statusOnlyOrdersCount,
 0200                    HasWarnings = warnings.Count > 0,
 0201                    WarningText = string.Join(". ", warnings),
 0202                    CanOpen = canOpen,
 0203                    GroupKey = $"shipped-to-commissioner:{warehouseName}:{groupIds}",
 0204                    PreviewOrders = groupOrders
 205                        .OrderBy(o => o.ShipmentNumber)
 206                        .Select(o => new WarehouseTransactionPreviewOrder
 207                        {
 208                            Id = o.Id,
 209                            ShipmentNumber = o.ShipmentNumber ?? string.Empty,
 210                            Article = o.Article ?? string.Empty,
 211                            StatusName = o.AppStatus?.Name ?? o.Status ?? "Без статуса",
 212                            StatusColor = o.AppStatus?.GetStatusColor() ?? "#e9ecef",
 213                            IsAlreadyProcessed = string.Equals(o.AppStatus?.Name, CommissionerStatusName, StringComparis
 214                            IsStatusOnlyChange = IsShippedToSeller(o.AppStatus?.Name)
 215                        })
 0216                        .ToList()
 0217                });
 218            }
 219
 0220            if (pages.Any())
 221            {
 0222                var model = new MultipleTransactionPagesViewModel
 0223                {
 0224                    WarehousePages = pages,
 0225                    OneCOperationDate = effectiveOneCOperationDate
 0226                };
 227
 0228                return View("MultipleTransactionPagesByWarehouse", model);
 229            }
 230        }
 231
 0232        return await CreateShippedToCommissionerTransaction(ids, page, effectiveOneCOperationDate);
 0233    }
 234
 235    [HttpGet]
 236    [Route("CreateShippedToCommissionerTransactionFromFileView")]
 237    public async Task<IActionResult> CreateShippedToCommissionerTransactionFromFileView(string ids, int page, DateTime? 
 238    {
 0239        await Task.CompletedTask;
 0240        if (string.IsNullOrWhiteSpace(ids))
 0241            return BadRequest("Не выбраны заказы для транзакции.");
 242
 0243        var effectiveOneCOperationDate = GetEffectiveOneCOperationDate(oneCOperationDate);
 0244        return Redirect($"/Studio2/ShippedToCommissionerTransaction/CreateShippedToCommissionerTransaction?ids={Uri.Esca
 0245    }
 246
 247    [HttpGet]
 248    [Route("CreateShippedToCommissionerTransaction")]
 249    public async Task<IActionResult> CreateShippedToCommissionerTransaction(string ids, int page, DateTime? oneCOperatio
 250    {
 0251        var effectiveOneCOperationDate = GetEffectiveOneCOperationDate(oneCOperationDate);
 0252        var transitions = await _statusTransitionDataServices.GetByTransactionType(TransactionType.ShippedToCommissioner
 0253        var targetStatus = await EnsureCommissionerStatusAsync();
 0254        HashSet<int>? validSourceStatusIds = null;
 255
 0256        if (transitions.Any())
 257        {
 258            validSourceStatusIds = transitions.Select(t => t.FromStatusId).ToHashSet();
 0259            targetStatus = transitions.First().ToStatus ?? targetStatus;
 0260            await EnsureStatusColorAsync(targetStatus);
 261        }
 262
 0263        var cancelledStatus = await _appStatusServices.GetAppStatusAsync(new AppStatus { Name = "Отменен" });
 0264        var filteredStatuses = new List<AppStatus> { targetStatus };
 0265        if (cancelledStatus != null)
 266        {
 0267            filteredStatuses.Add(cancelledStatus);
 268        }
 269
 270        filteredStatuses = filteredStatuses.GroupBy(s => s.Id).Select(g => g.First()).ToList();
 0271        ViewBag.AppStatuses = new SelectList(filteredStatuses, "Id", "Name");
 272        ViewBag.StatusColors = filteredStatuses.ToDictionary(s => s.Id.ToString(), s => s.GetStatusColor());
 273
 0274        if (string.IsNullOrWhiteSpace(ids))
 0275            return BadRequest("Не выбраны заказы для транзакции.");
 276
 0277        var idArray = ids.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();
 0278        if (idArray.Length == 0)
 0279            return BadRequest("Не выбраны заказы для транзакции.");
 280
 0281        var permittedWarehouses = (await _warehouseMappingDataServices.GetWarehouseMappings())
 282            .Select(wm => wm.OzonWarehouseName)
 283            .Where(n => !string.IsNullOrWhiteSpace(n))
 0284            .Select(WarehouseNameNormalizer.Normalize)
 0285            .OfType<string>()
 0286            .ToHashSet(StringComparer.OrdinalIgnoreCase);
 0287        string? errorReason = null;
 0288        var errorCode = TransactionErrorCode.Unknown;
 289
 0290        var orders = await _orderServices.GetOrdersNoTracking(idArray);
 0291        var ordersToTransaction = new List<Order>();
 0292        var orderStatusWarnings = new Dictionary<int, string>();
 0293        var warningMessages = new List<string>();
 0294        var invalidStatusNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
 0295        foreach (var order in orders)
 296        {
 0297            var allowed = validSourceStatusIds != null
 0298                ? order.AppStatusId.HasValue && validSourceStatusIds.Contains(order.AppStatusId.Value)
 0299                : order.Status != "Отменен покупателем";
 300
 0301            if (!allowed)
 302            {
 0303                var currentStatusName = string.IsNullOrWhiteSpace(order.AppStatus?.Name)
 0304                    ? "без статуса"
 0305                    : order.AppStatus!.Name;
 0306                invalidStatusNames.Add(currentStatusName);
 0307                var warning = $"Статус '{currentStatusName}' не соответствует правилам перехода для этой транзакции. Про
 0308                warningMessages.Add($"Заказ {order.ShipmentNumber}: {warning}");
 0309                orderStatusWarnings[order.Id] = warning;
 0310                order.UpdatedBy = User.Identity?.Name;
 0311                ordersToTransaction.Add(order);
 0312                continue;
 313            }
 314
 0315            if (IsShippedToSeller(order.AppStatus?.Name))
 316            {
 0317                orderStatusWarnings[order.Id] = "Статус будет изменен без проведения документа 'Отгружен комиссионеру'."
 318            }
 319
 0320            if (order.AppStatus?.Name != "Отменен")
 321            {
 0322                order.AppStatus = targetStatus;
 323            }
 324
 0325            order.UpdatedBy = User.Identity?.Name;
 326
 0327            ordersToTransaction.Add(order);
 328        }
 329
 330        // Склад-отправитель для предпросмотра остатков: в разметке строки стоит «эффективный» склад
 331        // заказа, а документ уйдёт со склада из маппинга — проверять надо именно его.
 0332        ViewData["StockCheckSenderByOrderId"] = TransactionStockCheckLineBuilder.BuildStockCheckSenderMap(
 0333            ordersToTransaction, await _warehouseMappingDataServices.GetWarehouseMappings());
 334
 0335        if (warningMessages.Any())
 336        {
 0337            var invalidStatusesSummary = invalidStatusNames.Any()
 0338                ? $"<div class='mb-2'><strong>Не подходят входящие статусы:</strong> {string.Join(", ", invalidStatusNam
 0339                : string.Empty;
 0340            ViewData["TransactionWarningMessage"] = invalidStatusesSummary + BuildSkippedOrdersWarningHtml(warningMessag
 341        }
 342        else
 343        {
 0344            ViewData["TransactionWarningMessage"] = null;
 345        }
 346
 0347        if (!ordersToTransaction.Any())
 348        {
 0349            errorReason ??= validSourceStatusIds != null
 0350                ? "Нет заказов с допустимым статусом для этой транзакции."
 0351                : "Нет заказов, доступных для отгрузки комиссионеру.";
 0352            if (errorCode == TransactionErrorCode.Unknown)
 353            {
 0354                errorCode = TransactionErrorCode.NoOrdersForTransaction;
 355            }
 356        }
 357        else
 358        {
 0359            var invalidWarehouses = ordersToTransaction
 0360                .Where(o =>
 0361                {
 0362                    var effectiveWarehouse = WarehouseNameNormalizer.Normalize(
 0363                        TransactionWarehouseHelper.GetEffectiveWarehouseName(o));
 0364                    return string.IsNullOrWhiteSpace(effectiveWarehouse) || !permittedWarehouses.Contains(effectiveWareh
 0365                })
 0366                .ToList();
 367
 0368            if (invalidWarehouses.Any())
 369            {
 0370                var invalidWarehousesMessage = "Выбраны заказы с недопустимыми эффективными складами: " +
 371                                               string.Join(", ", invalidWarehouses.Select(o => $"{o.ShipmentNumber} ({Tr
 0372                var allowedWarehousesMessage = "Допустимые склады: " + string.Join(", ", permittedWarehouses);
 0373                errorReason = invalidWarehousesMessage + ". " + allowedWarehousesMessage;
 0374                ordersToTransaction.Clear();
 0375                errorCode = TransactionErrorCode.MixedWarehouses;
 376            }
 377            else
 378            {
 0379                var distinctWarehouses = ordersToTransaction
 380                    .GroupBy(o => WarehouseNameNormalizer.Normalize(
 381                        TransactionWarehouseHelper.GetEffectiveWarehouseName(o)))
 0382                    .ToList();
 0383                if (distinctWarehouses.Count > 1)
 384                {
 0385                    errorReason = "Выбраны заказы с разными эффективными складами: " +
 386                                  string.Join(", ", distinctWarehouses.SelectMany(g => g.Select(o => $"{o.ShipmentNumber
 0387                    ordersToTransaction.Clear();
 0388                    errorCode = TransactionErrorCode.MixedWarehouses;
 389                }
 390            }
 391        }
 392
 0393        ViewData["TransactionErrorCode"] = errorCode.ToString();
 0394        ViewData["ErrorReason"] = errorReason;
 395
 0396        ViewData["TransactionConditionsMessage"] = null;
 397
 0398        var userTask = _userCacheService.GetCachedUserAsync(User);
 0399        var rateTasks = _currencyRatesViewBagService.StartFetch();
 0400        var zasylTask = _oneCDataManager.GetWarehouseListAsync();
 401
 0402        var suppliers = await _supplierDataServices.GetSuppliers();
 0403        var warehouses = await _warehouseDataServices.GetWarehouses();
 404
 405        var transactionOrders = ordersToTransaction.OrderBy(ot => ot.ShipmentNumber).ToList();
 0406        var articles = await _orderServices.GetArticleCountsForOrdersAsync(transactionOrders);
 0407        var cities = await _orderServices.GetDeliveryCityCountsForOrdersAsync(transactionOrders);
 0408        var numbers = await _orderServices.GetShipmentBaseCountsForOrdersAsync(transactionOrders);
 409
 0410        await Task.WhenAll(userTask, rateTasks.Usd, rateTasks.Eur, rateTasks.Byn, zasylTask);
 411
 0412        var rates = rateTasks.ToSnapshot();
 0413        var pageViewModel = new MultiplayEditOrderViewModel
 0414        {
 0415            RedirectPage = page,
 0416            OneCOperationDate = effectiveOneCOperationDate,
 0417            Orders = transactionOrders,
 0418            User = userTask.Result!,
 419            Suppliers = suppliers.OrderBy(s => s.Name).ToList(),
 0420            RateUSD = rates.RateUsd,
 0421            RateEUR = rates.RateEur,
 0422            RateBYN = rates.RateByn,
 0423            UniqueArticles = articles,
 0424            UniqueDeliveryCitys = cities,
 0425            UniqueNumbers = numbers,
 0426            OrderStatusWarnings = orderStatusWarnings,
 0427            AppStatus = targetStatus,
 428            ZasylValues = zasylTask.Result.Select(w => w.Name).Where(n => !string.IsNullOrWhiteSpace(n)).Select(n => n!)
 0429            Warehouses = warehouses,
 0430        };
 0431        await AttachTrackingKeyAsync(pageViewModel, TransactionType.ShippedToCommissioner);
 432
 0433        if (pageViewModel.User.AccessProfileId != null)
 434        {
 0435            pageViewModel.User.AccessProfile = _context.AccessProfiles.Find(pageViewModel.User.AccessProfileId);
 436        }
 437
 0438        if (pageViewModel.User.AccessProfile?.AvailableOrderColumns?.Contains("Себестоимость") == true && ordersToTransa
 439        {
 0440            await _orderServices.LoadExtraExpensesForOrders(ordersToTransaction);
 441        }
 442
 0443        return View(pageViewModel);
 0444    }
 445
 446    [HttpPost]
 447    [ValidateAntiForgeryToken]
 448    [Route("CreateShippedToCommissionerTransaction")]
 449    public async Task<IActionResult> CreateShippedToCommissionerTransaction(
 450        List<Order> orders,
 451        string userName,
 452        string? comment,
 453        int page,
 454        string? deletedOrders,
 455        Dictionary<int, string?>? orderSupplierMap,
 456        bool processIn1C = false,
 457        DateTime? oneCOperationDate = null)
 458    {
 0459        var aiFlow = TransactionCommentAiSchedulingHelper.BeginFlow(
 0460            comment,
 0461            TransactionType.ShippedToCommissioner,
 0462            userName,
 0463            processIn1C);
 0464        comment = aiFlow.UserCommentOnly;
 0465        var trackingKey = Request.Form["TrackingKey"].FirstOrDefault();
 0466        var oneCOrganizationInn = Request.Form["oneCOrganizationInn"].FirstOrDefault();
 0467        await _orderServices.SetZasylAsync(orderSupplierMap ?? new Dictionary<int, string?>());
 0468        var missingOrdersResult = TransactionErrorResponseHelper.EnsureOrdersProvided(this, orders, () => RedirectToActi
 0469        if (missingOrdersResult != null)
 470        {
 0471            return missingOrdersResult;
 472        }
 473
 0474        ArgumentNullException.ThrowIfNull(orders);
 475
 0476        if (!string.IsNullOrWhiteSpace(deletedOrders))
 477        {
 0478            var deletedOrderIds = DeletedOrderIds.Parse(deletedOrders);
 0479            orders = orders.Where(o => !deletedOrderIds.Contains(o.Id)).ToList();
 480        }
 481
 0482        foreach (var order in orders)
 483        {
 0484            if (order.ExtraExpenses != null && order.ExtraExpenses.Any())
 485            {
 0486                order.ExtraExpenses = order.ExtraExpenses
 487                    .Where(e => e != null && e.ExpenseTypeId > 0 && e.Amount > 0)
 0488                    .ToList();
 0489                if (!order.ExtraExpenses.Any())
 490                {
 0491                    order.ExtraExpenses = [];
 492                }
 493            }
 494        }
 495
 0496        var stockCheck = StockCheckResult.Empty;
 0497        List<WarehouseMapping>? warehouseMappings = null;
 498
 499        try
 500        {
 0501            var processedOrdersForRedirect = new List<Order>();
 0502            var postTransitions = await _statusTransitionDataServices.GetByTransactionType(TransactionType.ShippedToComm
 0503            var targetStatus = await EnsureCommissionerStatusAsync();
 0504            if (postTransitions.Any() && postTransitions.First().ToStatus != null)
 505            {
 0506                targetStatus = postTransitions.First().ToStatus!;
 0507                await EnsureStatusColorAsync(targetStatus);
 508            }
 509
 510            var validTargetStatusIds = postTransitions.Any() ? postTransitions.Select(t => t.ToStatusId).ToHashSet() : (
 511
 0512            if (orders.Count > 0)
 513            {
 0514                var createAt = DateTime.Now;
 515                var orderIds = orders.Select(o => o.Id).Distinct().ToList();
 0516                var orderStatusInfo = await _context.Orders.AsNoTracking()
 0517                    .Where(o => orderIds.Contains(o.Id))
 0518                    .Select(o => new { o.Id, FromStatusId = o.AppStatusId ?? 0, FromStatusName = o.AppStatus != null ? o
 519                    .ToDictionaryAsync(x => x.Id, x => x);
 520                var fromStatusByOrderId = orderStatusInfo.ToDictionary(x => x.Key, x => x.Value.FromStatusId);
 0521                var statusOnlySourceOrderIds = orderStatusInfo
 522                    .Where(x => IsShippedToSeller(x.Value.FromStatusName))
 523                    .Select(x => x.Key)
 0524                    .ToHashSet();
 525
 0526                var ordersToUpdate = new List<Order>();
 0527                var originalStatuses = new Dictionary<int, AppStatus?>();
 0528                var statusOnlyOrderIds = new HashSet<int>();
 529
 530                // Остатки спрашиваем до изменения статусов: при нехватке 1С создаст перемещение,
 531                // но откажет в проведении, не указав проблемные позиции. Набор заказов здесь —
 532                // зеркало фильтров ordersToTransaction ниже.
 0533                if (processIn1C)
 534                {
 0535                    warehouseMappings ??= await _warehouseMappingDataServices.GetWarehouseMappings();
 0536                    var stockCandidates = orders
 0537                        .Where(o => o.Quantity > 0 && !statusOnlySourceOrderIds.Contains(o.Id))
 0538                        .ToList();
 0539                    var stockOrders = await TransactionStockCheckLineBuilder.LoadForStockCheckAsync(
 540                        _context, stockCandidates.Select(o => o.Id));
 541
 0542                    stockCheck = await OneCStockGate.RunAsync(
 0543                        HttpContext,
 0544                        TransactionStockCheckLineBuilder.FromWarehouseMappings(
 0545                            stockOrders,
 0546                            warehouseMappings,
 0547                            orderSupplierMap: null,
 0548                            formQuantities: TransactionStockCheckLineBuilder.QuantitiesFromForm(stockCandidates)));
 0549                    await OneCStockGate.ReportAsync(HttpContext, stockCheck, "Отгружен комиссионеру");
 550
 0551                }
 552
 0553                var submittedFields = SubmittedOrderFields.FromForm(Request.HasFormContentType ? Request.Form : null);
 0554                foreach (var order in orders)
 555                {
 0556                    var orderToUpdate = await _orderServices.TransactOrder(order, submittedFields.For(order.Id));
 0557                    originalStatuses[orderToUpdate.Id] = orderToUpdate.AppStatus;
 558
 0559                if (orderToUpdate.AppStatus?.Name != "Отменен")
 560                {
 0561                    orderToUpdate.AppStatus = targetStatus;
 562                }
 563
 0564                    ordersToUpdate.Add(orderToUpdate);
 0565                    if (statusOnlySourceOrderIds.Contains(order.Id))
 566                    {
 0567                        statusOnlyOrderIds.Add(orderToUpdate.Id);
 568                    }
 0569                }
 570
 0571                var cancelledStatus = await _appStatusServices.GetAppStatusAsync(new AppStatus { Name = "Отменен" });
 0572                var ordersToTransaction = validTargetStatusIds != null
 0573                    ? ordersToUpdate.Where(o => o.AppStatus != null && validTargetStatusIds.Contains(o.AppStatus.Id) && 
 0574                    : ordersToUpdate.Where(o => ((targetStatus != null && o.AppStatus?.Id == targetStatus.Id) || (cancel
 0575                var statusOnlyOrders = ordersToTransaction
 0576                    .Where(o => statusOnlyOrderIds.Contains(o.Id))
 0577                    .ToList();
 0578                ordersToTransaction = ordersToTransaction
 0579                    .Where(o => !statusOnlyOrderIds.Contains(o.Id))
 0580                    .ToList();
 0581                var blockingOrderIds = OneCStockGate.GetBlockingOrderIds(stockCheck);
 0582                var ordersForOneC = ordersToTransaction.Where(o => !blockingOrderIds.Contains(o.Id)).ToList();
 0583                var ordersForOneCDraft = ordersToTransaction.Where(o => blockingOrderIds.Contains(o.Id)).ToList();
 0584                processedOrdersForRedirect = ordersToTransaction.Concat(statusOnlyOrders).ToList();
 0585                var processedShipmentNumbers = ordersToTransaction
 586                    .Select(o => o.ShipmentNumber)
 587                    .Where(n => !string.IsNullOrWhiteSpace(n))
 588                    .Select(n => n!.Trim())
 0589                    .Distinct(StringComparer.OrdinalIgnoreCase)
 0590                    .ToList();
 591
 0592                if (!ordersToTransaction.Any())
 593                {
 0594                    if (statusOnlyOrders.Any())
 595                    {
 0596                        await _cacheUpdater.Update(_cache);
 0597                        await _transactionCache.Update();
 0598                        var statusOnlyNumbers = string.Join(", ", statusOnlyOrders
 599                            .Select(o => o.ShipmentNumber)
 600                            .Where(n => !string.IsNullOrWhiteSpace(n))
 0601                            .Distinct(StringComparer.OrdinalIgnoreCase));
 0602                        TempData["TransactionResult"] = $"<div class='alert alert-info'><strong>Статус изменен без прове
 0603                        return Json(new { redirectTo = BuildOrdersIndexRedirectUrl(page, statusOnlyOrders) });
 604                    }
 605
 0606                    TempData["TransactionResult"] = "<div class='alert alert-warning'><strong>Нет подходящих заказов для
 0607                    return Json(new { redirectTo = Url.Action("Index", "Orders", new { sortOrder = GetSortStateCookie(),
 608                }
 609
 610                string oneCResult;
 0611                IEnumerable<string> oneCHash = [];
 0612                List<OneCResponse> transferResult = [];
 0613                List<OneCResponse> draftTransferResult = [];
 614
 0615                if (processIn1C && ordersForOneC.Any())
 616                {
 617                    try
 618                    {
 0619                        warehouseMappings ??= await _warehouseMappingDataServices.GetWarehouseMappings();
 0620                        orderSupplierMap ??= new Dictionary<int, string?>();
 0621                        foreach (var order in ordersForOneC)
 622                        {
 0623                            var mappedSender = ShippedToCommissionerWarehouseResolver.ResolveSenderWarehouse(
 0624                                order,
 0625                                warehouseMappings);
 0626                            if (!string.IsNullOrWhiteSpace(mappedSender))
 627                            {
 0628                                orderSupplierMap[order.Id] = mappedSender;
 629                            }
 630                        }
 631
 632                        var transferSupplierMap = orderSupplierMap?.ToDictionary(kv => kv.Key, kv => kv.Value ?? string.
 0633                        transferResult = await _oneCTransferManager.TransferStockByWarehousesWithReversedWarehouses(
 0634                            ordersForOneC,
 0635                            TransactionType.ShippedToCommissioner,
 0636                            transferSupplierMap,
 0637                            oneCOperationDate,
 0638                            userName,
 0639                            orgInnOverride: oneCOrganizationInn);
 0640                    }
 0641                    catch (Exception ex)
 642                    {
 0643                        await RestoreOriginalStatusesAsync(ordersToUpdate, originalStatuses);
 0644                        TempData["TransactionResult"] = $"<div class='alert alert-danger'><strong>⚠️ Ошибка обработки в 
 0645                        return Json(new { redirectTo = BuildOrdersIndexRedirectUrl(page, ordersToTransaction) });
 646                    }
 647
 648                    var successfulTransfers = transferResult.Where(tr => tr.Success).ToList();
 649                    var failedTransfers = transferResult.Where(tr => !tr.Success).ToList();
 0650                    if (failedTransfers.Count > 0)
 651                    {
 0652                        await RestoreOriginalStatusesAsync(ordersToUpdate, originalStatuses);
 653                        var errorMessages = failedTransfers.Select(tr => tr.Message).Distinct().Take(3);
 0654                        var errorCount = failedTransfers.Count;
 0655                        var successCount = successfulTransfers.Count;
 0656                        var totalCount = transferResult.Count;
 657
 0658                        TempData["TransactionResult"] = $"<div class='alert alert-warning'><strong>⚠️ Частичный результа
 0659                                                        $"Успешно: {successCount} из {totalCount} документов<br/>" +
 0660                                                        $"С ошибками: {errorCount} документов<br/>" +
 0661                                                        $"<small>Ошибки: {string.Join("; ", errorMessages)}" +
 0662                                                        (errorCount > 3 ? "..." : "") + "</small></div>";
 0663                        return Json(new { redirectTo = BuildOrdersIndexRedirectUrl(page, ordersToTransaction) });
 664                    }
 665
 0666                    oneCHash = successfulTransfers
 667                        .Select(h => h.TransactionId)
 668                        .Where(id => id != null)
 669                        .Select(id => id!);
 0670                    if (ordersForOneCDraft.Any())
 671                    {
 0672                        var draftSupplierMap = ordersForOneCDraft.ToDictionary(
 673                            o => o.Id,
 0674                            o => orderSupplierMap != null && orderSupplierMap.TryGetValue(o.Id, out var warehouse)
 0675                                ? warehouse ?? string.Empty
 0676                                : string.Empty);
 0677                        draftTransferResult = await _oneCTransferManager.TransferStockByWarehousesWithReversedWarehouses
 0678                            ordersForOneCDraft, TransactionType.ShippedToCommissioner, draftSupplierMap,
 0679                            oneCOperationDate, userName, oneCOrganizationInn, true,
 0680                            "недостаточно остатка в 1С. Проведите после оприходования товара.");
 0681                        transferResult.AddRange(draftTransferResult);
 682                    }
 0683                    oneCResult = OneCTransactionSummaryHelper.WrapSection("Проведённые документы 1С",
 0684                                     OneCTransactionSummaryHelper.BuildAlertSummary(ordersForOneC, successfulTransfers))
 0685                                 + OneCTransactionSummaryHelper.WrapSection("Черновики 1С по дефицитным позициям",
 0686                                     OneCTransactionSummaryHelper.BuildAlertSummary(ordersForOneCDraft, draftTransferRes
 687                                 + OneCStockGate.PartialPostingNotice(stockCheck, ordersForOneC.Count, draftTransferResu
 0688                                 + OneCStockGate.SkippedNotice(stockCheck);
 0689                }
 0690                else if (processIn1C && ordersForOneCDraft.Any())
 691                {
 0692                    warehouseMappings ??= await _warehouseMappingDataServices.GetWarehouseMappings();
 693                    var map = ordersForOneCDraft.ToDictionary(o => o.Id, o => ShippedToCommissionerWarehouseResolver.Res
 0694                    draftTransferResult = await _oneCTransferManager.TransferStockByWarehousesWithReversedWarehouses(
 0695                        ordersForOneCDraft, TransactionType.ShippedToCommissioner, map, oneCOperationDate, userName,
 0696                        oneCOrganizationInn, true, "недостаточно остатка в 1С. Проведите после оприходования товара.");
 0697                    transferResult.AddRange(draftTransferResult);
 0698                    oneCResult = OneCTransactionSummaryHelper.WrapSection("Черновики 1С по дефицитным позициям",
 0699                                     OneCTransactionSummaryHelper.BuildAlertSummary(ordersForOneCDraft, draftTransferRes
 700                                 + OneCStockGate.PartialPostingNotice(stockCheck, 0, draftTransferResult.Any(r => r.Succ
 701                }
 702                else
 703                {
 0704                    oneCResult = OneCStockGate.NoDocumentNotice(stockCheck);
 705                }
 706
 0707                await NotificationService.NotifyAllAsync(oneCResult);
 0708                var historyWindowStart = DateTime.Now;
 0709                var commentWithOneC = comment;
 0710                TransactionCommentAiSchedulingHelper.AppendOneCCommentDetails(
 0711                    aiFlow,
 0712                    ref commentWithOneC,
 0713                    ordersToTransaction,
 0714                    transferResult);
 0715                var commentForDb = TransactionCommentAiSchedulingHelper.ResolveDatabaseComment(aiFlow, commentWithOneC);
 716
 0717                var (changeCount, dateTime, transactionId) = await _transaction.CreateShippedToCommissionerTransaction(
 0718                    ordersToTransaction,
 0719                    userName,
 0720                    createAt,
 0721                    commentForDb);
 722                var cancelCount = ordersToTransaction.Count(o => o.AppStatus?.Name == "Отменен");
 723
 0724                if (changeCount > 0)
 725                {
 0726                    if (processIn1C && transferResult != null && transferResult.Count > 0)
 727                    {
 0728                        var historySender = ordersToTransaction
 0729                            .Select(o => orderSupplierMap != null &&
 0730                                         orderSupplierMap.TryGetValue(o.Id, out var w)
 0731                                ? w
 0732                                : null)
 733                            .Where(w => !string.IsNullOrWhiteSpace(w))
 0734                            .Distinct(StringComparer.OrdinalIgnoreCase)
 0735                            .ToList();
 0736                        var historyRecipient = ordersToTransaction
 737                            .Select(o => o.OzonClient?.WarehouseName?.Trim())
 738                            .FirstOrDefault(w => !string.IsNullOrWhiteSpace(w));
 739
 0740                        await OrderHistoryOneCPostingRecorder.TryRecordMovementAsync(
 0741                            HttpContext.RequestServices,
 0742                            ordersForOneC,
 743                            transferResult.Where(r => !r.IsDraft).ToList(),
 0744                            userName,
 0745                            transactionId > 0 ? (int?)transactionId : null,
 0746                            historyWindowStart,
 0747                            historySender.Count > 0 ? string.Join(", ", historySender) : null,
 0748                            historyRecipient);
 749                    }
 750
 0751                    var statusTransitionHistoryService = HttpContext.RequestServices.GetService<IStatusTransitionHistory
 0752                    if (postTransitions.Any() && statusTransitionHistoryService != null)
 753                    {
 0754                        var user = await _userCacheService.GetCachedUserAsync(User);
 0755                        await statusTransitionHistoryService.RecordStatusTransitionHistoryAsync(
 0756                            TransactionType.ShippedToCommissioner,
 0757                            ordersToTransaction,
 0758                            fromStatusByOrderId,
 0759                            postTransitions,
 0760                            commentForDb,
 0761                            user?.AccessProfileId,
 0762                            createAt,
 0763                            null);
 764                    }
 765
 0766                    await _cacheUpdater.Update(_cache);
 767                    var statusNameForMessage = string.Join(", ", ordersToTransaction.Select(o => o.AppStatus?.Name).Wher
 0768                    if (string.IsNullOrEmpty(statusNameForMessage) && postTransitions.Any())
 769                    {
 770                        statusNameForMessage = string.Join(", ", postTransitions.Select(t => t.ToStatus?.Name).Where(n =
 771                    }
 0772                    if (string.IsNullOrEmpty(statusNameForMessage))
 773                    {
 0774                        statusNameForMessage = CommissionerStatusName;
 775                    }
 776
 0777                    var msg = TransactionConfig.BuildStandardSuccessMessage(
 0778                        changeCount,
 0779                        cancelCount,
 0780                        dateTime,
 0781                        userName,
 0782                        commentForDb,
 0783                        ordersToTransaction,
 0784                        statusNameForMessage,
 0785                        oneCResult,
 0786                        processedShipmentNumbers);
 0787                    if (statusOnlyOrders.Any())
 788                    {
 0789                        var statusOnlyNumbers = string.Join(", ", statusOnlyOrders
 790                            .Select(o => o.ShipmentNumber)
 791                            .Where(n => !string.IsNullOrWhiteSpace(n))
 0792                            .Distinct(StringComparer.OrdinalIgnoreCase));
 0793                        msg += $"<div class='alert alert-info mt-2'><strong>Без проведения документа 'Отгружен комиссион
 794                    }
 0795                    await NotificationService.NotifyAllAsync(msg);
 0796                    TempData["TransactionResult"] = msg;
 0797                    await CompleteTrackingAsync(trackingKey, transactionId, ordersToTransaction.Count, commentForDb);
 798
 0799                    if (aiFlow.ScheduleAiComment && aiFlow.Builder != null && transactionId > 0)
 800                    {
 0801                        await TransactionCommentAiSchedulingHelper.TryScheduleAsync(
 0802                            HttpContext.RequestServices,
 0803                            aiFlow.Builder,
 0804                            true,
 0805                            (int)transactionId,
 0806                            ordersToTransaction,
 0807                            fromStatusByOrderId,
 0808                            _context,
 0809                            ct: HttpContext.RequestAborted);
 810                    }
 0811                }
 812
 0813                await _transactionCache.Update();
 0814            }
 815
 0816            ClearSelectedIdsSession();
 0817            await _orderServices.SetZasylAsync(orderSupplierMap ?? new Dictionary<int, string?>());
 0818            foreach (var order in orders)
 819            {
 0820                var cookieKey = $"PurchasePrice_{order.Id}";
 0821                if (Request.Cookies.ContainsKey(cookieKey))
 822                {
 0823                    Response.Cookies.Delete(cookieKey);
 824                }
 825            }
 826
 0827            return Json(new { redirectTo = BuildOrdersIndexRedirectUrl(page, processedOrdersForRedirect) });
 828        }
 0829        catch (TransactionException ex)
 830        {
 0831            TempData["TransactionErrorCode"] = ex.Code.ToString();
 0832            TempData["ErorrResult"] = ex.Message;
 0833            await _orderServices.SetZasylAsync(orderSupplierMap ?? new Dictionary<int, string?>());
 0834            return Json(new { redirectTo = Url.Action("Index", "Orders", new { sortOrder = GetSortStateCookie(), page })
 835        }
 0836        catch (Exception ex)
 837        {
 0838            TempData["TransactionErrorCode"] = TransactionErrorCode.Unknown.ToString();
 0839            TempData["ErorrResult"] = $"Не удалось провести выбранные заказы ({ex.Message})";
 0840            await _orderServices.SetZasylAsync(orderSupplierMap ?? new Dictionary<int, string?>());
 0841            return Json(new { redirectTo = Url.Action("Index", "Orders", new { sortOrder = GetSortStateCookie(), page })
 842        }
 0843    }
 844
 845    private async Task<AppStatus> EnsureCommissionerStatusAsync()
 846    {
 0847        var status = await _appStatusServices.GetAppStatusAsync(new AppStatus { Name = CommissionerStatusName });
 0848        if (status == null)
 849        {
 0850            status = new AppStatus { Name = CommissionerStatusName };
 0851            _context.AppStatuses.Add(status);
 0852            await _context.SaveChangesAsync();
 853        }
 854
 0855        await EnsureStatusColorAsync(status);
 0856        return status;
 0857    }
 858
 859    private static bool IsShippedToSeller(string? statusName)
 860    {
 0861        return string.Equals(statusName, ShippedToSellerStatusName, StringComparison.OrdinalIgnoreCase);
 862    }
 863
 864    private async Task EnsureStatusColorAsync(AppStatus status)
 865    {
 0866        if (!string.Equals(status.StatusColor?.ColorCode, CommissionerStatusColor, StringComparison.OrdinalIgnoreCase))
 867        {
 0868            await _appStatusServices.UpdateStatusColor(status.Id, CommissionerStatusColor);
 0869            status.StatusColor = new StatusColor { ColorCode = CommissionerStatusColor };
 870        }
 0871    }
 872
 873    private async Task RestoreOriginalStatusesAsync(IEnumerable<Order> orders, Dictionary<int, AppStatus?> originalStatu
 874    {
 0875        foreach (var order in orders)
 876        {
 0877            if (originalStatuses.TryGetValue(order.Id, out var originalStatus))
 878            {
 0879                order.AppStatus = originalStatus;
 880            }
 881        }
 882
 0883        await _orderServices.UpdateOrders(orders.ToList());
 0884    }
 885
 886    private string BuildOrdersIndexRedirectUrl(int page, IEnumerable<Order>? ordersToTransaction)
 887    {
 0888        var shipmentNumbers = ordersToTransaction?
 0889            .Select(o => o.ShipmentNumber?.Trim())
 0890            .Where(n => !string.IsNullOrWhiteSpace(n))
 0891            .Distinct(StringComparer.OrdinalIgnoreCase)
 0892            .ToList() ?? [];
 893
 0894        var filterData = new OrderFilterModel
 0895        {
 0896            ShipmentNumber = shipmentNumbers.Any() ? string.Join(",", shipmentNumbers) : null,
 0897            GroupByShipment = true,
 0898            IncludeArchived = false,
 0899            SyncClientStatusesOnLoad = false,
 0900            OnlyWithZasyl = false
 0901        };
 902
 0903        Response.Cookies.Append("FilterData", JsonConvert.SerializeObject(filterData));
 904
 0905        return Url.Action("Index", "Orders", new
 0906               {
 0907                   sortOrder = GetSortStateCookie(),
 0908                   page
 0909               })
 0910               ?? "/Studio2/Orders";
 911    }
 912
 913    private static string? BuildSkippedOrdersWarningHtml(IEnumerable<string> warningMessages)
 914    {
 0915        var warnings = warningMessages
 0916            .Where(m => !string.IsNullOrWhiteSpace(m))
 0917            .Distinct(StringComparer.OrdinalIgnoreCase)
 0918            .ToList();
 919
 0920        if (!warnings.Any())
 921        {
 0922            return null;
 923        }
 924
 0925        return "<ul class='mb-0'>" + string.Join("", warnings.Select(w => $"<li>{System.Net.WebUtility.HtmlEncode(w)}</l
 926    }
 927
 928    private static DateTime GetDefaultOneCOperationDate()
 929    {
 0930        return TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, RussianTimeZone);
 931    }
 932
 933    private static DateTime GetEffectiveOneCOperationDate(DateTime? oneCOperationDate)
 934    {
 0935        return oneCOperationDate ?? GetDefaultOneCOperationDate();
 936    }
 937
 938    private void RunBackgroundCacheTask(List<Order> orders)
 939    {
 0940        _ = Task.Run(async () =>
 0941        {
 0942            foreach (var order in orders)
 0943            {
 0944                try
 0945                {
 0946                    if (order.EtProducer != null && !string.IsNullOrEmpty(order.Article))
 0947                    {
 0948                        _ = await _proxyHttpClientService.GetJsonAsync($"https://api.interparts.ru/detail-full-info/{ord
 0949                        _ = await _proxyHttpClientService.GetJsonAsync($"https://api.interparts.ru/product-information/p
 0950                        Console.WriteLine($"Кэширован для проведения: {order.EtProducer.Name}/{order.Article}");
 0951                    }
 0952                }
 0953                catch (Exception ex)
 0954                {
 0955                    Console.WriteLine($"Ошибка в фоне: {ex.Message}");
 0956                }
 0957            }
 0958        });
 0959    }
 960
 961    [HttpPost]
 962    public IActionResult ClearSelectedIdsSession()
 963    {
 0964        HttpContext.Session.SetString("selectedIds", JsonConvert.SerializeObject(new List<int>()));
 0965        return Ok();
 966    }
 967
 968    private async Task AttachTrackingKeyAsync(MultiplayEditOrderViewModel model, TransactionType transactionType)
 969    {
 0970        if (model.Orders == null || !model.Orders.Any())
 0971            return;
 0972        var trackingService = HttpContext.RequestServices.GetService<ITransactionTimeTrackingService>();
 0973        if (trackingService == null)
 0974            return;
 0975        model.TrackingKey = await trackingService.StartSessionAsync(
 0976            User.Identity?.Name ?? "unknown",
 0977            model.User?.AccessProfileId,
 0978            transactionType,
 979            model.Orders.Select(o => o.Id),
 0980            $"{Request.Path}{Request.QueryString}",
 0981            HttpContext.Connection.RemoteIpAddress?.ToString(),
 0982            Request.Headers.UserAgent.ToString());
 0983    }
 984
 985    private async Task CompleteTrackingAsync(string? trackingKey, long transactionId, int ordersCount, string? comment)
 986    {
 0987        if (string.IsNullOrWhiteSpace(trackingKey) || transactionId <= 0)
 0988            return;
 0989        var trackingService = HttpContext.RequestServices.GetService<ITransactionTimeTrackingService>();
 0990        if (trackingService == null)
 0991            return;
 0992        await trackingService.CompleteSessionAsync(
 0993            trackingKey,
 0994            transactionId,
 0995            ordersCount,
 0996            comment,
 0997            $"/Studio2/Transaction/GetTransaction?transactionId={transactionId}");
 0998    }
 999
 1000    private OrderSortState GetSortStateCookie()
 1001    {
 01002        var cookie = Request.Cookies["SortState"];
 01003        if (!string.IsNullOrEmpty(cookie) && Enum.TryParse<OrderSortState>(cookie, out var saved))
 01004            return saved;
 01005        return OrderSortState.StandardState;
 1006    }
 1007}

Methods/Properties

.cctor()
.ctor(OzonRepositories.Context.ApplicationDbContext,Services.DataServices.AppStatusDataServices,Services.DataServices.OrdersDataServices,Services.TransactionUtilsServices.TransactionManager,Services.CacheServices.Cache.OzonOrdersCache.TransactionCache,Services.DataServices.SupplierDataServices,Services.CacheServices.Cache.UserCacheService.IUserCacheService,OzonOrdersWeb.WebServices.Currency.ICurrencyRatesViewBagService,Services.CacheServices.Cache.CacheUpdater`1<OzonDomains.Models.Order>,Services.CacheServices.Cache.OzonOrdersCache.OrderCache,Services.DataServices.WarehouseMappingDataServices,Services.ApiServices._1CApi.OneCTransferManager,PartsInfo.HttpUtils.ProxyHttpClientService,Services.DataServices.WarehouseDataServices,Services.ApiServices.OneCDataManager,Services.DataServices.StatusTransitionDataServices)
CreateShippedToCommissionerTransactionFromFile()
CreateShippedToCommissionerTransactionFromFileView()
CreateShippedToCommissionerTransaction()
CreateShippedToCommissionerTransaction()
EnsureCommissionerStatusAsync()
IsShippedToSeller(System.String)
EnsureStatusColorAsync()
RestoreOriginalStatusesAsync()
BuildOrdersIndexRedirectUrl(System.Int32,System.Collections.Generic.IEnumerable`1<OzonDomains.Models.Order>)
BuildSkippedOrdersWarningHtml(System.Collections.Generic.IEnumerable`1<System.String>)
GetDefaultOneCOperationDate()
GetEffectiveOneCOperationDate(System.Nullable`1<System.DateTime>)
RunBackgroundCacheTask(System.Collections.Generic.List`1<OzonDomains.Models.Order>)
ClearSelectedIdsSession()
AttachTrackingKeyAsync()
CompleteTrackingAsync()
GetSortStateCookie()