Webiant Logo Webiant Logo
  1. No results found.

    Try your search with a different keyword or use * as a wildcard.

RfqAdminController.cs

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Nop.Core.Domain.Orders;
using Nop.Plugin.Misc.RFQ.Domains;
using Nop.Plugin.Misc.RFQ.Factories;
using Nop.Plugin.Misc.RFQ.Models.Admin;
using Nop.Plugin.Misc.RFQ.Services;
using Nop.Services.Catalog;
using Nop.Services.Configuration;
using Nop.Services.Customers;
using Nop.Services.Localization;
using Nop.Services.Messages;
using Nop.Services.Orders;
using Nop.Services.Security;
using Nop.Web.Areas.Admin.Factories;
using Nop.Web.Areas.Admin.Models.Customers;
using Nop.Web.Framework;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Mvc;
using Nop.Web.Framework.Mvc.Filters;

namespace Nop.Plugin.Misc.RFQ.Controllers;

[Area(AreaNames.ADMIN)]
[AuthorizeAdmin]
[AutoValidateAntiforgeryToken]
public class RfqAdminController : BasePluginController
{
    #region Fields

    private readonly AdminModelFactory _modelFactory;
    private readonly ICustomerModelFactory _customerModelFactory;
    private readonly ICustomerService _customerService;
    private readonly ILocalizationService _localizationService;
    private readonly INotificationService _notificationService;
    private readonly IProductAttributeParser _productAttributeParser;
    private readonly IProductAttributeService _productAttributeService;
    private readonly IProductService _productService;
    private readonly ISettingService _settingService;
    private readonly IShoppingCartService _shoppingCartService;
    private readonly RfqMessageService _rfqMessageService;
    private readonly RfqService _rfqService;
    private readonly RfqSettings _rfqSettings;

    #endregion

    #region Ctor

    public RfqAdminController(AdminModelFactory modelFactory,
        ICustomerModelFactory customerModelFactory,
        ICustomerService customerService,
        ILocalizationService localizationService,
        INotificationService notificationService,
        IProductAttributeParser productAttributeParser,
        IProductAttributeService productAttributeService,
        IProductService productService,
        ISettingService settingService,
        IShoppingCartService shoppingCartService,
        RfqMessageService rfqMessageService,
        RfqService rfqService,
        RfqSettings rfqSettings)
    {
        _modelFactory = modelFactory;
        _customerModelFactory = customerModelFactory;
        _customerService = customerService;
        _localizationService = localizationService;
        _productAttributeParser = productAttributeParser;
        _productAttributeService = productAttributeService;
        _productService = productService;
        _notificationService = notificationService;
        _settingService = settingService;
        _shoppingCartService = shoppingCartService;
        _rfqMessageService = rfqMessageService;
        _rfqService = rfqService;
        _rfqSettings = rfqSettings;
    }

    #endregion

    #region Methods

    #region Configure

    public async Task Configure()
    {
        var model = new ConfigurationModel
        {
            Enabled = _rfqSettings.Enabled
        };

        return View("~/Plugins/Misc.RFQ/Views/Admin/Configure.cshtml", model);
    }

    [HttpPost, ActionName("Configure")]
    [FormValueRequired("save")]
    public async Task Configure(ConfigurationModel model)
    {
        if (!ModelState.IsValid)
            return await Configure();
        
        _rfqSettings.Enabled = model.Enabled;

        await _settingService.SaveSettingAsync(_rfqSettings);
        
        _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Admin.Plugins.Saved"));

        return await Configure();
    }

    #endregion

    #region Add product to quote

    [HttpPost]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    [CheckPermission(StandardPermission.Catalog.PRODUCTS_VIEW)]
    public async Task AddNewProduct(ProductSearchModel searchModel)
    {
        //prepare model
        var model = await _modelFactory.PrepareProductListModelAsync(searchModel);

        return Json(model);
    }

    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    [CheckPermission(StandardPermission.Catalog.PRODUCTS_VIEW)]
    public async Task AddProductDetails(int productId, int quoteId)
    {
        var quote = await _rfqService.GetQuoteByIdAsync(quoteId)
            ?? throw new ArgumentException("No quote found with the specified id");

        var product = await _productService.GetProductByIdAsync(productId)
            ?? throw new ArgumentException("No product found with the specified id");

        //prepare model
        var model = await _modelFactory.PrepareAddProductModelAsync(new AddProductModel(), quote, product);

        return View("~/Plugins/Misc.RFQ/Views/Admin/AddProductDetails.cshtml", model);
    }

