< Summary

Information
Class: OzonOrdersWeb.Areas.Studio2.Controllers.Directories.SupplierController
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/Directories/SupplierController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 155
Coverable lines: 155
Total lines: 287
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 36
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%
LoadCounterpartiesAsync()0%2040%
Index()0%620%
LoadApiSupplierSettingsAsync()100%210%
CreateApiProviderMap(...)100%210%
Create()0%620%
Create()0%2040%
DeleteConfirmed()0%7280%
Edit()0%620%
Edit()0%156120%
SearchCounterparties()100%210%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/Directories/SupplierController.cs

#LineLine coverage
 1using Microsoft.AspNetCore.Authorization;
 2using Microsoft.AspNetCore.Mvc;
 3using Microsoft.AspNetCore.Mvc.Rendering;
 4using Microsoft.EntityFrameworkCore;
 5using OzonDomains;
 6using OzonOrdersWeb.Authorization;
 7using OzonDomains.Models;
 8using OzonOrdersWeb.ViewModels.SupplierViewModels;
 9using OzonOrdersWeb.WebServices.Directories;
 10using Services.ApiServices;
 11using Services.CacheServices.Cache.OzonOrdersCache;
 12using Services.DataServices;
 13using Services.ParserServices;
 14using Services.ReleasServices.ReleaseManager;
 15using Services.CacheServices.Cache;
 16using Services.ApiSupplierSettingsServices;
 17
 18namespace OzonOrdersWeb.Areas.Studio2.Controllers.Directories
 19{
 20    [Authorize(Roles = "User,Admin")]
 21    [Area("Studio2")]
 22    public class SupplierController : Controller
 23    {
 24        private readonly SupplierDataServices _servcies;
 25        private readonly OrdersDataServices _ordersDataServices;
 26        private readonly OrderCache _cache;
 27        private readonly CacheUpdater<Order> _cacheUpdater;
 28        private readonly OrderCaster _orderCaster;
 29        private readonly ReleaseManager _releaseManager;
 30        private readonly OneCDataManager _oneCDataManager;
 31        private readonly ApiSupplierSettingsService _apiSupplierSettingsService;
 32
 033        public SupplierController(SupplierDataServices servcies,
 034            OrdersDataServices ordersDataServices,
 035            OrderCache orderCache,
 036            CacheUpdater<Order> cacheUpdater,
 037            OrderCaster orderCaster,
 038            ReleaseManager releaseManager,
 039            OneCDataManager oneCDataManager,
 040            ApiSupplierSettingsService apiSupplierSettingsService)
 41        {
 042            _servcies = servcies;
 043            _ordersDataServices = ordersDataServices;
 044            _cache = orderCache;
 045            _cacheUpdater = cacheUpdater;
 046            _orderCaster = orderCaster;
 047            _releaseManager = releaseManager;
 048            _oneCDataManager = oneCDataManager;
 049            _apiSupplierSettingsService = apiSupplierSettingsService;
 050        }
 51
 52        private async Task<List<SupplierResponse>> LoadCounterpartiesAsync()
 53        {
 54            try
 55            {
 056                return (await _oneCDataManager.GetSupplierListAsync())?.ToList() ?? new List<SupplierResponse>();
 57            }
 058            catch
 59            {
 060                return new List<SupplierResponse>();
 61            }
 062        }
 63
 64        /// <summary>
 65        /// Весь справочник поставщиков: сортировка, фильтры и поиск — в гриде AG Grid на клиенте, правка ячеек —
 66        /// в гриде с сохранением строки через <see cref="Edit(int, Supplier)"/>.
 67        /// </summary>
 68        public async Task<IActionResult> Index()
 69        {
 070            var suppliers = (await _servcies.GetSuppliers()).OrderBy(s => s.Name).ToList();
 071            var counterparties = await LoadCounterpartiesAsync();
 072            var apiSettings = await LoadApiSupplierSettingsAsync();
 73
 074            var rows = DirectoryGridRows.BuildSuppliers(suppliers, counterparties, CreateApiProviderMap(apiSettings));
 075            ViewBag.ApiSuppliers = ApiSupplierCatalog.Build(
 076                apiSettings.ToDictionary(x => x.ProviderKey, StringComparer.OrdinalIgnoreCase),
 077                apiSettings.ToDictionary(x => x.ProviderKey, x => ApiSupplierSettingsService.GetDefaultDisplayName(x.Pro
 078                rows,
 079                User.IsInRole("Admin"),
 080                (controller, action, provider) => provider == null
 081                    ? Url.Action(action, controller, new { area = "Studio2" })!
 082                    : Url.Action(action, controller, new { area = "Studio2", provider, supplierTab = "basket" })!);
 083            return View(rows);
 084        }
 85
 86        /// <summary>Настройки API-интеграций: валюта и проценка для грида «Поставщики по API» и связь «поставщик → API»
 87        private async Task<ApiSupplierSettings[]> LoadApiSupplierSettingsAsync()
 88        {
 089            var ct = HttpContext.RequestAborted;
 090            return
 091            [
 092                await _apiSupplierSettingsService.GetArmtekAsync(ct),
 093                await _apiSupplierSettingsService.GetArmtekByAsync(ct),
 094                await _apiSupplierSettingsService.GetShateMAsync(ct),
 095                await _apiSupplierSettingsService.GetAvdAsync(ct),
 096                await _apiSupplierSettingsService.GetBergAsync(ct),
 097                await _apiSupplierSettingsService.GetMotexAsync(ct),
 098                await _apiSupplierSettingsService.GetMlAutoAsync(ct),
 099                await _apiSupplierSettingsService.GetMlAutoRuAsync(ct),
 0100                await _apiSupplierSettingsService.GetForumAutoAsync(ct),
 0101                await _apiSupplierSettingsService.GetForumAutoInterpartsAsync(ct),
 0102                await _apiSupplierSettingsService.GetForumAutoPiterAsync(ct),
 0103                await _apiSupplierSettingsService.GetForumAutoRostovAsync(ct),
 0104                await _apiSupplierSettingsService.GetForumAutoIstraAsync(ct),
 0105                await _apiSupplierSettingsService.GetFavoritPartsAsync(ct),
 0106                await _apiSupplierSettingsService.GetFavoritPartsIstraAsync(ct),
 0107                await _apiSupplierSettingsService.GetFavoritPartsRostovAsync(ct),
 0108                await _apiSupplierSettingsService.GetMoskvorechieIstraAsync(ct),
 0109                await _apiSupplierSettingsService.GetProfitLigaAsync(ct),
 0110                await _apiSupplierSettingsService.GetMikadoMskHod20Async(ct),
 0111                await _apiSupplierSettingsService.GetZZapMoscowAsync(ct)
 0112            ];
 0113        }
 114
 115        private static Dictionary<int, string[]> CreateApiProviderMap(params ApiSupplierSettings[] settings)
 116        {
 0117            return settings
 0118                .Where(settingsItem => settingsItem.SupplierId.HasValue)
 0119                .GroupBy(settingsItem => settingsItem.SupplierId!.Value)
 0120                .ToDictionary(
 0121                    group => group.Key,
 0122                    group => group.Select(settingsItem => settingsItem.ProviderKey).Distinct().ToArray());
 123        }
 124
 125        public async Task<IActionResult> Create()
 126        {
 0127            string? result = TempData["SupplierResult"] as string;
 128
 0129            var suppliersList = await _servcies.GetSuppliers();
 130
 0131            var viewModel = new CreateSupplierViewModel
 0132            {
 0133                SupplierResult = result ?? string.Empty,
 0134                SuppliersList = suppliersList,
 0135                CurrencyCodes = new List<SelectListItem>
 0136                {
 0137                    new SelectListItem { Value = CurrencyCode.RUB.ToString(), Text = "RUB" },
 0138                    new SelectListItem { Value = CurrencyCode.USD.ToString(), Text = "USD" },
 0139                    new SelectListItem { Value = CurrencyCode.EUR.ToString(), Text = "EUR" },
 0140                    new SelectListItem { Value = CurrencyCode.BYN.ToString(), Text = "BYN" },
 0141                }
 0142            };
 143
 0144            return View(viewModel);
 0145        }
 146
 147
 148        [HttpPost]
 149        [ValidateAntiForgeryToken]
 150        public async Task<IActionResult> Create(Supplier supplier)
 151        {
 0152            if (supplier != null)
 153            {
 0154                var existingSupplier = await _servcies.GetSupplierAsync(supplier);
 0155                if (existingSupplier != null)
 156                {
 0157                    TempData["SupplierResult"] = $"Не удалось добавить поставщика '{supplier.Name}'";
 0158                    return RedirectToAction(nameof(Create));
 159                }
 160
 0161                await _servcies.AddSupplier(supplier);
 0162                TempData["SuccessMessage"] = $"Поставщик «{supplier.Name}» создан.";
 163            }
 164
 0165            return RedirectToAction(nameof(Index));
 0166        }
 167
 168        [HttpPost]
 169        [ActionName("Delete")]
 170        [ValidateAntiForgeryToken]
 171        public async Task<IActionResult> DeleteConfirmed(int id)
 172        {
 0173            var supplier = await _servcies.GetSupplierAsync(id);
 0174            if (supplier != null)
 175            {
 0176                var defaultSupplier = await _servcies.GetSupplierAsync(supplier);
 177
 0178                var ordersWithDeletedSupplier = (await _ordersDataServices.GetOrders()).ToList();
 0179                ordersWithDeletedSupplier = ordersWithDeletedSupplier.Where(o => o.SupplierId == id).ToList();
 0180                foreach (var order in ordersWithDeletedSupplier)
 181                {
 0182                    order.Supplier = defaultSupplier;
 183                }
 184
 0185                await _servcies.DeleteSupplier(supplier);
 0186                await _cacheUpdater.Update(_cache);
 0187                TempData["SuccessMessage"] = $"Поставщик «{supplier.Name}» удалён.";
 0188            }
 189
 0190            if (Request.Cookies.TryGetValue("PreviousPageUrl", out var previousPageUrl) && !string.IsNullOrEmpty(previou
 0191                return Redirect(previousPageUrl);
 192
 0193            return RedirectToAction(nameof(Index));
 0194        }
 195
 196        public async Task<IActionResult> Edit(int id)
 197        {
 0198            var supplier = await _servcies.GetSupplierAsync(id);
 0199            if (supplier == null)
 200            {
 0201                return NotFound();
 202            }
 203
 0204            var viewModel = new EditSupplierViewModel
 0205            {
 0206                Supplier = supplier,
 0207                CurrencyCodes = new List<SelectListItem>
 0208                {
 0209                    new SelectListItem { Value = CurrencyCode.RUB.ToString(), Text = "RUB" },
 0210                    new SelectListItem { Value = CurrencyCode.USD.ToString(), Text = "USD" },
 0211                    new SelectListItem { Value = CurrencyCode.EUR.ToString(), Text = "EUR" },
 0212                    new SelectListItem { Value = CurrencyCode.BYN.ToString(), Text = "BYN" },
 0213                }
 0214            };
 215
 0216            return View(viewModel);
 0217        }
 218
 219
 220        [HttpPost]
 221        [ValidateAntiForgeryToken]
 222        public async Task<IActionResult> Edit(int id, Supplier supplier)
 223        {
 0224            if (id != supplier.Id)
 225            {
 0226                return NotFound();
 227            }
 228
 0229            if (ModelState.IsValid)
 230            {
 231                try
 232                {
 233                    // Без трекинга: Update(supplier) ниже прикрепляет свой экземпляр с тем же Id —
 234                    // трекающийся FindAsync() здесь вызывал конфликт identity map EF Core.
 0235                    var existingSupplier = await _servcies.GetSupplierNoTrackingAsync(id);
 0236                    if (existingSupplier is null)
 237                    {
 0238                        return NotFound();
 239                    }
 240
 241                    // Inline-формы редактируют одно поле и не всегда отправляют SLA-норматив.
 242                    // Не сбрасываем его при сохранении других ячеек таблицы.
 0243                    if (Request.HasFormContentType && !Request.Form.ContainsKey(nameof(Supplier.ExpectedDeliveryHours)))
 244                    {
 0245                        supplier.ExpectedDeliveryHours = existingSupplier.ExpectedDeliveryHours;
 246                    }
 0247                    await _servcies.UpdateSupplier(supplier);
 0248                    TempData["SuccessMessage"] = $"Поставщик «{supplier.Name}» обновлён.";
 0249                }
 0250                catch (DbUpdateConcurrencyException)
 251                {
 0252                    if (await _servcies.GetSupplierAsync(id) == null)
 253                    {
 0254                        return NotFound();
 255                    }
 256                    else
 257                    {
 0258                        throw;
 259                    }
 260                }
 261
 0262                return RedirectToAction(nameof(Index));
 263            }
 264
 0265            var viewModel = new EditSupplierViewModel
 0266            {
 0267                Supplier = supplier,
 0268                CurrencyCodes = new List<SelectListItem>
 0269                {
 0270                    new SelectListItem { Value = CurrencyCode.RUB.ToString(), Text = "RUB" },
 0271                    new SelectListItem { Value = CurrencyCode.USD.ToString(), Text = "USD" },
 0272                    new SelectListItem { Value = CurrencyCode.EUR.ToString(), Text = "EUR" },
 0273                    new SelectListItem { Value = CurrencyCode.BYN.ToString(), Text = "BYN" },
 0274                }
 0275            };
 0276            return View(viewModel);
 0277        }
 278
 279        [HttpGet, ServiceEndpoint]
 280        public async Task<IActionResult> SearchCounterparties(string? q, int limit = 15)
 281        {
 0282            limit = Math.Min(limit, 30);
 0283            var results = await _oneCDataManager.SearchCounterpartiesAsync(q, limit);
 0284            return Json(results.Select(c => new { inn = c.Identifier, name = c.Description }));
 0285        }
 286    }
 287}