< Summary

Information
Class: OzonOrdersWeb.Areas.Studio2.Controllers.Directories.ApiSupplierSettingsController
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/Directories/ApiSupplierSettingsController.cs
Line coverage
5%
Covered lines: 30
Uncovered lines: 562
Coverable lines: 592
Total lines: 1071
Line coverage: 5%
Branch coverage
0%
Covered branches: 0
Total branches: 307
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%11100%
Index()100%11100%
Japarts()100%210%
Japarts()0%620%
Armtek()100%210%
Armtek()0%2040%
ArmtekBy()100%210%
ArmtekBy()0%2040%
ShateM()100%210%
ShateM()0%620%
Avd()100%210%
Avd()0%620%
ForumAuto()0%620%
ForumAuto()0%620%
ForumAutoInterparts()0%620%
ForumAutoInterparts()0%620%
ForumAutoPiter()0%620%
ForumAutoPiter()0%620%
ForumAutoRostov()0%620%
ForumAutoRostov()0%620%
ForumAutoIstra()0%620%
ForumAutoIstra()0%620%
FavoritParts()100%210%
FavoritParts()0%620%
FavoritPartsIstra()100%210%
FavoritPartsIstra()0%620%
FavoritPartsRostov()100%210%
FavoritPartsRostov()0%620%
MoskvorechieIstra()100%210%
MoskvorechieIstra()0%620%
ProfitLiga()100%210%
ProfitLiga()0%4260%
Nikei()100%210%
Nikei()0%620%
PopulateProfitLigaPointsAsync()0%2040%
PopulateMlAutoMappingsAsync()0%2040%
PopulateMlAutoRuMappingsAsync()0%2040%
ToMlAutoDeliveryAddresses(...)0%600240%
Berg()100%210%
Berg()0%110100%
PopulateBergAddressesAsync()0%4260%
Motex()100%210%
Motex()0%620%
MikadoMskHod20()100%210%
MikadoMskHod20()0%620%
ZZapMoscow()100%210%
ZZapMoscow()0%620%
MlAuto()100%210%
MlAuto()0%4260%
MlAutoRu()100%210%
MlAutoRu()0%4260%
PopulateRateAsync()0%3050%
PopulateRateAsync()0%3050%
PopulateModelAsync()0%2756520%
SaveSettingsAsync()0%620%
TryGetProviderSettings(...)0%1980440%
PopulateArmtekMappingsAsync()0%7280%
ValidateAndSaveArmtekMappingsAsync()0%210140%
ToBuyerViewModels(...)0%4260%
FirstNotEmpty(...)0%620%
PopulateRateAsync(...)0%620%
PopulateRateAsync(...)0%620%
PopulateRateAsync()0%3050%
PopulateRateAsync()100%210%
PopulateRateAsync(...)0%620%
PopulateRateAsync(...)0%620%
PopulateRateAsync(...)0%620%
PopulateRateAsync(...)0%620%
PopulateRateAsync(...)0%620%
PopulateRateAsync(...)0%620%
PopulateRateAsync(...)0%620%
PopulateRateAsync(...)0%620%
PopulateRateAsync(...)100%210%
ValidateSupplierAsync()0%2040%

File(s)

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