    [HttpPost]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    [CheckPermission(StandardPermission.Catalog.PRODUCTS_VIEW)]
    public async Task AddProductDetails(int quoteId, int productId, IFormCollection form)
    {
        var product = await _productService.GetProductByIdAsync(productId)
            ?? throw new ArgumentException("No product found with the specified id");

        //basic properties
        _ = decimal.TryParse(form["UnitPriceInclTax"], out var unitPriceInclTax);
        _ = int.TryParse(form["Quantity"], out var quantity);

        //warnings
        var warnings = new List();

        //attributes
        var attributesXml = await _productAttributeParser.ParseProductAttributesAsync(product, form, warnings);

        //rental product
        _productAttributeParser.ParseRentalDates(product, form, out _, out _);

        var quote = await _rfqService.GetQuoteByIdAsync(quoteId)
                ?? throw new ArgumentException("No quote found with the specified id");

        //try to get a customer with the specified id
        var customer = await _customerService.GetCustomerByIdAsync(quote.CustomerId)
            ?? throw new ArgumentException("No customer found with the specified id");

        //warnings
        warnings.AddRange(await _shoppingCartService.GetShoppingCartItemAttributeWarningsAsync(customer, ShoppingCartType.ShoppingCart, product, quantity, attributesXml));

        if (!warnings.Any())
        {
            var quoteItem = new QuoteItem
            {
                AttributesXml = attributesXml,
                ProductId = product.Id,
                OfferedQty = quantity,
                QuoteId = quoteId,
                OfferedUnitPrice = unitPriceInclTax
            };

            await _rfqService.InsertQuoteItemAsync(quoteItem);

            return RedirectToAction("AdminQuote", new { id = quoteId });
        }

        //prepare model
        var model = await _modelFactory.PrepareAddProductModelAsync(new AddProductModel(), quote, product);
        model.Warnings.AddRange(warnings);

        return View("~/Plugins/Misc.RFQ/Views/Admin/AddProductDetails.cshtml", model);
    }

    [HttpPost]
    public async Task ProductDetailsAttributeChange(int productId, bool validateAttributeConditions, IFormCollection form)
    {
        var product = await _productService.GetProductByIdAsync(productId);
        if (product == null)
            return new NullJsonResult();

        var errors = new List();
        var attributeXml = await _productAttributeParser.ParseProductAttributesAsync(product, form, errors);

        //conditional attributes
        var enabledAttributeMappingIds = new List();
        var disabledAttributeMappingIds = new List();

        if (validateAttributeConditions)
        {
            var attributes = await _productAttributeService.GetProductAttributeMappingsByProductIdAsync(product.Id);

            foreach (var attribute in attributes)
            {
                var conditionMet = await _productAttributeParser.IsConditionMetAsync(attribute, attributeXml);

                if (!conditionMet.HasValue)
                    continue;

                if (conditionMet.Value)
                    enabledAttributeMappingIds.Add(attribute.Id);
                else
                    disabledAttributeMappingIds.Add(attribute.Id);
            }
        }

        return Json(new
        {
            enabledattributemappingids = enabledAttributeMappingIds.ToArray(),
            disabledattributemappingids = disabledAttributeMappingIds.ToArray(),
            message = errors.Any() ? errors.ToArray() : null
        });
    }

    [HttpPost]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    [CheckPermission(StandardPermission.Customers.CUSTOMERS_VIEW)]
    public async Task CustomerList(CustomerSearchModel searchModel)
    {
        //prepare model
        var model = await _customerModelFactory.PrepareCustomerListModelAsync(searchModel);

        return Json(model);
    }

    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    [CheckPermission(StandardPermission.Customers.CUSTOMERS_VIEW)]
    public async Task SelectCustomerPopup()
    {
        //prepare model
        var model = await _customerModelFactory.PrepareCustomerSearchModelAsync(new CustomerSearchModel());

        return View("~/Plugins/Misc.RFQ/Views/Admin/SelectCustomerPopup.cshtml", model);
    }

    #endregion

