< Summary

Information
Class: OzonOrdersWeb.Areas.Studio2.Controllers.OrderReturnsController
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/OrderReturnsController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 90
Coverable lines: 90
Total lines: 171
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 38
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
GetReturnDetails()0%620%
Index()0%620%
GridRows()0%4260%
GridFacets()0%4260%
ReturnsQuery(...)0%4260%
StartLoadReturns()0%272160%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/OrderReturnsController.cs

#LineLine coverage
 1using Hangfire;
 2using Hangfire.Storage;
 3using Microsoft.AspNetCore.Authorization;
 4using Microsoft.AspNetCore.Mvc;
 5using Microsoft.EntityFrameworkCore;
 6using OzonDomains.Models;
 7using OzonDomains.Models.Authorization.Enums;
 8using OzonOrdersWeb.Authorization;
 9using OzonOrdersWeb.WebServices.Grid;
 10using OzonOrdersWeb.WebServices.Journals;
 11using OzonRepositories.Context;
 12using OzonDomains;
 13using OzonOrdersWeb.Areas.Studio2.ViewModels.OrderReturns;
 14using Services.DataServices;
 15using Services.HangfireService;
 16using Services.OzonReturnsServices;
 17using Services.ParserServices.HelpDictEnum;
 18
 19namespace OzonOrdersWeb.Areas.Studio2.Controllers;
 20
 21[Authorize(Roles = "Admin")]
 22[Area("Studio2")]
 23public class OrderReturnsController : Controller
 24{
 25    private const string PageCode = "Studio2.OrderReturns.Index";
 26    private readonly OrderReturnDataServices _orderReturnDataServices;
 27    private readonly OzonClientServices _ozonClientServices;
 28    private readonly ApplicationDbContext _context;
 29
 030    public OrderReturnsController(
 031        OrderReturnDataServices orderReturnDataServices,
 032        OzonClientServices ozonClientServices,
 033        ApplicationDbContext context)
 34    {
 035        _orderReturnDataServices = orderReturnDataServices;
 036        _ozonClientServices = ozonClientServices;
 037        _context = context;
 038    }
 39
 40    public async Task<IActionResult> GetReturnDetails(long id)
 41    {
 042        var r = await _orderReturnDataServices.GetByIdAsync(id);
 043        if (r == null)
 044            return NotFound();
 45
 046        return PartialView("_ReturnDetailsPartial", r);
 047    }
 48
 49    /// <summary>Каркас страницы: форма загрузки из API и серверный грид. orderId — ссылка из заказа (только его возврат
 50    public async Task<IActionResult> Index(int? orderId = null, CancellationToken cancellationToken = default)
 51    {
 052        var clients = await _ozonClientServices.GetOzonClients();
 053        long? autoOpenId = orderId.HasValue
 054            ? await _context.OrderReturns.AsNoTracking()
 055                .Where(r => r.OrderId == orderId)
 056                .OrderByDescending(r => r.VisualStatusChangeMoment).ThenByDescending(r => r.Id)
 057                .Select(r => (long?)r.Id)
 058                .FirstOrDefaultAsync(cancellationToken)
 059            : null;
 60
 061        return View(new OrderReturnsIndexViewModel
 062        {
 063            OzonClients = clients.Where(c => c.ClientType == ClientType.OZON).ToList(),
 064            TotalCount = await _context.OrderReturns.CountAsync(cancellationToken),
 065            VisualStatusOptions = OzonStatus.ReturnVisualStatuses,
 066            OrderId = orderId,
 067            AutoOpenReturnId = autoOpenId
 068        });
 069    }
 70
 71    [HttpPost("Studio2/OrderReturns/grid")]
 72    [ValidateAntiForgeryToken]
 73    [PageAuthorize(PageCode, AccessType.View)]
 74    public async Task<IActionResult> GridRows([FromBody] ServerGridRequest? request, CancellationToken cancellationToken
 75    {
 076        var error = ServerGridQuery.Validate(request);
 077        if (error != null) return BadRequest(new { success = false, error });
 78
 079        var def = JournalGridDefinitions.OrderReturns;
 080        var filtered = ServerGridQuery.ApplyFilters(ReturnsQuery(request!), def, request!);
 081        var total = await filtered.CountAsync(cancellationToken);
 082        var start = Math.Max(0, request!.StartRow);
 083        var take = Math.Clamp(request.EndRow - start, 1, ServerGridQuery.MaxBlockSize);
 084        var rows = start >= total
 085            ? []
 086            : await ServerGridQuery.ApplySort(filtered, def, request.SortModel).Skip(start).Take(take)
 087                .Select(r => new
 088                {
 089                    r.Id, r.PostingNumber, r.OrderNumber, ClientName = r.OzonClient != null ? r.OzonClient.Name : null,
 090                    r.ProductName, r.ProductOfferId, r.ProductQuantity, r.ProductPrice, r.ProductPriceCurrency,
 091                    VisualStatus = r.VisualStatusSysName, r.ReturnReasonName, r.ReturnType, r.Schema, r.PlaceName,
 092                    CompensationStatus = r.CompensationStatusDisplayName, r.OrderId, r.VisualStatusChangeMoment, r.Retur
 093                })
 094                .ToListAsync(cancellationToken);
 095        var lastRow = start + rows.Count >= total ? total : -1;
 096        return Ok(new { success = true, rows, totalCount = total, lastRow });
 097    }
 98
 99    [HttpPost("Studio2/OrderReturns/grid/facets")]
 100    [ValidateAntiForgeryToken]
 101    [PageAuthorize(PageCode, AccessType.View)]
 102    public async Task<IActionResult> GridFacets([FromBody] ServerGridRequest? request, CancellationToken cancellationTok
 103    {
 0104        if (request is null || string.IsNullOrWhiteSpace(request.ColId)) return BadRequest(new { success = false, error 
 0105        request.StartRow = 0;
 0106        request.EndRow = 1;
 0107        var error = ServerGridQuery.Validate(request);
 0108        if (error != null) return BadRequest(new { success = false, error });
 0109        var values = await ServerGridQuery.FacetsAsync(ReturnsQuery(request), JournalGridDefinitions.OrderReturns, reque
 0110        return Ok(new { success = true, values });
 0111    }
 112
 113    private IQueryable<OrderReturn> ReturnsQuery(ServerGridRequest request)
 114    {
 0115        var query = _context.OrderReturns.AsNoTracking();
 0116        return request.Params.TryGetValue("orderId", out var raw) && int.TryParse(raw, out var orderId) && orderId > 0
 0117            ? query.Where(r => r.OrderId == orderId)
 0118            : query;
 119    }
 120
 121    [HttpPost]
 122    public async Task<IActionResult> StartLoadReturns(int ozonClientId, DateTime from, DateTime to, [FromForm] string jo
 123    {
 0124        if (string.IsNullOrWhiteSpace(jobId))
 0125            return BadRequest(new { error = "jobId обязателен" });
 126
 0127        if (ozonClientId < 0)
 0128            return BadRequest(new { error = "Выберите клиента Ozon или «Все клиенты»" });
 129
 0130        if (ozonClientId > 0)
 131        {
 0132            var client = await _ozonClientServices.GetOzonClientAsync(ozonClientId);
 0133            if (client == null || string.IsNullOrEmpty(client.DecryptClientId) || string.IsNullOrEmpty(client.DecryptApi
 0134                return BadRequest(new { error = "Клиент Ozon не найден или не настроены учётные данные" });
 135        }
 136        else
 137        {
 0138            var ozonClients = (await _ozonClientServices.GetOzonClients())
 0139                .Where(c => c.ClientType == ClientType.OZON).ToList();
 0140            if (ozonClients.Count == 0)
 0141                return BadRequest(new { error = "Нет клиентов с типом Ozon" });
 142        }
 143
 144        // Проба лока: сразу отвечаем 409, если загрузка уже идёт, вместо постановки задачи,
 145        // которая всё равно упрётся в тот же лок и молча завершится. Лок здесь и берётся, и
 146        // отпускается — удерживает его сам джоб. Соединение обязательно закрыть: пул Hangfire
 147        // (Application Name=st2-hangfire) на 64 соединения, и каждый клик по кнопке подтекал.
 148        try
 149        {
 0150            using var lockConnection = JobStorage.Current.GetConnection();
 0151            using var distLock = lockConnection.AcquireDistributedLock(OzonReturnsSyncService.LockKey, TimeSpan.FromMill
 0152        }
 0153        catch (DistributedLockTimeoutException)
 154        {
 0155            return StatusCode(409, new { success = false, message = "Загрузка возвратов уже выполняется." });
 156        }
 157
 0158        var postingNumbersArray = postingNumbers?
 0159            .Split(new[] { ',', ';', ' ', '\n' }, StringSplitOptions.RemoveEmptyEntries)
 0160            .Select(s => s.Trim())
 0161            .Where(s => !string.IsNullOrEmpty(s))
 0162            .Take(50)
 0163            .ToArray();
 164
 165        // Ручной запуск всегда читает выбранный период целиком: пользователь задал границы явно.
 0166        HangfireJobClientExtensions.EnqueueMain<LoadOzonReturnsJob>(j =>
 0167            j.RunManualAsync(ozonClientId, from, to, jobId.Trim(), postingNumbersArray));
 168
 0169        return Json(new { success = true, jobId = jobId.Trim() });
 0170    }
 171}