< Summary

Information
Class: OzonOrdersWeb.Areas.Studio2.Controllers.Transactions.CancelledTransactionController
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/Transactions/CancelledTransactionController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 297
Coverable lines: 297
Total lines: 529
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 170
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/CancelledTransactionController.cs

#LineLine coverage
 1using System.Net;
 2using System.Text;
 3using Microsoft.AspNetCore.Mvc;
 4using Microsoft.EntityFrameworkCore;
 5using OzonDomains;
 6using OzonDomains.Models;
 7using OzonOrdersWeb.Areas.Studio2.Controllers.Base;
 8using OzonOrdersWeb.ViewModels.OrderViewModels;
 9using OzonRepositories.Context;
 10using Services.ApiServices.OzonApi;
 11using Services.ApiServices.OzonApi.Filters;
 12using Services.CacheServices.Cache.OzonOrdersCache;
 13using Services.CacheServices.Cache.UserCacheService;
 14using Services.DataServices;
 15using OzonOrdersWeb.WebServices.Currency;
 16using Services.TransactionUtilsServices;
 17using Services.CacheServices.Cache;
 18using SlqStudio.Application.Services.EmailService;
 19using Services.OzonWebhookServices;
 20
 21namespace OzonOrdersWeb.Areas.Studio2.Controllers.Transactions;
 22
 23/// <summary>
 24/// Контроллер транзакции "Отменен"
 25/// </summary>
 26public class CancelledTransactionController : BaseTransactionController
 27{
 28    private readonly IHttpClientFactory _httpClientFactory;
 29    private readonly IEmailService? _emailService;
 30    private readonly OzonClientServices _ozonClientServices;
 031    private readonly List<OzonCancelSuccessEntry> _ozonCancelSuccessEntries = new();
 32    private readonly IOzonPostingCancellationService _cancellationService;
 33
 34    public CancelledTransactionController(
 35        ApplicationDbContext context,
 36        AppStatusDataServices appStatusServices,
 37        OrdersDataServices orderServices,
 38        TransactionManager transactionManager,
 39        TransactionCache transactionCache,
 40        SupplierDataServices supplierDataServices,
 41        IUserCacheService userCacheService,
 42        ICurrencyRatesViewBagService currencyRatesViewBagService,
 43        CacheUpdater<Order> cacheUpdater,
 44        OrderCache orderCache,
 45        WarehouseDataServices warehouseDataServices,
 46        ExpenseTypeDataServices expenseTypeDataServices,
 47        OrderExtraExpenseDataServices orderExtraExpenseDataServices,
 48        StatusTransitionDataServices statusTransitionDataServices,
 49        IHttpClientFactory httpClientFactory,
 50        IEmailService? emailService,
 51        OzonClientServices ozonClientServices,
 52        IOzonPostingCancellationService cancellationService)
 053        : base(context, appStatusServices, orderServices, transactionManager, transactionCache,
 054               supplierDataServices, userCacheService, currencyRatesViewBagService, cacheUpdater, orderCache,
 055               warehouseDataServices: warehouseDataServices, expenseTypeDataServices: expenseTypeDataServices,
 056               orderExtraExpenseDataServices: orderExtraExpenseDataServices,
 057               statusTransitionDataServices: statusTransitionDataServices)
 58    {
 059        _httpClientFactory = httpClientFactory;
 060        _emailService = emailService;
 061        _ozonClientServices = ozonClientServices;
 062        _cancellationService = cancellationService;
 063    }
 64
 065    protected override TransactionConfig GetTransactionConfig() => new()
 066    {
 067        TransactionType = TransactionType.Cancelled,
 068        PrimaryStatusName = "Отменен",
 069        ViewName = "CreateCancelledTransaction",
 070        RequiredSourceStatuses = new List<string> { "Заказан поставщику" },
 071        IncludeCancelledOrders = false,
 072        // Стандартный фильтр исключает заказы с AppStatus="Не указан" и статусом Ozon "Отменён" —
 073        // но именно такие заказы (уже отменённые на маркетплейсе, ещё не отражённые у нас) и есть
 074        // основной кейс транзакции "Отменен". Допустимые исходные статусы уже ограничены StatusTransition.
 075        OrderFilter = order => order.Status != "Отменен покупателем",
 076        CreateTransactionMethod = (tm, orders, userName, createAt, comment) =>
 077            tm.CreateCancelledTransaction(orders, userName, createAt, comment),
 078        ConfigureViewModelAsync = LoadOzonCancelReasonsAsync
 079    };
 80
 81    public Task<IActionResult> CreateCancelledTransaction(string ids, int page)
 082        => CreateTransactionBase(ids, page);
 83
 84    [HttpPost]
 85    [ValidateAntiForgeryToken]
 86    public Task<IActionResult> CreateCancelledTransaction(
 87        List<Order> orders, string userName, string? comment,
 88        int page, string? deletedOrders, Dictionary<int, string?>? orderSupplierMap)
 089        => CreateTransactionPostBase(orders, userName, comment, page, deletedOrders, orderSupplierMap);
 90
 91    protected override string? GetPostTransactionSuccessAppendix() =>
 092        BuildOzonCancelSuccessHtml(_ozonCancelSuccessEntries);
 93
 94    protected override async Task<string?> ExecutePreTransactionAsync(List<Order> orders)
 95    {
 096        _ozonCancelSuccessEntries.Clear();
 097        var cancelRequests = ParseOzonCancelRequestsFromForm();
 098        if (cancelRequests.Count == 0)
 099            return null;
 100
 0101        await EnsureOzonClientsResolvedAsync(orders);
 0102        var ordersById = orders.ToDictionary(o => o.Id);
 0103        var errors = new List<string>();
 104
 0105        foreach (var request in cancelRequests)
 106        {
 0107            if (!ordersById.TryGetValue(request.OrderId, out var order))
 108            {
 0109                errors.Add($"Заказ #{request.OrderId} не найден в транзакции.");
 0110                continue;
 111            }
 112
 0113            if (!await IsOzonMarketplaceOrderAsync(order))
 114                continue;
 115
 0116            if (string.IsNullOrWhiteSpace(order.ShipmentNumber))
 117            {
 0118                errors.Add($"Заказ {order.ShipmentNumber ?? "#" + order.Id}: нет номера отправления.");
 0119                continue;
 120            }
 121
 0122            var ozonClientId = ResolveOzonClientId(order);
 0123            if (!ozonClientId.HasValue)
 124            {
 0125                errors.Add($"Заказ {order.ShipmentNumber}: не указан клиент Ozon.");
 0126                continue;
 127            }
 128
 0129            if (request.ReasonId <= 0)
 130            {
 0131                errors.Add($"Заказ {order.ShipmentNumber}: выберите причину отмены Ozon.");
 0132                continue;
 133            }
 134
 0135            if (request.ReasonId == OzonOrderCancelReasonsInfo.ReasonRequiresMessageId &&
 0136                string.IsNullOrWhiteSpace(request.ReasonMessage))
 137            {
 0138                errors.Add(
 0139                    $"Заказ {order.ShipmentNumber}: для причины «Другое (вина продавца)» укажите комментарий.");
 0140                continue;
 141            }
 142
 143            try
 144            {
 0145                var ozonClient = await _ozonClientServices.GetOzonClientAsync(ozonClientId.Value);
 0146                if (ozonClient == null)
 147                {
 0148                    errors.Add($"Заказ {order.ShipmentNumber}: клиент Ozon не найден.");
 0149                    continue;
 150                }
 151
 0152                var clientId = ozonClient.DecryptClientId;
 0153                var apiKey = ozonClient.DecryptApiKey;
 154
 0155                if (string.IsNullOrWhiteSpace(clientId) || string.IsNullOrWhiteSpace(apiKey))
 156                {
 0157                    errors.Add($"Заказ {order.ShipmentNumber}: не настроены учётные данные Ozon API.");
 0158                    continue;
 159                }
 160
 0161                var apiManager = new OzonApiDataManager(_httpClientFactory, _emailService, clientId, apiKey);
 0162                var cancelled = await apiManager.CancelFbsPostingAsync(
 0163                    order.ShipmentNumber,
 0164                    request.ReasonId,
 0165                    request.ReasonMessage);
 166
 0167                if (!cancelled)
 168                {
 0169                    errors.Add($"Заказ {order.ShipmentNumber}: Ozon не подтвердил отмену отправления.");
 0170                    continue;
 171                }
 172
 0173                await _cancellationService.RecordAsync(ozonClient.Id, order.ShipmentNumber.Trim(), request.ReasonId,
 0174                    request.ReasonTitle, request.ReasonMessage, null, DateTime.UtcNow, "Manual", [order.Id]);
 175
 0176                _ozonCancelSuccessEntries.Add(new OzonCancelSuccessEntry(
 0177                    order.ShipmentNumber.Trim(),
 0178                    request.ReasonTitle,
 0179                    request.ReasonMessage));
 0180            }
 0181            catch (Exception ex)
 182            {
 0183                errors.Add($"Заказ {order.ShipmentNumber}: {ex.Message}");
 0184            }
 0185        }
 186
 0187        return errors.Count > 0 ? string.Join("<br/>", errors) : null;
 0188    }
 189
 190    private static string? BuildOzonCancelSuccessHtml(IReadOnlyList<OzonCancelSuccessEntry> entries)
 191    {
 0192        if (entries == null || entries.Count == 0)
 0193            return null;
 194
 0195        var sb = new StringBuilder();
 0196        sb.Append("<div class='tx-result-extra ozon-cancel-success-summary'>");
 0197        sb.Append("<div class='small fw-semibold mb-1'><i class='fas fa-store me-1'></i>Отмена в Ozon</div>");
 0198        sb.Append("<ul class='small mb-0 ps-3' style='list-style: disc;'>");
 0199        foreach (var entry in entries)
 200        {
 0201            sb.Append("<li>");
 0202            sb.Append(WebUtility.HtmlEncode(entry.ShipmentNumber));
 0203            if (!string.IsNullOrWhiteSpace(entry.ReasonTitle))
 204            {
 0205                sb.Append(" — ");
 0206                sb.Append(WebUtility.HtmlEncode(entry.ReasonTitle));
 207            }
 208
 0209            if (!string.IsNullOrWhiteSpace(entry.ReasonMessage))
 210            {
 0211                sb.Append(" (");
 0212                sb.Append(WebUtility.HtmlEncode(entry.ReasonMessage));
 0213                sb.Append(')');
 214            }
 215
 0216            sb.Append("</li>");
 217        }
 218
 0219        sb.Append("</ul></div>");
 0220        return sb.ToString();
 221    }
 222
 223    private async Task LoadOzonCancelReasonsAsync(MultiplayEditOrderViewModel viewModel)
 224    {
 0225        if (viewModel.Orders == null || !viewModel.Orders.Any())
 0226            return;
 227
 0228        await EnsureOzonClientsResolvedAsync(viewModel.Orders);
 229
 0230        var reasonsByOrderId = new Dictionary<int, OzonOrderCancelReasonsInfo>();
 231
 0232        foreach (var order in viewModel.Orders)
 233        {
 0234            if (!await IsOzonMarketplaceOrderAsync(order))
 235                continue;
 236
 0237            if (string.IsNullOrWhiteSpace(order.ShipmentNumber))
 238            {
 0239                reasonsByOrderId[order.Id] = new OzonOrderCancelReasonsInfo
 0240                {
 0241                    ErrorMessage = "Нет номера отправления"
 0242                };
 0243                continue;
 244            }
 245
 0246            if (!ResolveOzonClientId(order).HasValue)
 247            {
 0248                reasonsByOrderId[order.Id] = new OzonOrderCancelReasonsInfo
 0249                {
 0250                    PostingNumber = order.ShipmentNumber.Trim(),
 0251                    ErrorMessage = "Не указан клиент Ozon"
 0252                };
 0253                continue;
 254            }
 255
 0256            reasonsByOrderId[order.Id] = new OzonOrderCancelReasonsInfo
 0257            {
 0258                PostingNumber = order.ShipmentNumber.Trim()
 0259            };
 0260        }
 261
 0262        var clientGroups = viewModel.Orders
 0263            .Where(o => reasonsByOrderId.ContainsKey(o.Id))
 0264            .Select(o => new { Order = o, ClientId = ResolveOzonClientId(o) })
 0265            .Where(x => x.ClientId.HasValue && !string.IsNullOrWhiteSpace(x.Order.ShipmentNumber))
 0266            .GroupBy(x => x.ClientId!.Value, x => x.Order);
 267
 0268        foreach (var clientGroup in clientGroups)
 269        {
 270            try
 271            {
 0272                var ozonClient = await _ozonClientServices.GetOzonClientAsync(clientGroup.Key);
 0273                if (ozonClient == null)
 274                {
 0275                    foreach (var order in clientGroup)
 0276                        reasonsByOrderId[order.Id].ErrorMessage = "Клиент Ozon не найден";
 0277                    continue;
 278                }
 279
 0280                var clientId = ozonClient.DecryptClientId;
 0281                var apiKey = ozonClient.DecryptApiKey;
 282
 0283                if (string.IsNullOrWhiteSpace(clientId) || string.IsNullOrWhiteSpace(apiKey))
 284                {
 0285                    foreach (var order in clientGroup)
 286                    {
 0287                        reasonsByOrderId[order.Id].ErrorMessage = "Не настроены Client-Id или Api-Key Ozon";
 288                    }
 289
 0290                    continue;
 291                }
 292
 0293                var apiManager = new OzonApiDataManager(_httpClientFactory, _emailService, clientId, apiKey);
 0294                var postingNumbers = clientGroup
 0295                    .Select(o => o.ShipmentNumber!.Trim())
 0296                    .Distinct(StringComparer.OrdinalIgnoreCase)
 0297                    .ToList();
 298
 0299                var response = await apiManager.GetFbsCancelReasonsAsync(postingNumbers);
 0300                var reasonsByPosting = (response?.Result ?? new List<FbsCancelReasonPostingResult>())
 0301                    .Where(r => !string.IsNullOrWhiteSpace(r.PostingNumber))
 0302                    .ToDictionary(
 0303                        r => r.PostingNumber!,
 0304                        r => r.Reasons ?? new List<FbsCancelReasonItem>(),
 0305                        StringComparer.OrdinalIgnoreCase);
 306
 0307                foreach (var order in clientGroup)
 308                {
 0309                    var posting = order.ShipmentNumber!.Trim();
 0310                    if (!reasonsByPosting.TryGetValue(posting, out var cancelReasons) || cancelReasons.Count == 0)
 311                    {
 0312                        reasonsByOrderId[order.Id].ErrorMessage =
 0313                            "Ozon не вернул причины отмены для этого отправления";
 0314                        continue;
 315                    }
 316
 0317                    reasonsByOrderId[order.Id].Reasons = cancelReasons
 0318                        .Select(r => new OzonCancelReasonOption
 0319                        {
 0320                            Id = r.Id,
 0321                            Title = r.Title ?? string.Empty,
 0322                            TypeId = r.TypeId
 0323                        })
 0324                        .ToList();
 325                }
 0326            }
 0327            catch (Exception ex)
 328            {
 0329                foreach (var order in clientGroup)
 0330                    reasonsByOrderId[order.Id].ErrorMessage = ex.Message;
 0331            }
 0332        }
 333
 0334        viewModel.OzonPostingCancelReasons = reasonsByOrderId;
 0335    }
 336
 337    private List<OzonCancelFormRequest> ParseOzonCancelRequestsFromForm()
 338    {
 0339        var result = new List<OzonCancelFormRequest>();
 0340        var checkedOrderIds = new HashSet<int>();
 341
 0342        foreach (var key in Request.Form.Keys)
 343        {
 0344            if (!key.StartsWith("CancelInOzon[", StringComparison.Ordinal) || !key.EndsWith(']'))
 345                continue;
 346
 0347            var idPart = key["CancelInOzon[".Length..^1];
 0348            if (!int.TryParse(idPart, out var orderId))
 349                continue;
 350
 0351            var value = Request.Form[key].FirstOrDefault();
 0352            if (string.Equals(value, "true", StringComparison.OrdinalIgnoreCase) ||
 0353                string.Equals(value, "on", StringComparison.OrdinalIgnoreCase))
 354            {
 0355                checkedOrderIds.Add(orderId);
 356            }
 357        }
 358
 0359        foreach (var orderId in checkedOrderIds)
 360        {
 0361            long reasonId = 0;
 0362            var reasonKey = $"OzonCancelReasonId[{orderId}]";
 0363            if (Request.Form.ContainsKey(reasonKey))
 0364                long.TryParse(Request.Form[reasonKey].FirstOrDefault(), out reasonId);
 365
 0366            string? message = null;
 0367            var messageKey = $"OzonCancelReasonMessage[{orderId}]";
 0368            if (Request.Form.ContainsKey(messageKey))
 0369                message = Request.Form[messageKey].FirstOrDefault();
 370
 0371            string? reasonTitle = null;
 0372            var titleKey = $"OzonCancelReasonTitle[{orderId}]";
 0373            if (Request.Form.ContainsKey(titleKey))
 0374                reasonTitle = Request.Form[titleKey].FirstOrDefault();
 375
 0376            result.Add(new OzonCancelFormRequest(orderId, reasonId, message, reasonTitle));
 377        }
 378
 0379        return result;
 380    }
 381
 382    /// <summary>
 383    /// Клиент Ozon из FK, навигации (в т.ч. скрытое поле формы) или данных заказа в БД.
 384    /// </summary>
 385    private async Task<bool> IsOzonMarketplaceOrderAsync(Order order)
 386    {
 0387        if (order.OzonClient?.ClientType == ClientType.OZON)
 0388            return true;
 389
 0390        var clientId = ResolveOzonClientId(order);
 0391        if (!clientId.HasValue)
 0392            return false;
 393
 394        try
 395        {
 0396            var client = await _ozonClientServices.GetOzonClientAsync(clientId.Value);
 0397            if (client == null)
 0398                return false;
 399
 0400            order.OzonClient ??= client;
 0401            return client.ClientType == ClientType.OZON;
 402        }
 0403        catch
 404        {
 0405            return false;
 406        }
 0407    }
 408
 409    private static int? ResolveOzonClientId(Order order)
 410    {
 0411        if (order.OzonClientId is > 0)
 0412            return order.OzonClientId;
 413
 0414        if (order.OzonClient?.Id is > 0)
 0415            return order.OzonClient.Id;
 416
 0417        return null;
 418    }
 419
 420    private async Task EnsureOzonClientsResolvedAsync(IEnumerable<Order> orders)
 421    {
 0422        var orderList = orders.ToList();
 0423        if (orderList.Count == 0)
 0424            return;
 425
 0426        foreach (var order in orderList)
 427        {
 0428            var resolved = ResolveOzonClientId(order);
 0429            if (resolved.HasValue)
 0430                order.OzonClientId = resolved;
 431        }
 432
 0433        var missingIds = orderList
 0434            .Where(o => !ResolveOzonClientId(o).HasValue)
 0435            .Select(o => o.Id)
 0436            .Distinct()
 0437            .ToList();
 438
 0439        if (missingIds.Count == 0)
 0440            return;
 441
 0442        var dbOrders = await Context.Orders
 0443            .AsNoTracking()
 0444            .Include(o => o.OzonClient)
 0445            .Where(o => missingIds.Contains(o.Id))
 0446            .ToListAsync();
 447
 0448        var dbById = dbOrders.ToDictionary(o => o.Id);
 0449        foreach (var order in orderList.Where(o => !ResolveOzonClientId(o).HasValue))
 450        {
 0451            if (!dbById.TryGetValue(order.Id, out var dbOrder))
 452                continue;
 453
 0454            ApplyOzonClientFromSource(order, dbOrder);
 455        }
 456
 0457        var stillMissing = orderList.Where(o => !ResolveOzonClientId(o).HasValue).ToList();
 0458        if (stillMissing.Count == 0)
 0459            return;
 460
 0461        var shipmentIds = stillMissing
 0462            .Where(o => o.ShipmentId.HasValue)
 0463            .Select(o => o.ShipmentId!.Value)
 0464            .Distinct()
 0465            .ToList();
 466
 0467        if (shipmentIds.Count > 0)
 468        {
 0469            var clientByShipment = await Context.Orders
 0470                .AsNoTracking()
 0471                .Where(o => o.ShipmentId.HasValue &&
 0472                            shipmentIds.Contains(o.ShipmentId.Value) &&
 0473                            o.OzonClientId != null)
 0474                .GroupBy(o => o.ShipmentId!.Value)
 0475                .Select(g => new { ShipmentId = g.Key, OzonClientId = g.Select(x => x.OzonClientId).First() })
 0476                .ToDictionaryAsync(x => x.ShipmentId, x => x.OzonClientId!.Value);
 477
 0478            foreach (var order in stillMissing.Where(o => o.ShipmentId.HasValue))
 479            {
 0480                if (clientByShipment.TryGetValue(order.ShipmentId!.Value, out var clientId))
 0481                    order.OzonClientId = clientId;
 482            }
 483        }
 484
 0485        stillMissing = orderList.Where(o => !ResolveOzonClientId(o).HasValue).ToList();
 0486        var shipmentNumbers = stillMissing
 0487            .Select(o => o.ShipmentNumber?.Trim())
 0488            .Where(n => !string.IsNullOrWhiteSpace(n))
 0489            .Distinct(StringComparer.OrdinalIgnoreCase)
 0490            .ToList();
 491
 0492        if (shipmentNumbers.Count == 0)
 0493            return;
 494
 0495        var clientByPosting = await Context.Orders
 0496            .AsNoTracking()
 0497            .Where(o => o.ShipmentNumber != null &&
 0498                        o.OzonClientId != null &&
 0499                        shipmentNumbers.Contains(o.ShipmentNumber))
 0500            .GroupBy(o => o.ShipmentNumber!)
 0501            .Select(g => new { ShipmentNumber = g.Key, OzonClientId = g.Select(x => x.OzonClientId).First() })
 0502            .ToDictionaryAsync(x => x.ShipmentNumber, x => x.OzonClientId!.Value, StringComparer.OrdinalIgnoreCase);
 503
 0504        foreach (var order in stillMissing)
 505        {
 0506            var posting = order.ShipmentNumber?.Trim();
 0507            if (string.IsNullOrWhiteSpace(posting))
 508                continue;
 509
 0510            if (clientByPosting.TryGetValue(posting, out var clientId))
 0511                order.OzonClientId = clientId;
 512        }
 0513    }
 514
 515    private static void ApplyOzonClientFromSource(Order target, Order source)
 516    {
 0517        if (source.OzonClientId is > 0)
 0518            target.OzonClientId = source.OzonClientId;
 0519        else if (source.OzonClient?.Id is > 0)
 0520            target.OzonClientId = source.OzonClient.Id;
 521
 0522        if (target.OzonClient == null && source.OzonClient != null)
 0523            target.OzonClient = source.OzonClient;
 0524    }
 525
 0526    private sealed record OzonCancelSuccessEntry(string ShipmentNumber, string? ReasonTitle, string? ReasonMessage);
 527
 0528    private sealed record OzonCancelFormRequest(int OrderId, long ReasonId, string? ReasonMessage, string? ReasonTitle);
 529}

Methods/Properties

.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.WarehouseDataServices,Services.DataServices.ExpenseTypeDataServices,Services.DataServices.OrderExtraExpenseDataServices,Services.DataServices.StatusTransitionDataServices,System.Net.Http.IHttpClientFactory,SlqStudio.Application.Services.EmailService.IEmailService,Services.DataServices.OzonClientServices,Services.OzonWebhookServices.IOzonPostingCancellationService)
GetTransactionConfig()
CreateCancelledTransaction(System.String,System.Int32)
CreateCancelledTransaction(System.Collections.Generic.List`1<OzonDomains.Models.Order>,System.String,System.String,System.Int32,System.String,System.Collections.Generic.Dictionary`2<System.Int32,System.String>)
GetPostTransactionSuccessAppendix()
ExecutePreTransactionAsync()
BuildOzonCancelSuccessHtml(System.Collections.Generic.IReadOnlyList`1<OzonOrdersWeb.Areas.Studio2.Controllers.Transactions.CancelledTransactionController/OzonCancelSuccessEntry>)
LoadOzonCancelReasonsAsync()
ParseOzonCancelRequestsFromForm()
IsOzonMarketplaceOrderAsync()
ResolveOzonClientId(OzonDomains.Models.Order)
EnsureOzonClientsResolvedAsync()
ApplyOzonClientFromSource(OzonDomains.Models.Order,OzonDomains.Models.Order)
get_ShipmentNumber()
get_OrderId()