    #region Request a quote

    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task AdminRequests()
    {
        //prepare model
        var model = await _modelFactory.PrepareRequestQuoteSearchModelAsync();

        return View("~/Plugins/Misc.RFQ/Views/Admin/AdminRequests.cshtml", model);
    }

    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task AdminRequest(int id)
    {
        if (id <= 0)
            return RedirectToAction("AdminRequests");

        var requestQuote = await _rfqService.GetRequestQuoteByIdAsync(id);

        if (requestQuote == null)
            return RedirectToAction("AdminRequests");

        var model = await _modelFactory.PreparedRequestQuoteModelAsync(requestQuote);

        return View("~/Plugins/Misc.RFQ/Views/Admin/AdminRequest.cshtml", model);
    }

    [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")]
    [FormValueRequired("save", "save-continue")]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task AdminRequest(RequestQuoteModel model, bool continueEditing)
    {
        var requestQuote = await _rfqService.GetRequestQuoteByIdAsync(model.Id);

        if (requestQuote == null)
            return RedirectToAction("AdminRequests");

        if (ModelState.IsValid)
        {
            if (requestQuote.AdminNotes != model.AdminNotes)
            {
                requestQuote.AdminNotes = model.AdminNotes;
                await _rfqService.UpdateRequestQuoteAsync(requestQuote);
            }

            _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Plugins.Misc.RFQ.AdminRequest.Updated"));

            return !continueEditing ? RedirectToAction("AdminRequests") : RedirectToAction("AdminRequest", new { id = requestQuote.Id });
        }

        //if we got this far, something failed, redisplay form
        model = await _modelFactory.PreparedRequestQuoteModelAsync(requestQuote);

        return View("~/Plugins/Misc.RFQ/Views/Admin/AdminRequest.cshtml", model);
    }

    [HttpPost, ActionName("AdminRequest")]
    [FormValueRequired(FormValueRequirement.StartsWith, "btnSave")]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task EditRequestItem(int id, IFormCollection form)
    {
        if (id <= 0)
            return RedirectToAction("AdminRequests");

        var requestQuote = await _rfqService.GetRequestQuoteByIdAsync(id);

        if (requestQuote == null)
            return RedirectToAction("AdminRequests");

        //get request a quote item identifier
        var requestQuoteItemId = 0;

        var saveButtonId = form
            .FirstOrDefault(p => p.Key.StartsWith("btnSave", StringComparison.InvariantCultureIgnoreCase)).Key;

        if (!string.IsNullOrEmpty(saveButtonId))
            requestQuoteItemId = Convert.ToInt32(saveButtonId["btnSave".Length..]);

        if (requestQuoteItemId <= 0)
            return await AdminRequest(id);

        int.TryParse(form[$"quantity{requestQuoteItemId}"], out var requestedQty);
        decimal.TryParse(form[$"unitPrice{requestQuoteItemId}"], out var requestedUnitPrice);

        await _rfqService.UpdateRequestQuoteItemAsync(requestQuoteItemId, requestedQty, requestedUnitPrice);

        if (requestQuote.AdminNotes != form["AdminNotes"])
        {
            requestQuote.AdminNotes = form["AdminNotes"];
            await _rfqService.UpdateRequestQuoteAsync(requestQuote);
        }

        _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Plugins.Misc.RFQ.AdminRequest.Updated"));

        return await AdminRequest(id);
    }

    [HttpPost, ActionName("AdminRequest")]
    [FormValueRequired(FormValueRequirement.StartsWith, "btnDelete")]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task DeleteRequestItem(int id, IFormCollection form)
    {
        if (id <= 0)
            return RedirectToAction("AdminRequests");

        var requestQuote = await _rfqService.GetRequestQuoteByIdAsync(id);

        if (requestQuote == null)
            return RedirectToAction("AdminRequests");

        //get request a quote item identifier
        var requestQuoteItemId = 0;

        var deleteButtonId = form
            .FirstOrDefault(p => p.Key.StartsWith("btnDelete", StringComparison.InvariantCultureIgnoreCase)).Key;

        if (!string.IsNullOrEmpty(deleteButtonId))
            requestQuoteItemId = Convert.ToInt32(deleteButtonId["btnDelete".Length..]);

        if (requestQuoteItemId <= 0)
            return await AdminRequest(id);

        await _rfqService.DeleteRequestQuoteItemAsync(requestQuoteItemId);

        _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Plugins.Misc.RFQ.AdminRequest.Updated"));

        return await AdminRequest(id);
    }

    [HttpPost, ActionName("AdminRequest")]
    [FormValueRequired("deleteRequest")]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task DeleteRequest(RequestQuoteModel model)
    {
        await _rfqService.DeleteRequestQuoteAsync(model.Id);

        _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Plugins.Misc.RFQ.AdminRequest.Deleted"));

        return RedirectToAction("AdminRequests");
    }

    [HttpPost, ActionName("AdminRequest")]
    [FormValueRequired("cancelRequest")]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task CancelRequest(RequestQuoteModel model)
    {
        var requestQuote = await _rfqService.GetRequestQuoteByIdAsync(model.Id);

        if (requestQuote == null)
            return RedirectToAction("AdminRequests");

        await _rfqService.ChangeAndLogRequestQuoteStatusAsync(requestQuote, RequestQuoteStatus.Canceled);
        await _rfqService.UpdateRequestQuoteAsync(requestQuote);

        _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Plugins.Misc.RFQ.AdminRequest.Canceled"));

        return RedirectToAction("AdminRequest", new { id = requestQuote.Id });
    }

    [HttpPost]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task DeleteSelectedRequests(ICollection selectedIds)
    {
        await _rfqService.DeleteRequestsQuoteByIdsAsync(selectedIds);

        _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Plugins.Misc.RFQ.AdminRequest.Selected.Deleted"));

        return new NullJsonResult();
    }

    [HttpPost]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task RequestList(RequestQuoteSearchModel searchModel)
    {
        //prepare model
        var model = await _modelFactory.PrepareRequestQuoteListModelAsync(searchModel);

        return Json(model);
    }

    #endregion

    #region Quote

    [HttpPost]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task CreateQuote(RequestQuoteModel model)
    {
        var quoteId = await _rfqService.CreateQuoteByRequestAsync(model.Id);

        return RedirectToAction("AdminQuote", new { id = quoteId });
    }

    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task AdminQuote(int id)
    {
        if (id <= 0)
            return RedirectToAction("AdminQuotes");

        var quote = await _rfqService.GetQuoteByIdAsync(id);

        if (quote == null)
            return RedirectToAction("AdminQuotes");

        var model = await _modelFactory.PreparedQuoteModelAsync(quote);

        return View("~/Plugins/Misc.RFQ/Views/Admin/AdminQuote.cshtml", model);
    }

    [HttpPost]
    [FormValueRequired("btnCreate")]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task AdminQuotes(QuoteSearchModel model)
    {
        var requestQuote = await _rfqService.CreateQuoteAsync(model.CustomerId);

        return RedirectToAction("AdminQuote", new { id = requestQuote.Id });
    }

    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task AdminQuotes()
    {
        //prepare model
        var model = await _modelFactory.PrepareQuoteSearchModelAsync();

        return View("~/Plugins/Misc.RFQ/Views/Admin/AdminQuotes.cshtml", model);
    }

    [HttpPost]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task DeleteSelectedQuotes(ICollection selectedIds)
    {
        await _rfqService.DeleteQuotesByIdsAsync(selectedIds);

        _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Plugins.Misc.RFQ.AdminQuote.Selected.Deleted"));

        return new NullJsonResult();
    }

    [HttpPost]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task QuoteList(QuoteSearchModel searchModel)
    {
        //prepare model
        var model = await _modelFactory.PrepareQuoteListModelAsync(searchModel);

        return Json(model);
    }

    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    [CheckPermission(StandardPermission.Catalog.PRODUCTS_VIEW)]
    public async Task AddProductToQuote(int quoteId)
    {
        var quote = await _rfqService.GetQuoteByIdAsync(quoteId);

        if (quote == null)
            return RedirectToAction("AdminQuotes");

        //prepare model
        var model = await _modelFactory.PrepareAddProductSearchModelAsync(new ProductSearchModel(), quote.Id);

        return View("~/Plugins/Misc.RFQ/Views/Admin/AddProductToQuote.cshtml", model);
    }

    [HttpPost, ActionName("AdminQuote")]
    [FormValueRequired("sendQuote")]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task SendQuote(QuoteModel model)
    {
        var quote = await _rfqService.GetQuoteByIdAsync(model.Id);

        if (quote == null)
            return RedirectToAction("AdminQuotes");

        await _rfqService.ChangeAndLogQuoteStatusAsync(quote, QuoteStatus.Submitted);
        await _rfqService.UpdateQuoteAsync(quote);

        await _rfqMessageService.AdminSentNewQuoteAsync(quote);
        _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Plugins.Misc.RFQ.AdminQuote.QuoteSubmitted"));

        return RedirectToAction("AdminQuote", new { id = quote.Id });
    }

    [HttpPost, ActionName("AdminQuote")]
    [FormValueRequired("deleteQuote")]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task DeleteQuote(QuoteModel model)
    {
        await _rfqService.DeleteQuoteAsync(model.Id);

        _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Plugins.Misc.RFQ.AdminQuote.Deleted"));

        return RedirectToAction("AdminQuotes");
    }

    [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")]
    [FormValueRequired("save", "save-continue")]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task AdminQuote(QuoteModel model, bool continueEditing)
    {
        var quote = await _rfqService.GetQuoteByIdAsync(model.Id);

        if (quote == null)
            return RedirectToAction("AdminQuotes");

        if (ModelState.IsValid)
        {
            var needUpdate = false;

            if (quote.AdminNotes != model.AdminNotes)
            {
                needUpdate = true;
                quote.AdminNotes = model.AdminNotes;
            }

            if (quote.ExpirationDateUtc != model.ExpirationDateUtc)
            {
                needUpdate = true;
                quote.ExpirationDateUtc = model.ExpirationDateUtc;
            }

            if (needUpdate)
                await _rfqService.UpdateQuoteAsync(quote);

            _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Plugins.Misc.RFQ.AdminQuote.Updated"));

            return !continueEditing ? RedirectToAction("AdminQuotes") : RedirectToAction("AdminQuote", new { id = quote.Id });
        }

        //if we got this far, something failed, redisplay form
        model = await _modelFactory.PreparedQuoteModelAsync(quote);

        return View("~/Plugins/Misc.RFQ/Views/Admin/AdminQuote.cshtml", model);
    }

    [HttpPost, ActionName("AdminQuote")]
    [FormValueRequired(FormValueRequirement.StartsWith, "btnSave")]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task EditQuoteItem(int id, IFormCollection form)
    {
        if (id <= 0)
            return RedirectToAction("AdminQuotes");

        var quote = await _rfqService.GetQuoteByIdAsync(id);

        if (quote == null)
            return RedirectToAction("AdminQuotes");

        //get the quote item identifier
        var quoteItemId = 0;

        var saveButtonId = form
            .FirstOrDefault(p => p.Key.StartsWith("btnSave", StringComparison.InvariantCultureIgnoreCase)).Key;

        if (!string.IsNullOrEmpty(saveButtonId))
            quoteItemId = Convert.ToInt32(saveButtonId["btnSave".Length..]);

        if (quoteItemId <= 0)
            return await AdminQuote(id);

        int.TryParse(form[$"quantity{quoteItemId}"], out var offeredQty);
        decimal.TryParse(form[$"unitPrice{quoteItemId}"], out var offeredUnitPrice);

        await _rfqService.UpdateQuoteItemAsync(quoteItemId, offeredQty, offeredUnitPrice);

        if (quote.AdminNotes != form["AdminNotes"])
        {
            quote.AdminNotes = form["AdminNotes"];
            await _rfqService.UpdateQuoteAsync(quote);
        }

        _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Plugins.Misc.RFQ.AdminQuote.Updated"));

        return await AdminQuote(id);
    }

    [HttpPost, ActionName("AdminQuote")]
    [FormValueRequired(FormValueRequirement.StartsWith, "btnDelete")]
    [CheckPermission(RfqPermissionConfigManager.ADMIN_ACCESS_RFQ)]
    public async Task DeleteQuoteItem(int id, IFormCollection form)
    {
        if (id <= 0)
            return RedirectToAction("AdminQuotes");

        var quote = await _rfqService.GetQuoteByIdAsync(id);

        if (quote == null)
            return RedirectToAction("AdminQuotes");

        //get the quote item identifier
        var quoteItemId = 0;

        var deleteButtonId = form
            .FirstOrDefault(p => p.Key.StartsWith("btnDelete", StringComparison.InvariantCultureIgnoreCase)).Key;

        if (!string.IsNullOrEmpty(deleteButtonId))
            quoteItemId = Convert.ToInt32(deleteButtonId["btnDelete".Length..]);

        if (quoteItemId <= 0)
            return await AdminQuote(id);

        await _rfqService.DeleteQuoteItemAsync(quoteItemId);

        _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Plugins.Misc.RFQ.AdminQuote.Updated"));

        return await AdminQuote(id);
    }

    #endregion

    #endregion
}