| | | 1 | | using Microsoft.AspNetCore.Authorization; |
| | | 2 | | using Microsoft.AspNetCore.Mvc; |
| | | 3 | | using OzonDomains; |
| | | 4 | | using OzonDomains.Models; |
| | | 5 | | using OzonOrdersWeb.WebServices; |
| | | 6 | | using OzonOrdersWeb.WebServices.Transactions.Grid.Definitions; |
| | | 7 | | using Services.DataServices; |
| | | 8 | | |
| | | 9 | | namespace OzonOrdersWeb.Areas.Studio2.Controllers.Directories; |
| | | 10 | | |
| | | 11 | | /// <summary> |
| | | 12 | | /// Админка типов транзакций: имена/иконки/цвета в меню проведения и флаг «использовать новый грид». |
| | | 13 | | /// Хранится в <see cref="TransactionTypeSetting"/>; строка без переопределений = дефолты каталога. |
| | | 14 | | /// </summary> |
| | | 15 | | [Authorize(Roles = "Admin")] |
| | | 16 | | [Area("Studio2")] |
| | | 17 | | public sealed class TransactionTypeSettingsController : Controller |
| | | 18 | | { |
| | | 19 | | private readonly ITransactionTypeSettingDataServices _settings; |
| | | 20 | | private readonly ITransactionDefinitionProvider _definitions; |
| | | 21 | | private readonly TransactionTransitionRuleService _ruleService; |
| | | 22 | | private readonly TransactionTooltipService _tooltipService; |
| | | 23 | | |
| | | 24 | | public TransactionTypeSettingsController( |
| | | 25 | | ITransactionTypeSettingDataServices settings, |
| | | 26 | | ITransactionDefinitionProvider definitions, |
| | | 27 | | TransactionTransitionRuleService ruleService, |
| | | 28 | | TransactionTooltipService tooltipService) |
| | | 29 | | { |
| | | 30 | | _settings = settings; |
| | | 31 | | _definitions = definitions; |
| | | 32 | | _ruleService = ruleService; |
| | | 33 | | _tooltipService = tooltipService; |
| | | 34 | | } |
| | | 35 | | |
| | | 36 | | public async Task<IActionResult> Index(CancellationToken cancellationToken) |
| | | 37 | | { |
| | | 38 | | var definitions = await _definitions.GetAllAsync(cancellationToken); |
| | | 39 | | var stored = (await _settings.GetAllAsync(cancellationToken)).ToDictionary(s => s.TransactionType); |
| | | 40 | | var rows = definitions |
| | | 41 | | .Select(d => new TransactionTypeSettingRow(d, TransactionDefinitionCatalog.ByType(d.Type)!, stored.GetValueO |
| | | 42 | | .ToList(); |
| | | 43 | | return View(rows); |
| | | 44 | | } |
| | | 45 | | |
| | | 46 | | public async Task<IActionResult> Edit(TransactionType id, CancellationToken cancellationToken) |
| | | 47 | | { |
| | | 48 | | var catalog = TransactionDefinitionCatalog.ByType(id); |
| | | 49 | | if (catalog == null) |
| | | 50 | | { |
| | | 51 | | return NotFound(); |
| | | 52 | | } |
| | | 53 | | |
| | | 54 | | var stored = await _settings.GetAsync(id, cancellationToken) ?? new TransactionTypeSetting { TransactionType = i |
| | | 55 | | ViewBag.Catalog = catalog; |
| | | 56 | | ViewBag.Groups = new[] |
| | | 57 | | { |
| | | 58 | | TransactionDefinitionCatalog.GroupPurchase, TransactionDefinitionCatalog.GroupShipping, |
| | | 59 | | TransactionDefinitionCatalog.GroupWarehouse, TransactionDefinitionCatalog.GroupReturns, |
| | | 60 | | TransactionDefinitionCatalog.GroupCancel, TransactionDefinitionCatalog.GroupPrices |
| | | 61 | | }; |
| | | 62 | | return View(stored); |
| | | 63 | | } |
| | | 64 | | |
| | | 65 | | [HttpPost] |
| | | 66 | | [ValidateAntiForgeryToken] |
| | | 67 | | public async Task<IActionResult> Edit(TransactionType id, TransactionTypeSetting model, CancellationToken cancellati |
| | | 68 | | { |
| | | 69 | | if (TransactionDefinitionCatalog.ByType(id) == null) |
| | | 70 | | { |
| | | 71 | | return NotFound(); |
| | | 72 | | } |
| | | 73 | | |
| | | 74 | | if (!string.IsNullOrWhiteSpace(model.Color) && !System.Text.RegularExpressions.Regex.IsMatch(model.Color.Trim(), |
| | | 75 | | { |
| | | 76 | | ModelState.AddModelError(nameof(model.Color), "Цвет в формате #rrggbb."); |
| | | 77 | | } |
| | | 78 | | |
| | | 79 | | if (!string.IsNullOrWhiteSpace(model.DefaultsJson)) |
| | | 80 | | { |
| | | 81 | | try |
| | | 82 | | { |
| | | 83 | | using var _ = System.Text.Json.JsonDocument.Parse(model.DefaultsJson); |
| | | 84 | | } |
| | | 85 | | catch (System.Text.Json.JsonException) |
| | | 86 | | { |
| | | 87 | | ModelState.AddModelError(nameof(model.DefaultsJson), "Некорректный JSON."); |
| | | 88 | | } |
| | | 89 | | } |
| | | 90 | | |
| | | 91 | | if (!ModelState.IsValid) |
| | | 92 | | { |
| | | 93 | | ViewBag.Catalog = TransactionDefinitionCatalog.ByType(id); |
| | | 94 | | ViewBag.Groups = new[] |
| | | 95 | | { |
| | | 96 | | TransactionDefinitionCatalog.GroupPurchase, TransactionDefinitionCatalog.GroupShipping, |
| | | 97 | | TransactionDefinitionCatalog.GroupWarehouse, TransactionDefinitionCatalog.GroupReturns, |
| | | 98 | | TransactionDefinitionCatalog.GroupCancel, TransactionDefinitionCatalog.GroupPrices |
| | | 99 | | }; |
| | | 100 | | model.TransactionType = id; |
| | | 101 | | return View(model); |
| | | 102 | | } |
| | | 103 | | |
| | | 104 | | model.TransactionType = id; |
| | | 105 | | await _settings.UpsertAsync(model, User.Identity?.Name, cancellationToken); |
| | | 106 | | InvalidateCaches(); |
| | | 107 | | TempData["StatusResult"] = $"Настройки «{TransactionDefinitionCatalog.ByType(id)!.Title}» сохранены."; |
| | | 108 | | return RedirectToAction(nameof(Index)); |
| | | 109 | | } |
| | | 110 | | |
| | | 111 | | /// <summary>Быстрое переключение «использовать грид» из таблицы.</summary> |
| | | 112 | | [HttpPost] |
| | | 113 | | [ValidateAntiForgeryToken] |
| | | 114 | | public async Task<IActionResult> ToggleGrid(TransactionType id, bool useGrid, CancellationToken cancellationToken) |
| | | 115 | | { |
| | | 116 | | if (TransactionDefinitionCatalog.ByType(id) == null) |
| | | 117 | | { |
| | | 118 | | return NotFound(); |
| | | 119 | | } |
| | | 120 | | |
| | | 121 | | var stored = await _settings.GetAsync(id, cancellationToken) ?? new TransactionTypeSetting { TransactionType = i |
| | | 122 | | stored.UseGrid = useGrid; |
| | | 123 | | await _settings.UpsertAsync(stored, User.Identity?.Name, cancellationToken); |
| | | 124 | | InvalidateCaches(); |
| | | 125 | | return Json(new { ok = true, useGrid }); |
| | | 126 | | } |
| | | 127 | | |
| | | 128 | | [HttpPost] |
| | | 129 | | [ValidateAntiForgeryToken] |
| | | 130 | | public async Task<IActionResult> Reset(TransactionType id, CancellationToken cancellationToken) |
| | | 131 | | { |
| | | 132 | | await _settings.ResetAsync(id, cancellationToken); |
| | | 133 | | InvalidateCaches(); |
| | | 134 | | TempData["StatusResult"] = "Настройки сброшены к значениям каталога."; |
| | | 135 | | return RedirectToAction(nameof(Index)); |
| | | 136 | | } |
| | | 137 | | |
| | | 138 | | private void InvalidateCaches() |
| | | 139 | | { |
| | | 140 | | _definitions.Invalidate(); |
| | | 141 | | _ruleService.InvalidateCache(); |
| | | 142 | | _tooltipService.InvalidateCache(); |
| | | 143 | | } |
| | | 144 | | } |
| | | 145 | | |
| | | 146 | | /// <summary>Строка таблицы админки: эффективное определение, дефолт каталога и сохранённая настройка (если есть).</summ |
| | 0 | 147 | | public sealed record TransactionTypeSettingRow(TransactionDefinition Effective, TransactionDefinition Catalog, Transacti |