#LineLine coverage
 1using Microsoft.AspNetCore.Authorization;
 2using Microsoft.AspNetCore.Mvc;
 3using OzonDomains;
 4using OzonDomains.Models;
 5using OzonOrdersWeb.Areas.Studio2.ViewModels.ApiSupplierSettings;
 6using Services.ApiSupplierSettingsServices;
 7using Services.ApiServices.ArmtekApi;
 8using Services.ApiServices.ArmtekApi.Models.Response;
 9using Services.ApiServices.MlAutoApi;
 10using Services.ApiServices.ProfitLigaApi;
 11using Services.DataServices;
 12using Services.PriceСlculationServices;
 13
 14namespace OzonOrdersWeb.Areas.Studio2.Controllers.Directories;
 15
 16[Area("Studio2")]
 17[Authorize(Roles = "Admin")]
 18public class ApiSupplierSettingsController : Controller
 19{
 20    private readonly ApiSupplierSettingsService _settingsService;
 21    private readonly ApiSupplierWarehouseService _supplierWarehouses;
 22    private readonly ICurrencyRateFetcher _currencyRateFetcher;
 23    private readonly SupplierDataServices _supplierDataServices;
 24    private readonly WarehouseDataServices _warehouseDataServices;
 25    private readonly ArmtekDeliveryAddressMappingService _armtekDeliveryAddressMappings;
 26    private readonly ArmtekDataManager _armtek;
 27    private readonly ArmtekByDataManager _armtekBy;
 28    private readonly MlAutoDeliveryAddressMappingService _mlAutoDeliveryMappings;
 29    private readonly ProfitLigaPointMappingService _profitLigaPointMappings;
 30    private readonly BergDeliveryAddressMappingService _bergAddressMappings;
 31    private readonly Services.ApiServices.BergApi.BergDataManager _berg;
 32    private readonly ProfitLigaDataManager _profitLiga;
 33    private readonly MlAutoDataManager _mlAuto;
 34    private readonly MlAutoRuDeliveryAddressMappingService _mlAutoRuDeliveryMappings;
 35    private readonly MlAutoRuDataManager _mlAutoRu;
 36
 137    public ApiSupplierSettingsController(
 138        ApiSupplierSettingsService settingsService,
 139        ICurrencyRateFetcher currencyRateFetcher,
 140        SupplierDataServices supplierDataServices,
 141        WarehouseDataServices warehouseDataServices,
 142        ArmtekDeliveryAddressMappingService armtekDeliveryAddressMappings,
 143        ArmtekDataManager armtek,
 144        ArmtekByDataManager armtekBy, MlAutoDeliveryAddressMappingService mlAutoDeliveryMappings, MlAutoDataManager mlAu
 145        ProfitLigaPointMappingService profitLigaPointMappings, ProfitLigaDataManager profitLiga,
 146        BergDeliveryAddressMappingService bergAddressMappings, Services.ApiServices.BergApi.BergDataManager berg,
 147        MlAutoRuDeliveryAddressMappingService mlAutoRuDeliveryMappings, MlAutoRuDataManager mlAutoRu,
 148        ApiSupplierWarehouseService supplierWarehouses)
 49    {
 150        _settingsService = settingsService;
 151        _supplierWarehouses = supplierWarehouses;
 152        _currencyRateFetcher = currencyRateFetcher;
 153        _supplierDataServices = supplierDataServices;
 154        _warehouseDataServices = warehouseDataServices;
 155        _armtekDeliveryAddressMappings = armtekDeliveryAddressMappings;
 156        _armtek = armtek;
 157        _armtekBy = armtekBy;
 158        _mlAutoDeliveryMappings = mlAutoDeliveryMappings;
 159        _profitLigaPointMappings = profitLigaPointMappings;
 160        _bergAddressMappings = bergAddressMappings;
 161        _berg = berg;
 162        _profitLiga = profitLiga;
 163        _mlAuto = mlAuto;
 164        _mlAutoRuDeliveryMappings = mlAutoRuDeliveryMappings;
 165        _mlAutoRu = mlAutoRu;
 166    }
 67
 68    [HttpGet]
 169    public IActionResult Index() => RedirectToAction(nameof(Armtek));
 70
 71    [HttpGet]
 72    public async Task<IActionResult> Japarts(CancellationToken cancellationToken)
 73    {
 074        var settings = await _settingsService.GetAsync(ApiSupplierSettings.JapartsProviderKey, CurrencyCode.RUB, cancell
 075        var model = new JapartsSettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = settings.I
 076        await PopulateModelAsync(model);
 077        return View(model);
 078    }
 79
 80    [HttpPost, ValidateAntiForgeryToken]
 81    public async Task<IActionResult> Japarts(JapartsSettingsViewModel model, CancellationToken cancellationToken)
 82    {
 083        await ValidateSupplierAsync(model, cancellationToken);
 084        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 085        await SaveSettingsAsync(model, cancellationToken);
 086        TempData["SuccessMessage"] = "Настройки Japarts сохранены.";
 087        return RedirectToAction(nameof(Japarts));
 088    }
 89
 90    [HttpGet]
 91    public async Task<IActionResult> Armtek(CancellationToken cancellationToken)
 92    {
 093        var settings = await _settingsService.GetArmtekAsync(cancellationToken);
 094        var model = new ArmtekSettingsViewModel
 095        {
 096            CurrencyCode = settings.CurrencyCode,
 097            IsQuotationEnabled = settings.IsQuotationEnabled,
 098            SupplierId = settings.SupplierId,
 099            ExtraDeliveryHours = settings.ExtraDeliveryHours
 0100        };
 0101        await PopulateModelAsync(model);
 0102        await PopulateArmtekMappingsAsync(model, ApiSupplierSettings.ArmtekProviderKey, cancellationToken);
 0103        return View(model);
 0104    }
 105
 106    [HttpPost]
 107    [ValidateAntiForgeryToken]
 108    public async Task<IActionResult> Armtek(ArmtekSettingsViewModel model, CancellationToken cancellationToken)
 109    {
 0110        await ValidateSupplierAsync(model, cancellationToken);
 0111        if (!ModelState.IsValid)
 112        {
 0113            await PopulateModelAsync(model);
 0114            await PopulateArmtekMappingsAsync(model, ApiSupplierSettings.ArmtekProviderKey, cancellationToken, preserveP
 0115            return View(model);
 116        }
 117
 0118        await ValidateAndSaveArmtekMappingsAsync(model, ApiSupplierSettings.ArmtekProviderKey, cancellationToken);
 0119        if (!ModelState.IsValid)
 120        {
 0121            await PopulateModelAsync(model);
 0122            await PopulateArmtekMappingsAsync(model, ApiSupplierSettings.ArmtekProviderKey, cancellationToken, preserveP
 0123            return View(model);
 124        }
 0125        await SaveSettingsAsync(model, cancellationToken);
 0126        TempData["SuccessMessage"] = "Настройки Армтека сохранены.";
 0127        return RedirectToAction(nameof(Armtek));
 0128    }
 129
 130    [HttpGet]
 131    public async Task<IActionResult> ArmtekBy(CancellationToken cancellationToken)
 132    {
 0133        var settings = await _settingsService.GetArmtekByAsync(cancellationToken);
 0134        var model = new ArmtekBySettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = settings.
 0135        await PopulateModelAsync(model);
 0136        await PopulateArmtekMappingsAsync(model, ApiSupplierSettings.ArmtekByProviderKey, cancellationToken);
 0137        return View(model);
 0138    }
 139
 140    [HttpPost]
 141    [ValidateAntiForgeryToken]
 142    public async Task<IActionResult> ArmtekBy(ArmtekBySettingsViewModel model, CancellationToken cancellationToken)
 143    {
 0144        await ValidateSupplierAsync(model, cancellationToken);
 0145        if (!ModelState.IsValid)
 146        {
 0147            await PopulateModelAsync(model);
 0148            await PopulateArmtekMappingsAsync(model, ApiSupplierSettings.ArmtekByProviderKey, cancellationToken, preserv
 0149            return View(model);
 150        }
 0151        await ValidateAndSaveArmtekMappingsAsync(model, ApiSupplierSettings.ArmtekByProviderKey, cancellationToken);
 0152        if (!ModelState.IsValid)
 153        {
 0154            await PopulateModelAsync(model);
 0155            await PopulateArmtekMappingsAsync(model, ApiSupplierSettings.ArmtekByProviderKey, cancellationToken, preserv
 0156            return View(model);
 157        }
 0158        await SaveSettingsAsync(model, cancellationToken);
 0159        TempData["SuccessMessage"] = "Настройки Армтека BY сохранены.";
 0160        return RedirectToAction(nameof(ArmtekBy));
 0161    }
 162
 163    [HttpGet]
 164    public async Task<IActionResult> ShateM(CancellationToken cancellationToken)
 165    {
 0166        var settings = await _settingsService.GetShateMAsync(cancellationToken);
 0167        var model = new ShateMSettingsViewModel
 0168        {
 0169            CurrencyCode = settings.CurrencyCode,
 0170            IsQuotationEnabled = settings.IsQuotationEnabled,
 0171            SupplierId = settings.SupplierId,
 0172            ExtraDeliveryHours = settings.ExtraDeliveryHours
 0173        };
 0174        await PopulateModelAsync(model);
 0175        return View(model);
 0176    }
 177
 178    [HttpPost]
 179    [ValidateAntiForgeryToken]
 180    public async Task<IActionResult> ShateM(ShateMSettingsViewModel model, CancellationToken cancellationToken)
 181    {
 0182        await ValidateSupplierAsync(model, cancellationToken);
 0183        if (!ModelState.IsValid)
 184        {
 0185            await PopulateModelAsync(model);
 0186            return View(model);
 187        }
 0188        await SaveSettingsAsync(model, cancellationToken);
 0189        TempData["SuccessMessage"] = "Настройки Шате-М сохранены.";
 0190        return RedirectToAction(nameof(ShateM));
 0191    }
 192
 193    [HttpGet]
 194    public async Task<IActionResult> Avd(CancellationToken cancellationToken)
 195    {
 0196        var settings = await _settingsService.GetAvdAsync(cancellationToken);
 0197        var model = new AvdSettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = settings.IsQuo
 0198        await PopulateModelAsync(model);
 0199        return View(model);
 0200    }
 201
 202    [HttpPost]
 203    [ValidateAntiForgeryToken]
 204    public async Task<IActionResult> Avd(AvdSettingsViewModel model, CancellationToken cancellationToken)
 205    {
 0206        await ValidateSupplierAsync(model, cancellationToken);
 0207        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0208        await SaveSettingsAsync(model, cancellationToken);
 0209        TempData["SuccessMessage"] = "Настройки АВД сохранены.";
 0210        return RedirectToAction(nameof(Avd));
 0211    }
 212
 213    [HttpGet]
 214    public async Task<IActionResult> ForumAuto(CancellationToken cancellationToken)
 215    {
 0216        var settings = await _settingsService.GetForumAutoAsync(cancellationToken);
 0217        var model = new ForumAutoSettingsViewModel { DisplayName = settings.DisplayName ?? ApiSupplierSettingsService.Ge
 0218        await PopulateModelAsync(model);
 0219        return View(model);
 0220    }
 221
 222    [HttpPost]
 223    [ValidateAntiForgeryToken]
 224    public async Task<IActionResult> ForumAuto(ForumAutoSettingsViewModel model, CancellationToken cancellationToken)
 225    {
 0226        await ValidateSupplierAsync(model, cancellationToken);
 0227        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0228        await SaveSettingsAsync(model, cancellationToken);
 0229        TempData["SuccessMessage"] = "Настройки Forum-Auto сохранены.";
 0230        return RedirectToAction(nameof(ForumAuto));
 0231    }
 232
 233    [HttpGet]
 234    public async Task<IActionResult> ForumAutoInterparts(CancellationToken cancellationToken)
 235    {
 0236        var settings = await _settingsService.GetForumAutoInterpartsAsync(cancellationToken);
 0237        var model = new ForumAutoInterpartsSettingsViewModel { DisplayName = settings.DisplayName ?? ApiSupplierSettings
 0238        await PopulateModelAsync(model);
 0239        return View(model);
 0240    }
 241
 242    [HttpPost]
 243    [ValidateAntiForgeryToken]
 244    public async Task<IActionResult> ForumAutoInterparts(ForumAutoInterpartsSettingsViewModel model, CancellationToken c
 245    {
 0246        await ValidateSupplierAsync(model, cancellationToken);
 0247        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0248        await SaveSettingsAsync(model, cancellationToken);
 0249        TempData["SuccessMessage"] = "Настройки Forum-Auto (Интерпартс) сохранены.";
 0250        return RedirectToAction(nameof(ForumAutoInterparts));
 0251    }
 252
 253    [HttpGet]
 254    public async Task<IActionResult> ForumAutoPiter(CancellationToken cancellationToken)
 255    {
 0256        var settings = await _settingsService.GetForumAutoPiterAsync(cancellationToken);
 0257        var model = new ForumAutoPiterSettingsViewModel { DisplayName = settings.DisplayName ?? ApiSupplierSettingsServi
 0258        await PopulateModelAsync(model);
 0259        return View(model);
 0260    }
 261
 262    [HttpPost]
 263    [ValidateAntiForgeryToken]
 264    public async Task<IActionResult> ForumAutoPiter(ForumAutoPiterSettingsViewModel model, CancellationToken cancellatio
 265    {
 0266        await ValidateSupplierAsync(model, cancellationToken);
 0267        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0268        await SaveSettingsAsync(model, cancellationToken);
 0269        TempData["SuccessMessage"] = "Настройки Forum-Auto (Питер) сохранены.";
 0270        return RedirectToAction(nameof(ForumAutoPiter));
 0271    }
 272
 273    [HttpGet]
 274    public async Task<IActionResult> ForumAutoRostov(CancellationToken cancellationToken)
 275    {
 0276        var settings = await _settingsService.GetForumAutoRostovAsync(cancellationToken);
 0277        var model = new ForumAutoRostovSettingsViewModel { DisplayName = settings.DisplayName ?? ApiSupplierSettingsServ
 0278        await PopulateModelAsync(model);
 0279        return View(model);
 0280    }
 281
 282    [HttpPost]
 283    [ValidateAntiForgeryToken]
 284    public async Task<IActionResult> ForumAutoRostov(ForumAutoRostovSettingsViewModel model, CancellationToken cancellat
 285    {
 0286        await ValidateSupplierAsync(model, cancellationToken);
 0287        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0288        await SaveSettingsAsync(model, cancellationToken);
 0289        TempData["SuccessMessage"] = "Настройки Forum-Auto (Ростов) сохранены.";
 0290        return RedirectToAction(nameof(ForumAutoRostov));
 0291    }
 292
 293    [HttpGet]
 294    public async Task<IActionResult> ForumAutoIstra(CancellationToken cancellationToken)
 295    {
 0296        var settings = await _settingsService.GetForumAutoIstraAsync(cancellationToken);
 0297        var model = new ForumAutoIstraSettingsViewModel { DisplayName = settings.DisplayName ?? ApiSupplierSettingsServi
 0298        await PopulateModelAsync(model);
 0299        return View(model);
 0300    }
 301
 302    [HttpPost]
 303    [ValidateAntiForgeryToken]
 304    public async Task<IActionResult> ForumAutoIstra(ForumAutoIstraSettingsViewModel model, CancellationToken cancellatio
 305    {
 0306        await ValidateSupplierAsync(model, cancellationToken);
 0307        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0308        await SaveSettingsAsync(model, cancellationToken);
 0309        TempData["SuccessMessage"] = "Настройки Forum-Auto (Истра) сохранены.";
 0310        return RedirectToAction(nameof(ForumAutoIstra));
 0311    }
 312
 313    [HttpGet]
 314    public async Task<IActionResult> FavoritParts(CancellationToken cancellationToken)
 315    {
 0316        var settings = await _settingsService.GetFavoritPartsAsync(cancellationToken);
 0317        var model = new FavoritPartsSettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = setti
 0318        await PopulateModelAsync(model);
 0319        return View(model);
 0320    }
 321
 322    [HttpPost]
 323    [ValidateAntiForgeryToken]
 324    public async Task<IActionResult> FavoritParts(FavoritPartsSettingsViewModel model, CancellationToken cancellationTok
 325    {
 0326        await ValidateSupplierAsync(model, cancellationToken);
 0327        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0328        await SaveSettingsAsync(model, cancellationToken);
 0329        TempData["SuccessMessage"] = "Настройки FavoritParts сохранены.";
 0330        return RedirectToAction(nameof(FavoritParts));
 0331    }
 332
 333    [HttpGet]
 334    public async Task<IActionResult> FavoritPartsIstra(CancellationToken cancellationToken)
 335    {
 0336        var settings = await _settingsService.GetFavoritPartsIstraAsync(cancellationToken);
 0337        var model = new FavoritPartsIstraSettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = 
 0338        await PopulateModelAsync(model);
 0339        return View(model);
 0340    }
 341
 342    [HttpPost, ValidateAntiForgeryToken]
 343    public async Task<IActionResult> FavoritPartsIstra(FavoritPartsIstraSettingsViewModel model, CancellationToken cance
 344    {
 0345        await ValidateSupplierAsync(model, cancellationToken);
 0346        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0347        await SaveSettingsAsync(model, cancellationToken);
 0348        TempData["SuccessMessage"] = "Настройки Фаворит Истра сохранены.";
 0349        return RedirectToAction(nameof(FavoritPartsIstra));
 0350    }
 351
 352    [HttpGet]
 353    public async Task<IActionResult> FavoritPartsRostov(CancellationToken cancellationToken)
 354    {
 0355        var settings = await _settingsService.GetFavoritPartsRostovAsync(cancellationToken);
 0356        var model = new FavoritPartsRostovSettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled =
 0357        await PopulateModelAsync(model);
 0358        return View(model);
 0359    }
 360
 361    [HttpPost, ValidateAntiForgeryToken]
 362    public async Task<IActionResult> FavoritPartsRostov(FavoritPartsRostovSettingsViewModel model, CancellationToken can
 363    {
 0364        await ValidateSupplierAsync(model, cancellationToken);
 0365        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0366        await SaveSettingsAsync(model, cancellationToken);
 0367        TempData["SuccessMessage"] = "Настройки Фаворит Ростов сохранены.";
 0368        return RedirectToAction(nameof(FavoritPartsRostov));
 0369    }
 370
 371    [HttpGet]
 372    public async Task<IActionResult> MoskvorechieIstra(CancellationToken cancellationToken)
 373    {
 0374        var settings = await _settingsService.GetMoskvorechieIstraAsync(cancellationToken);
 0375        var model = new MoskvorechieIstraSettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = 
 0376        await PopulateModelAsync(model);
 0377        return View(model);
 0378    }
 379
 380    [HttpPost, ValidateAntiForgeryToken]
 381    public async Task<IActionResult> MoskvorechieIstra(MoskvorechieIstraSettingsViewModel model, CancellationToken cance
 382    {
 0383        await ValidateSupplierAsync(model, cancellationToken);
 0384        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0385        await SaveSettingsAsync(model, cancellationToken);
 0386        TempData["SuccessMessage"] = "Настройки Москворечье (Истра) сохранены.";
 0387        return RedirectToAction(nameof(MoskvorechieIstra));
 0388    }
 389
 390    [HttpGet]
 391    public async Task<IActionResult> ProfitLiga(CancellationToken cancellationToken)
 392    {
 0393        var settings = await _settingsService.GetProfitLigaAsync(cancellationToken);
 0394        var model = new ProfitLigaSettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = setting
 0395        await PopulateModelAsync(model);
 0396        return View(model);
 0397    }
 398
 399    [HttpPost, ValidateAntiForgeryToken]
 400    public async Task<IActionResult> ProfitLiga(ProfitLigaSettingsViewModel model, CancellationToken cancellationToken)
 401    {
 0402        await ValidateSupplierAsync(model, cancellationToken);
 0403        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 404
 405        // Список точек в форму не приходит — перечитываем его сами. Без актуального списка
 406        // сохранять связи нельзя: селекты были отключены, и пустой POST стёр бы уже настроенные точки.
 0407        await PopulateProfitLigaPointsAsync(model, cancellationToken, preservePosted: true);
 0408        if (!string.IsNullOrWhiteSpace(model.PointsLoadError))
 409        {
 0410            ModelState.AddModelError(string.Empty, "Нельзя сохранить связи без списка торговых точек Профит-Лиги.");
 0411            return View(model);
 412        }
 413
 414        var allowed = model.Points.Select(x => x.Code).ToHashSet(StringComparer.Ordinal);
 0415        if (model.PointMappings.Any(x => !string.IsNullOrWhiteSpace(x.PointCode) && !allowed.Contains(x.PointCode!)))
 416        {
 0417            ModelState.AddModelError(string.Empty, "Выберите торговую точку из актуального списка Профит-Лиги.");
 0418            return View(model);
 419        }
 420
 421        var names = model.Points.ToDictionary(x => x.Code, x => x.DisplayName, StringComparer.Ordinal);
 0422        await SaveSettingsAsync(model, cancellationToken);
 0423        await _profitLigaPointMappings.ReplaceAsync(model.PointMappings
 0424            .Select(x => new ProfitLigaPointMappingInput(x.ShipmentWarehouseId, x.PointCode,
 0425                x.PointCode is null ? null : names.GetValueOrDefault(x.PointCode)))
 0426            .ToList(), cancellationToken);
 427
 0428        TempData["SuccessMessage"] = "Настройки Профит-Лиги сохранены.";
 0429        return RedirectToAction(nameof(ProfitLiga));
 0430    }
 431
 432    [HttpGet]
 433    public async Task<IActionResult> Nikei(CancellationToken cancellationToken)
 434    {
 0435        var settings = await _settingsService.GetNikeiAsync(cancellationToken);
 0436        var model = new NikeiSettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = settings.IsQ
 0437        await PopulateModelAsync(model);
 0438        return View(model);
 0439    }
 440
 441    [HttpPost, ValidateAntiForgeryToken]
 442    public async Task<IActionResult> Nikei(NikeiSettingsViewModel model, CancellationToken cancellationToken)
 443    {
 0444        await ValidateSupplierAsync(model, cancellationToken);
 0445        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0446        await SaveSettingsAsync(model, cancellationToken);
 0447        TempData["SuccessMessage"] = "Настройки Nikei сохранены.";
 0448        return RedirectToAction(nameof(Nikei));
 0449    }
 450
 451    /// <summary>Список точек грузится из API; при недоступности форма остаётся доступной,
 452    /// но связи сохранять нечем — об этом сообщает PointsLoadError.</summary>
 453    private async Task PopulateProfitLigaPointsAsync(ProfitLigaSettingsViewModel model, CancellationToken ct,
 454        bool preservePosted = false)
 455    {
 456        var warehouses = (await _warehouseDataServices.GetWarehouses()).OrderBy(x => x.Name).ToList();
 0457        if (!preservePosted)
 458        {
 459            var mapped = (await _profitLigaPointMappings.GetAsync(ct)).ToDictionary(x => x.ShipmentWarehouseId);
 0460            model.PointMappings = warehouses.Select(x => new ProfitLigaPointMappingViewModel
 0461            {
 0462                ShipmentWarehouseId = x.Id,
 0463                WarehouseName = x.Name ?? $"ID {x.Id}",
 0464                PointCode = mapped.GetValueOrDefault(x.Id)?.PointCode
 0465            }).ToList();
 0466        }
 467        else
 468        {
 469            var warehouseNames = warehouses.ToDictionary(x => x.Id, x => x.Name ?? $"ID {x.Id}");
 0470            foreach (var row in model.PointMappings)
 0471                row.WarehouseName = warehouseNames.GetValueOrDefault(row.ShipmentWarehouseId, $"ID {row.ShipmentWarehous
 472        }
 473
 474        try
 475        {
 0476            var parameters = await _profitLiga.GetOrderParamsAsync(ct);
 477            model.Points = parameters.AllPoints.Select(x => new ProfitLigaPointOptionViewModel
 478            {
 479                Code = x.Code,
 480                DisplayName = x.IsPickup ? $"Самовывоз: {x.DisplayName}" : x.DisplayName,
 481                IsPickup = x.IsPickup
 482            }).ToList();
 0483        }
 0484        catch (ProfitLigaApiException)
 485        {
 0486            model.PointsLoadError = "Профит-Лига временно недоступна: список торговых точек не загружен.";
 0487        }
 0488    }
 489
 490    private async Task PopulateMlAutoMappingsAsync(MlAutoSettingsViewModel model, CancellationToken ct, bool preservePos
 491    {
 492        var warehouses = (await _warehouseDataServices.GetWarehouses()).OrderBy(x => x.Name).ToList();
 0493        if (!preservePosted)
 494        {
 495            var mapped = (await _mlAutoDeliveryMappings.GetAsync(ct)).ToDictionary(x => x.ShipmentWarehouseId);
 0496            model.DeliveryMappings = warehouses.Select(x => new MlAutoDeliveryMappingViewModel { ShipmentWarehouseId = x
 0497        }
 498        else { var names = warehouses.ToDictionary(x => x.Id, x => x.Name ?? $"ID {x.Id}"); foreach (var row in model.De
 499        try
 500        {
 0501            var token = await _mlAuto.GetDeliveryAddressesAsync(ct);
 502            model.DeliveryAddresses = (token is Newtonsoft.Json.Linq.JContainer c ? c.DescendantsAndSelf() : [token]).Of
 0503        }
 0504        catch (MlAutoApiException) { model.DeliveryMappingsLoadError = "ML-Auto временно недоступен: список адресов не з
 0505    }
 506
 507    private async Task PopulateMlAutoRuMappingsAsync(MlAutoRuSettingsViewModel model, CancellationToken ct, bool preserv
 508    {
 509        var warehouses = (await _warehouseDataServices.GetWarehouses()).OrderBy(x => x.Name).ToList();
 0510        if (!preservePosted)
 511        {
 512            var mapped = (await _mlAutoRuDeliveryMappings.GetAsync(ct)).ToDictionary(x => x.ShipmentWarehouseId);
 0513            model.DeliveryMappings = warehouses.Select(x => new MlAutoDeliveryMappingViewModel { ShipmentWarehouseId = x
 0514        }
 515        else { var names = warehouses.ToDictionary(x => x.Id, x => x.Name ?? $"ID {x.Id}"); foreach (var row in model.De
 516        try
 517        {
 0518            var token = await _mlAutoRu.GetDeliveryAddressesAsync(ct);
 0519            model.DeliveryAddresses = ToMlAutoDeliveryAddresses(token);
 0520        }
 0521        catch (MlAutoApiException) { model.DeliveryMappingsLoadError = "ML-Auto RU временно недоступен: список адресов н
 0522    }
 523
 524    private static List<MlAutoDeliveryAddressViewModel> ToMlAutoDeliveryAddresses(Newtonsoft.Json.Linq.JToken token) =>
 0525        (token is Newtonsoft.Json.Linq.JContainer c ? c.DescendantsAndSelf() : [token]).OfType<Newtonsoft.Json.Linq.JObj
 0526        .Select(x => new MlAutoDeliveryAddressViewModel { Id = x.GetValue("ADDRESS_ID", StringComparison.OrdinalIgnoreCa
 0527        .Where(x => !string.IsNullOrWhiteSpace(x.Id)).GroupBy(x => x.Id).Select(x => { var item = x.First(); if (string.
 528
 529    [HttpGet]
 530    public async Task<IActionResult> Berg(CancellationToken cancellationToken)
 531    {
 0532        var settings = await _settingsService.GetBergAsync(cancellationToken);
 0533        var model = new BergSettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = settings.IsQu
 0534        await PopulateModelAsync(model);
 0535        await PopulateBergAddressesAsync(model, cancellationToken);
 0536        return View(model);
 0537    }
 538
 539    [HttpPost]
 540    [ValidateAntiForgeryToken]
 541    public async Task<IActionResult> Berg(BergSettingsViewModel model, CancellationToken cancellationToken)
 542    {
 0543        await ValidateSupplierAsync(model, cancellationToken);
 0544        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 545
 546        // Список адресов в форму не приходит — перечитываем его сами: без актуального списка
 547        // пустой POST стёр бы уже настроенные связи.
 0548        await PopulateBergAddressesAsync(model, cancellationToken, preservePosted: true);
 0549        if (!string.IsNullOrWhiteSpace(model.AddressesLoadError))
 550        {
 0551            ModelState.AddModelError(string.Empty, "Нельзя сохранить связи без списка адресов отгрузки Berg.");
 0552            await PopulateModelAsync(model);
 0553            return View(model);
 554        }
 555
 556        var allowed = model.Addresses.Select(x => x.Id).ToHashSet();
 0557        if (model.AddressMappings.Any(x => x.BergShipmentAddressId.HasValue && !allowed.Contains(x.BergShipmentAddressId
 558        {
 0559            ModelState.AddModelError(string.Empty, "Выберите адрес из актуального списка Berg.");
 0560            await PopulateModelAsync(model);
 0561            return View(model);
 562        }
 563
 0564        await SaveSettingsAsync(model, cancellationToken);
 565        var names = model.Addresses.ToDictionary(x => x.Id, x => x.DisplayName);
 0566        foreach (var row in model.AddressMappings)
 0567            await _bergAddressMappings.SaveAsync(row.ShipmentWarehouseId, row.BergShipmentAddressId,
 0568                row.BergShipmentAddressId.HasValue ? names.GetValueOrDefault(row.BergShipmentAddressId.Value) : null,
 0569                row.DispatchType, cancellationToken);
 570
 0571        TempData["SuccessMessage"] = "Настройки Berg сохранены.";
 0572        return RedirectToAction(nameof(Berg));
 0573    }
 574
 575    /// <summary>Адреса грузятся из API; при недоступности форма остаётся доступной,
 576    /// но связи сохранять нечем — об этом сообщает AddressesLoadError.</summary>
 577    private async Task PopulateBergAddressesAsync(BergSettingsViewModel model, CancellationToken ct,
 578        bool preservePosted = false)
 579    {
 580        var warehouses = (await _warehouseDataServices.GetWarehouses()).OrderBy(x => x.Name).ToList();
 0581        if (!preservePosted)
 582        {
 583            var mapped = (await _bergAddressMappings.GetAllAsync(ct)).ToDictionary(x => x.ShipmentWarehouseId);
 0584            model.AddressMappings = warehouses.Select(x => new BergAddressMappingViewModel
 0585            {
 0586                ShipmentWarehouseId = x.Id,
 0587                WarehouseName = x.Name ?? $"ID {x.Id}",
 0588                BergShipmentAddressId = mapped.GetValueOrDefault(x.Id)?.BergShipmentAddressId,
 0589                DispatchType = mapped.GetValueOrDefault(x.Id)?.DispatchType ?? 3
 0590            }).ToList();
 0591        }
 592        else
 593        {
 594            var warehouseNames = warehouses.ToDictionary(x => x.Id, x => x.Name ?? $"ID {x.Id}");
 0595            foreach (var row in model.AddressMappings)
 0596                row.WarehouseName = warehouseNames.GetValueOrDefault(row.ShipmentWarehouseId, $"ID {row.ShipmentWarehous
 597        }
 598
 599        try
 600        {
 0601            var response = await _berg.GetActiveShipmentAddressesAsync(ct);
 0602            model.Addresses = (response["shipment_address_list"] as Newtonsoft.Json.Linq.JArray ?? [])
 0603                .OfType<Newtonsoft.Json.Linq.JObject>()
 604                .Where(x => x.Value<int?>("state") == 1)
 605                .Select(x => new BergAddressOptionViewModel
 606                {
 607                    Id = x.Value<long?>("id") ?? 0,
 608                    DisplayName = string.Join(" · ", new[] { x.Value<string>("address"), x.Value<string>("person") }
 609                        .Where(value => !string.IsNullOrWhiteSpace(value)))
 610                })
 611                .Where(x => x.Id > 0)
 0612                .ToList();
 0613        }
 0614        catch (Services.ApiServices.BergApi.BergApiException)
 615        {
 0616            model.AddressesLoadError = "Berg временно недоступен: список адресов отгрузки не загружен.";
 0617        }
 0618    }
 619
 620    [HttpGet]
 621    public async Task<IActionResult> Motex(CancellationToken cancellationToken)
 622    {
 0623        var settings = await _settingsService.GetMotexAsync(cancellationToken);
 0624        var model = new MotexSettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = settings.IsQ
 0625        await PopulateModelAsync(model);
 0626        return View(model);
 0627    }
 628
 629    [HttpPost]
 630    [ValidateAntiForgeryToken]
 631    public async Task<IActionResult> Motex(MotexSettingsViewModel model, CancellationToken cancellationToken)
 632    {
 0633        await ValidateSupplierAsync(model, cancellationToken);
 0634        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0635        await SaveSettingsAsync(model, cancellationToken);
 0636        TempData["SuccessMessage"] = "Настройки МоТехС сохранены.";
 0637        return RedirectToAction(nameof(Motex));
 0638    }
 639
 640    [HttpGet]
 641    public async Task<IActionResult> MikadoMskHod20(CancellationToken cancellationToken)
 642    {
 0643        var settings = await _settingsService.GetMikadoMskHod20Async(cancellationToken);
 0644        var model = new MikadoMskHod20SettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = set
 0645        await PopulateModelAsync(model);
 0646        return View(model);
 0647    }
 648
 649    [HttpPost, ValidateAntiForgeryToken]
 650    public async Task<IActionResult> MikadoMskHod20(MikadoMskHod20SettingsViewModel model, CancellationToken cancellatio
 651    {
 0652        await ValidateSupplierAsync(model, cancellationToken);
 0653        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0654        await SaveSettingsAsync(model, cancellationToken);
 0655        TempData["SuccessMessage"] = "Настройки Микадо МскХод20 сохранены.";
 0656        return RedirectToAction(nameof(MikadoMskHod20));
 0657    }
 658
 659    [HttpGet]
 660    public async Task<IActionResult> ZZapMoscow(CancellationToken cancellationToken)
 661    {
 0662        var settings = await _settingsService.GetZZapMoscowAsync(cancellationToken);
 0663        var model = new ZZapMoscowSettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = setting
 0664        await PopulateModelAsync(model);
 0665        return View(model);
 0666    }
 667
 668    [HttpPost, ValidateAntiForgeryToken]
 669    public async Task<IActionResult> ZZapMoscow(ZZapMoscowSettingsViewModel model, CancellationToken cancellationToken)
 670    {
 0671        await ValidateSupplierAsync(model, cancellationToken);
 0672        if (!ModelState.IsValid) { await PopulateModelAsync(model); return View(model); }
 0673        await SaveSettingsAsync(model, cancellationToken);
 0674        TempData["SuccessMessage"] = "Настройки ZZap Москва сохранены.";
 0675        return RedirectToAction(nameof(ZZapMoscow));
 0676    }
 677
 678    [HttpGet]
 679    public async Task<IActionResult> MlAuto(CancellationToken cancellationToken)
 680    {
 0681        var settings = await _settingsService.GetMlAutoAsync(cancellationToken);
 0682        var model = new MlAutoSettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = settings.Is
 0683        await PopulateModelAsync(model);
 0684        await PopulateMlAutoMappingsAsync(model, cancellationToken);
 0685        return View(model);
 0686    }
 687
 688    [HttpPost]
 689    [ValidateAntiForgeryToken]
 690    public async Task<IActionResult> MlAuto(MlAutoSettingsViewModel model, CancellationToken cancellationToken)
 691    {
 0692        await ValidateSupplierAsync(model, cancellationToken);
 0693        if (!ModelState.IsValid) { await PopulateModelAsync(model); await PopulateMlAutoMappingsAsync(model, cancellatio
 0694        await PopulateMlAutoMappingsAsync(model, cancellationToken, true);
 0695        if (!string.IsNullOrWhiteSpace(model.DeliveryMappingsLoadError)) { ModelState.AddModelError(string.Empty, "Нельз
 696        var allowed = model.DeliveryAddresses.Select(x => x.Id).ToHashSet(StringComparer.Ordinal);
 0697        if (model.DeliveryMappings.Any(x => !string.IsNullOrWhiteSpace(x.DeliveryAddressId) && !allowed.Contains(x.Deliv
 698        await _mlAutoDeliveryMappings.ReplaceAsync(model.DeliveryMappings.Select(x => new MlAutoDeliveryAddressMappingIn
 0699        await SaveSettingsAsync(model, cancellationToken);
 0700        TempData["SuccessMessage"] = "Настройки ML-Auto сохранены.";
 0701        return RedirectToAction(nameof(MlAuto));
 0702    }
 703
 704    [HttpGet]
 705    public async Task<IActionResult> MlAutoRu(CancellationToken cancellationToken)
 706    {
 0707        var settings = await _settingsService.GetMlAutoRuAsync(cancellationToken);
 0708        var model = new MlAutoRuSettingsViewModel { CurrencyCode = settings.CurrencyCode, IsQuotationEnabled = settings.
 0709        await PopulateModelAsync(model);
 0710        await PopulateMlAutoRuMappingsAsync(model, cancellationToken);
 0711        return View(model);
 0712    }
 713
 714    [HttpPost]
 715    [ValidateAntiForgeryToken]
 716    public async Task<IActionResult> MlAutoRu(MlAutoRuSettingsViewModel model, CancellationToken cancellationToken)
 717    {
 0718        await ValidateSupplierAsync(model, cancellationToken);
 0719        if (!ModelState.IsValid) { await PopulateModelAsync(model); await PopulateMlAutoRuMappingsAsync(model, cancellat
 0720        await PopulateMlAutoRuMappingsAsync(model, cancellationToken, true);
 0721        if (!string.IsNullOrWhiteSpace(model.DeliveryMappingsLoadError)) { ModelState.AddModelError(string.Empty, "Нельз
 722        var allowed = model.DeliveryAddresses.Select(x => x.Id).ToHashSet(StringComparer.Ordinal);
 0723        if (model.DeliveryMappings.Any(x => !string.IsNullOrWhiteSpace(x.DeliveryAddressId) && !allowed.Contains(x.Deliv
 724        await _mlAutoRuDeliveryMappings.ReplaceAsync(model.DeliveryMappings.Select(x => new MlAutoRuDeliveryAddressMappi
 0725        await SaveSettingsAsync(model, cancellationToken);
 0726        TempData["SuccessMessage"] = "Настройки ML-Auto RU сохранены.";
 0727        return RedirectToAction(nameof(MlAutoRu));
 0728    }
 729
 730    private async Task PopulateRateAsync(ArmtekSettingsViewModel model)
 731    {
 732        try
 733        {
 0734            model.CurrentRateToRub = model.CurrencyCode switch
 0735            {
 0736                CurrencyCode.RUB => 1m,
 0737                CurrencyCode.USD => await _currencyRateFetcher.GetUSDRateAsync(),
 0738                CurrencyCode.EUR => await _currencyRateFetcher.GetEURRateAsync(),
 0739                CurrencyCode.BYN => await _currencyRateFetcher.GetBYNRateAsync(),
 0740                _ => null
 0741            };
 0742        }
 0743        catch
 744        {
 0745            model.CurrentRateToRub = null;
 0746        }
 0747    }
 748
 749    private async Task PopulateRateAsync(ShateMSettingsViewModel model)
 750    {
 751        try
 752        {
 0753            model.CurrentRateToRub = model.CurrencyCode switch
 0754            {
 0755                CurrencyCode.RUB => 1m,
 0756                CurrencyCode.USD => await _currencyRateFetcher.GetUSDRateAsync(),
 0757                CurrencyCode.EUR => await _currencyRateFetcher.GetEURRateAsync(),
 0758                CurrencyCode.BYN => await _currencyRateFetcher.GetBYNRateAsync(),
 0759                _ => null
 0760            };
 0761        }
 0762        catch { model.CurrentRateToRub = null; }
 0763    }
 764
 765    private async Task PopulateModelAsync(ApiSupplierSettingsViewModelBase model)
 766    {
 0767        if (string.IsNullOrWhiteSpace(model.DeliveryAddress) && !ModelState.ContainsKey(nameof(model.DeliveryAddress)) &
 0768            TryGetProviderSettings(model, out var providerKey, out var defaultCurrency))
 0769            model.DeliveryAddress = (await _settingsService.GetAsync(providerKey, defaultCurrency)).DeliveryAddress;
 770
 0771        if (!ModelState.ContainsKey(nameof(model.PriceMultiplier)) &&
 0772            TryGetProviderSettings(model, out var multiplierProviderKey, out var multiplierDefaultCurrency))
 0773            model.PriceMultiplier = (await _settingsService.GetAsync(multiplierProviderKey, multiplierDefaultCurrency)).
 774
 0775        model.Suppliers = (await _supplierDataServices.GetSuppliers())
 776            .OrderBy(x => x.Name)
 0777            .ToList();
 778        model.Warehouses = (await _warehouseDataServices.GetWarehouses()).OrderBy(x => x.Name).ToList();
 779        // При возврате формы с ошибкой отметки складов приходят из POST — перечитывать их из БД нельзя.
 0780        if (!ModelState.ContainsKey(nameof(model.WarehouseIds)) && TryGetProviderSettings(model, out var warehouseProvid
 0781            model.WarehouseIds = await _supplierWarehouses.GetWarehouseIdsAsync(warehouseProviderKey);
 0782        var selected = model.Suppliers.FirstOrDefault(x => x.Id == model.SupplierId);
 0783        if (selected != null)
 0784            model.CurrencyCode = selected.CurrencyCode;
 785
 786        switch (model)
 787        {
 788            case ArmtekSettingsViewModel armtek:
 0789                await PopulateRateAsync(armtek);
 0790                break;
 791            case ShateMSettingsViewModel shateM:
 0792                await PopulateRateAsync(shateM);
 0793                break;
 794            case ProfitLigaSettingsViewModel profitLiga:
 0795                await PopulateProfitLigaPointsAsync(profitLiga, HttpContext.RequestAborted,
 0796                    ModelState.ContainsKey($"{nameof(profitLiga.PointMappings)}[0].{nameof(ProfitLigaPointMappingViewMod
 0797                break;
 798            case AvdSettingsViewModel avd:
 0799                await PopulateRateAsync(avd);
 0800                break;
 801            case BergSettingsViewModel berg:
 0802                await PopulateRateAsync(berg);
 0803                break;
 804            case MotexSettingsViewModel motex:
 0805                await PopulateRateAsync(motex);
 0806                break;
 807            case MikadoMskHod20SettingsViewModel:
 808            case ZZapMoscowSettingsViewModel:
 809                break;
 810            case MlAutoSettingsViewModel mlAuto:
 0811                await PopulateRateAsync(mlAuto);
 0812                break;
 813            case MlAutoRuSettingsViewModel mlAutoRu:
 0814                await PopulateRateAsync(mlAutoRu);
 0815                break;
 816            case ForumAutoSettingsViewModel forumAuto:
 0817                await PopulateRateAsync(forumAuto);
 0818                break;
 819            case ForumAutoInterpartsSettingsViewModel forumAutoInterparts:
 0820                await PopulateRateAsync(forumAutoInterparts);
 0821                break;
 822            case ForumAutoPiterSettingsViewModel forumAutoPiter:
 0823                await PopulateRateAsync(forumAutoPiter);
 0824                break;
 825            case ForumAutoRostovSettingsViewModel forumAutoRostov:
 0826                await PopulateRateAsync(forumAutoRostov);
 0827                break;
 828            case ForumAutoIstraSettingsViewModel forumAutoIstra:
 0829                await PopulateRateAsync(forumAutoIstra);
 0830                break;
 831            case FavoritPartsSettingsViewModel favoritParts:
 0832                await PopulateRateAsync(favoritParts);
 0833                break;
 834            case FavoritPartsRostovSettingsViewModel favoritPartsRostov:
 0835                await PopulateRateAsync(favoritPartsRostov);
 0836                break;
 837            case MoskvorechieIstraSettingsViewModel moskvorechieIstra:
 0838                await PopulateRateAsync(moskvorechieIstra);
 839                break;
 840        }
 0841    }
 842
 843    private async Task SaveSettingsAsync(ApiSupplierSettingsViewModelBase model, CancellationToken cancellationToken)
 844    {
 0845        if (!TryGetProviderSettings(model, out var providerKey, out var defaultCurrency))
 0846            throw new InvalidOperationException($"Не определён поставщик для {model.GetType().Name}.");
 847
 0848        await _settingsService.SaveAsync(providerKey, model.SupplierId, defaultCurrency, model.IsQuotationEnabled,
 0849            model.DeliveryAddress, cancellationToken, extraDeliveryHours: model.ExtraDeliveryHours,
 0850            priceMultiplier: model.PriceMultiplier, displayName: model.DisplayName);
 0851        await _supplierWarehouses.ReplaceAsync(providerKey, model.WarehouseIds, cancellationToken);
 0852    }
 853
 854    private static bool TryGetProviderSettings(ApiSupplierSettingsViewModelBase model, out string providerKey,
 855        out CurrencyCode defaultCurrency)
 856    {
 0857        (providerKey, defaultCurrency) = model switch
 0858        {
 0859            ArmtekBySettingsViewModel => (ApiSupplierSettings.ArmtekByProviderKey, CurrencyCode.BYN),
 0860            ArmtekSettingsViewModel => (ApiSupplierSettings.ArmtekProviderKey, CurrencyCode.RUB),
 0861            ShateMSettingsViewModel => (ApiSupplierSettings.ShateMProviderKey, CurrencyCode.BYN),
 0862            AvdSettingsViewModel => (ApiSupplierSettings.AvdProviderKey, CurrencyCode.RUB),
 0863            BergSettingsViewModel => (ApiSupplierSettings.BergProviderKey, CurrencyCode.RUB),
 0864            MotexSettingsViewModel => (ApiSupplierSettings.MotexProviderKey, CurrencyCode.BYN),
 0865            MikadoMskHod20SettingsViewModel => (ApiSupplierSettings.MikadoMskHod20ProviderKey, CurrencyCode.RUB),
 0866            MlAutoSettingsViewModel => (ApiSupplierSettings.MlAutoProviderKey, CurrencyCode.BYN),
 0867            MlAutoRuSettingsViewModel => (ApiSupplierSettings.MlAutoRuProviderKey, CurrencyCode.RUB),
 0868            ForumAutoSettingsViewModel => (ApiSupplierSettings.ForumAutoProviderKey, CurrencyCode.RUB),
 0869            ForumAutoInterpartsSettingsViewModel => (ApiSupplierSettings.ForumAutoInterpartsProviderKey, CurrencyCode.RU
 0870            ForumAutoPiterSettingsViewModel => (ApiSupplierSettings.ForumAutoPiterProviderKey, CurrencyCode.RUB),
 0871            ForumAutoRostovSettingsViewModel => (ApiSupplierSettings.ForumAutoRostovProviderKey, CurrencyCode.RUB),
 0872            ForumAutoIstraSettingsViewModel => (ApiSupplierSettings.ForumAutoIstraProviderKey, CurrencyCode.RUB),
 0873            FavoritPartsSettingsViewModel => (ApiSupplierSettings.FavoritPartsProviderKey, CurrencyCode.RUB),
 0874            FavoritPartsIstraSettingsViewModel => (ApiSupplierSettings.FavoritPartsIstraProviderKey, CurrencyCode.RUB),
 0875            FavoritPartsRostovSettingsViewModel => (ApiSupplierSettings.FavoritPartsRostovProviderKey, CurrencyCode.RUB)
 0876            MoskvorechieIstraSettingsViewModel => (ApiSupplierSettings.MoskvorechieIstraProviderKey, CurrencyCode.RUB),
 0877            ProfitLigaSettingsViewModel => (ApiSupplierSettings.ProfitLigaProviderKey, CurrencyCode.RUB),
 0878            NikeiSettingsViewModel => (ApiSupplierSettings.NikeiProviderKey, CurrencyCode.RUB),
 0879            JapartsSettingsViewModel => (ApiSupplierSettings.JapartsProviderKey, CurrencyCode.RUB),
 0880            ZZapMoscowSettingsViewModel => (ApiSupplierSettings.ZZapMoscowProviderKey, CurrencyCode.RUB),
 0881            _ => (string.Empty, CurrencyCode.RUB)
 0882        };
 0883        return !string.IsNullOrEmpty(providerKey);
 884    }
 885
 886    private async Task PopulateArmtekMappingsAsync(ArmtekSettingsViewModel model, string providerKey,
 887        CancellationToken cancellationToken, bool preservePostedMappings = false)
 888    {
 889        var warehouses = (await _warehouseDataServices.GetWarehouses()).OrderBy(x => x.Name).ToList();
 0890        if (!preservePostedMappings)
 891        {
 0892            var mappings = await _armtekDeliveryAddressMappings.GetAsync(providerKey, cancellationToken);
 893            var byWarehouse = mappings.ToDictionary(x => x.ShipmentWarehouseId);
 0894            model.DeliveryMappings = warehouses.Select(warehouse =>
 0895            {
 0896                byWarehouse.TryGetValue(warehouse.Id, out var mapping);
 0897                return new ArmtekDeliveryAddressMappingViewModel
 0898                {
 0899                    ShipmentWarehouseId = warehouse.Id,
 0900                    WarehouseName = warehouse.Name ?? $"ID {warehouse.Id}",
 0901                    BuyerKunnr = mapping?.BuyerKunnr,
 0902                    DeliveryAddressKunnr = mapping?.DeliveryAddressKunnr
 0903                };
 0904            }).ToList();
 0905        }
 906        else
 907        {
 908            var names = warehouses.ToDictionary(x => x.Id, x => x.Name ?? $"ID {x.Id}");
 0909            foreach (var mapping in model.DeliveryMappings)
 0910                mapping.WarehouseName = names.GetValueOrDefault(mapping.ShipmentWarehouseId, $"ID {mapping.ShipmentWareh
 911        }
 912
 913        try
 914        {
 0915            var client = providerKey == ApiSupplierSettings.ArmtekByProviderKey ? (ArmtekDataManager)_armtekBy : _armtek
 0916            var response = await client.GetUserInfoAsync(structure: true, ct: cancellationToken);
 0917            if (response.HasErrors)
 918            {
 0919                model.DeliveryMappingsLoadError = "Armtek не вернул список клиентов и адресов. Сохранённые коды можно ув
 0920                return;
 921            }
 0922            model.ArmtekBuyers = ToBuyerViewModels(response.Resp);
 0923        }
 0924        catch (Exception ex) when (ex is ArmtekApiException or HttpRequestException)
 925        {
 0926            model.DeliveryMappingsLoadError = "Armtek временно недоступен. Повторите загрузку страницы позже.";
 0927        }
 0928    }
 929
 930    private async Task ValidateAndSaveArmtekMappingsAsync(ArmtekSettingsViewModel model, string providerKey,
 931        CancellationToken cancellationToken)
 932    {
 0933        await PopulateArmtekMappingsAsync(model, providerKey, cancellationToken, preservePostedMappings: true);
 0934        if (!string.IsNullOrWhiteSpace(model.DeliveryMappingsLoadError))
 935        {
 0936            ModelState.AddModelError(string.Empty, "Нельзя сохранить связи, пока Armtek не вернул актуальный список адре
 0937            return;
 938        }
 939
 940        var buyers = model.ArmtekBuyers.ToDictionary(x => x.Kunnr, StringComparer.Ordinal);
 941        foreach (var mapping in model.DeliveryMappings.Where(x => !string.IsNullOrWhiteSpace(x.BuyerKunnr) || !string.Is
 942        {
 0943            if (string.IsNullOrWhiteSpace(mapping.BuyerKunnr) || string.IsNullOrWhiteSpace(mapping.DeliveryAddressKunnr)
 0944                !buyers.TryGetValue(mapping.BuyerKunnr, out var buyer) ||
 0945                !buyer.DeliveryAddresses.Any(x => x.Kunnr == mapping.DeliveryAddressKunnr))
 0946                ModelState.AddModelError(string.Empty, $"Для склада «{mapping.WarehouseName}» выберите адрес выбранного 
 947        }
 0948        if (!ModelState.IsValid)
 0949            return;
 950
 0951        await _armtekDeliveryAddressMappings.ReplaceAsync(providerKey,
 952            model.DeliveryMappings.Select(x => new ArmtekDeliveryAddressMappingInput(x.ShipmentWarehouseId, x.BuyerKunnr
 0953            cancellationToken);
 0954    }
 955
 956    private static List<ArmtekBuyerAddressViewModel> ToBuyerViewModels(ArmtekUserInfoResponse? userInfo) =>
 0957        (userInfo?.Structure.RgTab ?? [])
 0958        .Where(x => !string.IsNullOrWhiteSpace(x.Kunnr))
 0959        .Select(buyer => new ArmtekBuyerAddressViewModel
 0960        {
 0961            Kunnr = buyer.Kunnr,
 0962            DisplayName = $"{buyer.Kunnr} — {FirstNotEmpty(buyer.FullName, buyer.ShortName, "Без названия")}",
 0963            DeliveryAddresses = buyer.ZaTab.Where(x => !string.IsNullOrWhiteSpace(x.Kunnr)).Select(address => new Armtek
 0964            {
 0965                Kunnr = address.Kunnr,
 0966                DisplayName = string.Concat(
 0967                    $"{address.Kunnr} — {FirstNotEmpty(address.ShortName, address.FullName, "Без названия")}",
 0968                    string.IsNullOrWhiteSpace(address.Address) ? string.Empty : $"; {address.Address}")
 0969            }).ToList()
 0970        }).ToList();
 971
 0972    private static string FirstNotEmpty(params string[] values) => values.FirstOrDefault(x => !string.IsNullOrWhiteSpace
 973
 974    private Task PopulateRateAsync(AvdSettingsViewModel model)
 975    {
 0976        model.CurrentRateToRub = model.CurrencyCode == CurrencyCode.RUB ? 1m : null;
 0977        return Task.CompletedTask;
 978    }
 979
 980    private Task PopulateRateAsync(BergSettingsViewModel model)
 981    {
 0982        model.CurrentRateToRub = model.CurrencyCode == CurrencyCode.RUB ? 1m : null;
 0983        return Task.CompletedTask;
 984    }
 985
 986    private async Task PopulateRateAsync(MotexSettingsViewModel model)
 987    {
 988        try
 989        {
 0990            model.CurrentRateToRub = model.CurrencyCode switch
 0991            {
 0992                CurrencyCode.RUB => 1m,
 0993                CurrencyCode.USD => await _currencyRateFetcher.GetUSDRateAsync(),
 0994                CurrencyCode.EUR => await _currencyRateFetcher.GetEURRateAsync(),
 0995                CurrencyCode.BYN => await _currencyRateFetcher.GetBYNRateAsync(),
 0996                _ => null
 0997            };
 0998        }
 0999        catch { model.CurrentRateToRub = null; }
 01000    }
 1001
 1002    private async Task PopulateRateAsync(MlAutoSettingsViewModel model)
 1003    {
 01004        try { model.CurrentRateToRub = await _currencyRateFetcher.GetBYNRateAsync(); }
 01005        catch { model.CurrentRateToRub = null; }
 01006    }
 1007
 1008    private Task PopulateRateAsync(MlAutoRuSettingsViewModel model)
 1009    {
 01010        model.CurrentRateToRub = model.CurrencyCode == CurrencyCode.RUB ? 1m : null;
 01011        return Task.CompletedTask;
 1012    }
 1013
 1014    private Task PopulateRateAsync(ForumAutoSettingsViewModel model)
 1015    {
 01016        model.CurrentRateToRub = model.CurrencyCode == CurrencyCode.RUB ? 1m : null;
 01017        return Task.CompletedTask;
 1018    }
 1019
 1020    private Task PopulateRateAsync(ForumAutoInterpartsSettingsViewModel model)
 1021    {
 01022        model.CurrentRateToRub = model.CurrencyCode == CurrencyCode.RUB ? 1m : null;
 01023        return Task.CompletedTask;
 1024    }
 1025
 1026    private Task PopulateRateAsync(ForumAutoPiterSettingsViewModel model)
 1027    {
 01028        model.CurrentRateToRub = model.CurrencyCode == CurrencyCode.RUB ? 1m : null;
 01029        return Task.CompletedTask;
 1030    }
 1031
 1032    private Task PopulateRateAsync(ForumAutoRostovSettingsViewModel model)
 1033    {
 01034        model.CurrentRateToRub = model.CurrencyCode == CurrencyCode.RUB ? 1m : null;
 01035        return Task.CompletedTask;
 1036    }
 1037
 1038    private Task PopulateRateAsync(ForumAutoIstraSettingsViewModel model)
 1039    {
 01040        model.CurrentRateToRub = model.CurrencyCode == CurrencyCode.RUB ? 1m : null;
 01041        return Task.CompletedTask;
 1042    }
 1043
 1044    private Task PopulateRateAsync(FavoritPartsSettingsViewModel model)
 1045    {
 01046        model.CurrentRateToRub = model.CurrencyCode == CurrencyCode.RUB ? 1m : null;
 01047        return Task.CompletedTask;
 1048    }
 1049
 1050    private Task PopulateRateAsync(FavoritPartsRostovSettingsViewModel model)
 1051    {
 01052        model.CurrentRateToRub = model.CurrencyCode == CurrencyCode.RUB ? 1m : null;
 01053        return Task.CompletedTask;
 1054    }
 1055
 1056    private Task PopulateRateAsync(MoskvorechieIstraSettingsViewModel model)
 1057    {
 01058        model.CurrentRateToRub = 1m;
 01059        return Task.CompletedTask;
 1060    }
 1061
 1062    private async Task ValidateSupplierAsync(ApiSupplierSettingsViewModelBase model, CancellationToken cancellationToken
 1063    {
 01064        if (!model.SupplierId.HasValue)
 01065            return;
 1066
 01067        var supplier = await _supplierDataServices.GetSupplierAsync(model.SupplierId.Value);
 01068        if (supplier == null)
 01069            ModelState.AddModelError(nameof(model.SupplierId), "Выбранный системный поставщик не найден.");
 01070    }
 1071}

Methods/Properties

.ctor(Services.ApiSupplierSettingsServices.ApiSupplierSettingsService,Services.PriceСlculationServices.ICurrencyRateFetcher,Services.DataServices.SupplierDataServices,Services.DataServices.WarehouseDataServices,Services.ApiSupplierSettingsServices.ArmtekDeliveryAddressMappingService,Services.ApiServices.ArmtekApi.ArmtekDataManager,Services.ApiServices.ArmtekApi.ArmtekByDataManager,Services.ApiSupplierSettingsServices.MlAutoDeliveryAddressMappingService,Services.ApiServices.MlAutoApi.MlAutoDataManager,Services.ApiSupplierSettingsServices.ProfitLigaPointMappingService,Services.ApiServices.ProfitLigaApi.ProfitLigaDataManager,Services.ApiSupplierSettingsServices.BergDeliveryAddressMappingService,Services.ApiServices.BergApi.BergDataManager,Services.ApiSupplierSettingsServices.MlAutoRuDeliveryAddressMappingService,Services.ApiServices.MlAutoApi.MlAutoRuDataManager,Services.ApiSupplierSettingsServices.ApiSupplierWarehouseService)
Index()
Japarts()
Japarts()
Armtek()
Armtek()
ArmtekBy()
ArmtekBy()
ShateM()
ShateM()
Avd()
Avd()
ForumAuto()
ForumAuto()
ForumAutoInterparts()
ForumAutoInterparts()
ForumAutoPiter()
ForumAutoPiter()
ForumAutoRostov()
ForumAutoRostov()
ForumAutoIstra()
ForumAutoIstra()
FavoritParts()
FavoritParts()
FavoritPartsIstra()
FavoritPartsIstra()
FavoritPartsRostov()
FavoritPartsRostov()
MoskvorechieIstra()
MoskvorechieIstra()
ProfitLiga()
ProfitLiga()
Nikei()
Nikei()
PopulateProfitLigaPointsAsync()
PopulateMlAutoMappingsAsync()
PopulateMlAutoRuMappingsAsync()
ToMlAutoDeliveryAddresses(Newtonsoft.Json.Linq.JToken)
Berg()
Berg()
PopulateBergAddressesAsync()
Motex()
Motex()
MikadoMskHod20()
MikadoMskHod20()
ZZapMoscow()
ZZapMoscow()
MlAuto()
MlAuto()
MlAutoRu()
MlAutoRu()
PopulateRateAsync()
PopulateRateAsync()
PopulateModelAsync()
SaveSettingsAsync()
TryGetProviderSettings(OzonOrdersWeb.Areas.Studio2.ViewModels.ApiSupplierSettings.ApiSupplierSettingsViewModelBase,System.String&,OzonDomains.CurrencyCode&)
PopulateArmtekMappingsAsync()
ValidateAndSaveArmtekMappingsAsync()
ToBuyerViewModels(Services.ApiServices.ArmtekApi.Models.Response.ArmtekUserInfoResponse)
FirstNotEmpty(System.String[])
PopulateRateAsync(OzonOrdersWeb.Areas.Studio2.ViewModels.ApiSupplierSettings.AvdSettingsViewModel)
PopulateRateAsync(OzonOrdersWeb.Areas.Studio2.ViewModels.ApiSupplierSettings.BergSettingsViewModel)
PopulateRateAsync()
PopulateRateAsync()
PopulateRateAsync(OzonOrdersWeb.Areas.Studio2.ViewModels.ApiSupplierSettings.MlAutoRuSettingsViewModel)
PopulateRateAsync(OzonOrdersWeb.Areas.Studio2.ViewModels.ApiSupplierSettings.ForumAutoSettingsViewModel)
PopulateRateAsync(OzonOrdersWeb.Areas.Studio2.ViewModels.ApiSupplierSettings.ForumAutoInterpartsSettingsViewModel)
PopulateRateAsync(OzonOrdersWeb.Areas.Studio2.ViewModels.ApiSupplierSettings.ForumAutoPiterSettingsViewModel)
PopulateRateAsync(OzonOrdersWeb.Areas.Studio2.ViewModels.ApiSupplierSettings.ForumAutoRostovSettingsViewModel)
PopulateRateAsync(OzonOrdersWeb.Areas.Studio2.ViewModels.ApiSupplierSettings.ForumAutoIstraSettingsViewModel)
PopulateRateAsync(OzonOrdersWeb.Areas.Studio2.ViewModels.ApiSupplierSettings.FavoritPartsSettingsViewModel)
PopulateRateAsync(OzonOrdersWeb.Areas.Studio2.ViewModels.ApiSupplierSettings.FavoritPartsRostovSettingsViewModel)
PopulateRateAsync(OzonOrdersWeb.Areas.Studio2.ViewModels.ApiSupplierSettings.MoskvorechieIstraSettingsViewModel)
ValidateSupplierAsync()