From 1ef5efd57894d6aa4a4ae11267bf87bde291882b Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Fri, 13 Jun 2025 20:57:55 +0200 Subject: [PATCH 1/2] Add checkout attribute management functionality - Updated `PermissionExtensions` to include permission for managing checkout attributes. - Modified `ICheckoutAttributeViewModelService` to accept an optional `storeId` parameter. - Enhanced `CheckoutAttributeViewModelService` to utilize the `storeId` when retrieving attributes. - Added/modified Razor views for creating, editing, and listing checkout attributes and their values. - Updated `CheckoutAttributeController` to support new management features for checkout attributes. - Made changes to `DefaultLanguage.xml` for localization updates. --- .../Extensions/PermissionExtensions.cs | 1 + .../ICheckoutAttributeViewModelService.cs | 2 +- .../CheckoutAttributeViewModelService.cs | 4 +- .../Views/CheckoutAttribute/Create.cshtml | 37 ++ .../Store/Views/CheckoutAttribute/Edit.cshtml | 42 ++ .../Store/Views/CheckoutAttribute/List.cshtml | 98 +++++ .../CreateOrUpdate.TabCondition.cshtml | 126 ++++++ .../Partials/CreateOrUpdate.TabInfo.cshtml | 201 ++++++++++ .../Partials/CreateOrUpdate.TabValues.cshtml | 146 +++++++ .../Partials/CreateOrUpdate.cshtml | 34 ++ .../Partials/CreateOrUpdateValue.cshtml | 95 +++++ .../CheckoutAttribute/ValueCreatePopup.cshtml | 58 +++ .../CheckoutAttribute/ValueEditPopup.cshtml | 54 +++ .../CheckoutAttributeController.cs | 372 ++++++++++++++++++ .../App_Data/Resources/DefaultLanguage.xml | Bin 1535894 -> 1536216 bytes 15 files changed, 1267 insertions(+), 3 deletions(-) create mode 100644 src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Create.cshtml create mode 100644 src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Edit.cshtml create mode 100644 src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/List.cshtml create mode 100644 src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.TabCondition.cshtml create mode 100644 src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.TabInfo.cshtml create mode 100644 src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.TabValues.cshtml create mode 100644 src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.cshtml create mode 100644 src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdateValue.cshtml create mode 100644 src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/ValueCreatePopup.cshtml create mode 100644 src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/ValueEditPopup.cshtml create mode 100644 src/Web/Grand.Web.Store/Controllers/CheckoutAttributeController.cs diff --git a/src/Modules/Grand.Module.Installer/Extensions/PermissionExtensions.cs b/src/Modules/Grand.Module.Installer/Extensions/PermissionExtensions.cs index f998b7cb9..729f88c5b 100644 --- a/src/Modules/Grand.Module.Installer/Extensions/PermissionExtensions.cs +++ b/src/Modules/Grand.Module.Installer/Extensions/PermissionExtensions.cs @@ -211,6 +211,7 @@ public static IEnumerable DefaultPermissions() StandardPermission.ManagePaymentTransactions, StandardPermission.ManageShipments, StandardPermission.ManageMerchandiseReturns, + StandardPermission.ManageCheckoutAttribute, StandardPermission.ManageReports ] }, diff --git a/src/Web/Grand.Web.AdminShared/Interfaces/ICheckoutAttributeViewModelService.cs b/src/Web/Grand.Web.AdminShared/Interfaces/ICheckoutAttributeViewModelService.cs index fa71218a2..f79ea7052 100644 --- a/src/Web/Grand.Web.AdminShared/Interfaces/ICheckoutAttributeViewModelService.cs +++ b/src/Web/Grand.Web.AdminShared/Interfaces/ICheckoutAttributeViewModelService.cs @@ -5,7 +5,7 @@ namespace Grand.Web.AdminShared.Interfaces; public interface ICheckoutAttributeViewModelService { - Task> PrepareCheckoutAttributeListModel(); + Task> PrepareCheckoutAttributeListModel(string storeId = ""); Task> PrepareCheckoutAttributeValuesModel(string checkoutAttributeId); Task PrepareCheckoutAttributeModel(); Task PrepareCheckoutAttributeValueModel(string checkoutAttributeId); diff --git a/src/Web/Grand.Web.AdminShared/Services/CheckoutAttributeViewModelService.cs b/src/Web/Grand.Web.AdminShared/Services/CheckoutAttributeViewModelService.cs index 8871673e2..da4b224bb 100644 --- a/src/Web/Grand.Web.AdminShared/Services/CheckoutAttributeViewModelService.cs +++ b/src/Web/Grand.Web.AdminShared/Services/CheckoutAttributeViewModelService.cs @@ -27,9 +27,9 @@ public class CheckoutAttributeViewModelService( IEnumTranslationService enumTranslationService) : ICheckoutAttributeViewModelService { - public virtual async Task> PrepareCheckoutAttributeListModel() + public virtual async Task> PrepareCheckoutAttributeListModel(string storeId = "") { - var checkoutAttributes = await checkoutAttributeService.GetAllCheckoutAttributes(ignoreAcl: true); + var checkoutAttributes = await checkoutAttributeService.GetAllCheckoutAttributes(storeId: storeId, ignoreAcl: true); return checkoutAttributes.Select((Func)(x => { var attributeModel = x.ToModel(); diff --git a/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Create.cshtml b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Create.cshtml new file mode 100644 index 000000000..780ee3d3e --- /dev/null +++ b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Create.cshtml @@ -0,0 +1,37 @@ +@model CheckoutAttributeModel +@{ + //page title + ViewBag.Title = Loc["Admin.Orders.CheckoutAttributes.AddNew"]; +} +
+ +
+
+
+
+
+ + @Loc["Admin.Orders.CheckoutAttributes.AddNew"] + + @Html.ActionLink(Loc["Admin.Orders.CheckoutAttributes.BackToList"], "List") + +
+
+
+ + + +
+
+
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Edit.cshtml b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Edit.cshtml new file mode 100644 index 000000000..34ab60b99 --- /dev/null +++ b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Edit.cshtml @@ -0,0 +1,42 @@ +@model CheckoutAttributeModel + +@{ + //page title + ViewBag.Title = Loc["Admin.Orders.CheckoutAttributes.EditAttributeDetails"]; +} +
+ +
+
+
+
+
+ + @Loc["Admin.Orders.CheckoutAttributes.EditAttributeDetails"] - @Model.Name + + @Html.ActionLink(Loc["Admin.Orders.CheckoutAttributes.BackToList"], "List") + +
+
+
+ + + + @Loc["Admin.Common.Delete"] + + +
+
+
+
+ +
+
+
+
+
+ \ No newline at end of file diff --git a/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/List.cshtml b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/List.cshtml new file mode 100644 index 000000000..da94020f9 --- /dev/null +++ b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/List.cshtml @@ -0,0 +1,98 @@ +@{ + //page title + ViewBag.Title = Loc["Admin.Orders.CheckoutAttributes"]; +} + +
+
+
+
+
+ + @Loc["Admin.Orders.CheckoutAttributes"] +
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.TabCondition.cshtml b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.TabCondition.cshtml new file mode 100644 index 000000000..3031f4945 --- /dev/null +++ b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.TabCondition.cshtml @@ -0,0 +1,126 @@ +@model CheckoutAttributeModel + +@{ + if (!string.IsNullOrEmpty(Model.Id)) + { + + +
+
+
+ +
+ + +
+
+
+
+
+ @if (Model.ConditionModel.ConditionAttributes.Count > 0) + { +
+
+
+ +
+ + +
+
+
+ +
+ @for (var i = 0; i < Model.ConditionModel.ConditionAttributes.Count; i++) + { + +
+ @switch (Model.ConditionModel.ConditionAttributes[i].AttributeControlType) + { + case AttributeControlType.DropdownList: + + break; + case AttributeControlType.RadioList: + case AttributeControlType.ColorSquares: + case AttributeControlType.ImageSquares: + foreach (var value in Model.ConditionModel.ConditionAttributes[i].Values) + { +
+ + +
+ } + + break; + case AttributeControlType.Checkboxes: + for (var j = 0; j < Model.ConditionModel.ConditionAttributes[i].Values.Count; j++) + { +
+ +
+ } + + break; + case AttributeControlType.ReadonlyCheckboxes: + case AttributeControlType.TextBox: + case AttributeControlType.MultilineTextbox: + case AttributeControlType.Datepicker: + case AttributeControlType.FileUpload: + default: + //not supported as conditions + break; + } +
+ } +
+
+
+
+ } + else + { +
+ @Loc["Admin.Orders.CheckoutAttributes.Condition.NoAttributeExists"] +
+ } +
+ } + else + { +
+ @Loc["Admin.Orders.CheckoutAttributes.Condition.SaveBeforeEdit"] +
+ } +} + \ No newline at end of file diff --git a/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.TabInfo.cshtml b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.TabInfo.cshtml new file mode 100644 index 000000000..fd67f2a7f --- /dev/null +++ b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.TabInfo.cshtml @@ -0,0 +1,201 @@ +@using Microsoft.AspNetCore.Mvc.Razor +@model CheckoutAttributeModel + + + + +@{ + Func + template = @
+
+ +
+ + +
+
+
+ +
+ + +
+
+ +
; +} + +
+ +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ @{ + var attributeControlTypes = + EnumTranslationService.ToSelectList(((AttributeControlType)Model.AttributeControlTypeId), valuesToExclude: [(int)AttributeControlType.ImageSquares, (int)AttributeControlType.Hidden]); + } + + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ \ No newline at end of file diff --git a/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.TabValues.cshtml b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.TabValues.cshtml new file mode 100644 index 000000000..183bbbbd8 --- /dev/null +++ b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.TabValues.cshtml @@ -0,0 +1,146 @@ +@model CheckoutAttributeModel + +@{ + if (!string.IsNullOrEmpty(Model.Id)) + { + + + + + } + else + { +
+ @Loc["Admin.Orders.CheckoutAttributes.Values.SaveBeforeEdit"] +
+ } +} + \ No newline at end of file diff --git a/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.cshtml b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.cshtml new file mode 100644 index 000000000..29de17e25 --- /dev/null +++ b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdate.cshtml @@ -0,0 +1,34 @@ +@model CheckoutAttributeModel + +
+ + + + + + +
+ +
+
+
+ + +
+ +
+
+
+ @if (Model.ConditionAllowed) + { + + +
+ +
+
+
+ } + +
+
\ No newline at end of file diff --git a/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdateValue.cshtml b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdateValue.cshtml new file mode 100644 index 000000000..e05bbca6f --- /dev/null +++ b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/Partials/CreateOrUpdateValue.cshtml @@ -0,0 +1,95 @@ +@using Microsoft.AspNetCore.Mvc.Razor +@model CheckoutAttributeValueModel + +
+ + + + + + +@{ + Func + template = @
+
+ +
+ + +
+
+ +
; +} + +
+ +
+
+ +
+ + +
+
+
+
+
+ @if (Model.DisplayColorSquaresRgb) + { +
+ +
+ +
+ + +
+
+ } +
+ +
+ [@Model.PrimaryStoreCurrencyCode] + +
+
+
+ +
+ [@Model.BaseWeightIn] + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ + +
+
+
+
+ \ No newline at end of file diff --git a/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/ValueCreatePopup.cshtml b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/ValueCreatePopup.cshtml new file mode 100644 index 000000000..816a1f3a9 --- /dev/null +++ b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/ValueCreatePopup.cshtml @@ -0,0 +1,58 @@ +@{ + Layout = ""; +} + +@model CheckoutAttributeValueModel + +
+
+
+
+
+
+ + @Loc["Admin.Orders.CheckoutAttributes.Values.AddNew"] +
+
+ +
+
+
+ +
+
+
+
+ +
\ No newline at end of file diff --git a/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/ValueEditPopup.cshtml b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/ValueEditPopup.cshtml new file mode 100644 index 000000000..d5acf93a8 --- /dev/null +++ b/src/Web/Grand.Web.Store/Areas/Store/Views/CheckoutAttribute/ValueEditPopup.cshtml @@ -0,0 +1,54 @@ +@model CheckoutAttributeValueModel +@{ + Layout = ""; +} +
+ +
+
+
+
+
+ + @Loc["Admin.Orders.CheckoutAttributes.Values.EditValueDetails"] +
+
+
+ +
+
+
+
+ +
\ No newline at end of file diff --git a/src/Web/Grand.Web.Store/Controllers/CheckoutAttributeController.cs b/src/Web/Grand.Web.Store/Controllers/CheckoutAttributeController.cs new file mode 100644 index 000000000..9f5d2174c --- /dev/null +++ b/src/Web/Grand.Web.Store/Controllers/CheckoutAttributeController.cs @@ -0,0 +1,372 @@ +using Grand.Business.Core.Extensions; +using Grand.Business.Core.Interfaces.Catalog.Directory; +using Grand.Business.Core.Interfaces.Checkout.CheckoutAttributes; +using Grand.Business.Core.Interfaces.Common.Directory; +using Grand.Business.Core.Interfaces.Common.Localization; +using Grand.Domain.Catalog; +using Grand.Domain.Directory; +using Grand.Domain.Orders; +using Grand.Domain.Permissions; +using Grand.Infrastructure; +using Grand.Web.AdminShared.Extensions.Mapping; +using Grand.Web.AdminShared.Interfaces; +using Grand.Web.AdminShared.Models.Orders; +using Grand.Web.Common.DataSource; +using Grand.Web.Common.Filters; +using Grand.Web.Common.Security.Authorization; +using Microsoft.AspNetCore.Mvc; +using Grand.Web.AdminShared.Extensions; + +namespace Grand.Web.Store.Controllers; + +[PermissionAuthorize(PermissionSystemName.CheckoutAttributes)] +public class CheckoutAttributeController : BaseStoreController +{ + #region Constructors + + public CheckoutAttributeController(ICheckoutAttributeService checkoutAttributeService, + IContextAccessor contextAccessor, + ILanguageService languageService, + ITranslationService translationService, + ICurrencyService currencyService, + CurrencySettings currencySettings, + IMeasureService measureService, + MeasureSettings measureSettings, + ICheckoutAttributeViewModelService checkoutAttributeViewModelService) + { + _checkoutAttributeService = checkoutAttributeService; + _contextAccessor = contextAccessor; + _languageService = languageService; + _translationService = translationService; + _currencyService = currencyService; + _currencySettings = currencySettings; + _measureService = measureService; + _measureSettings = measureSettings; + _checkoutAttributeViewModelService = checkoutAttributeViewModelService; + } + + #endregion + + #region Fields + + private readonly ICheckoutAttributeService _checkoutAttributeService; + private readonly IContextAccessor _contextAccessor; + private readonly ILanguageService _languageService; + private readonly ITranslationService _translationService; + private readonly ICurrencyService _currencyService; + private readonly CurrencySettings _currencySettings; + private readonly IMeasureService _measureService; + private readonly MeasureSettings _measureSettings; + private readonly ICheckoutAttributeViewModelService _checkoutAttributeViewModelService; + + #endregion + + #region Helper Methods + + /// + /// Checks access permissions for a checkout attribute and handles warnings + /// + /// Checkout attribute to check + /// True if access is allowed, false if access should be denied + private bool CheckAccessPermission(CheckoutAttribute checkoutAttribute) + { + if (checkoutAttribute == null) + return false; + + if (!checkoutAttribute.LimitedToStores || (checkoutAttribute.LimitedToStores && + checkoutAttribute.Stores.Contains(_contextAccessor.WorkContext.CurrentCustomer.StaffStoreId) && + checkoutAttribute.Stores.Count > 1)) + { + Warning(_translationService.GetResource("admin.Catalog.attributes.checkoutAttributes.permissions")); + return true; + } + + return checkoutAttribute.AccessToEntityByStore(_contextAccessor.WorkContext.CurrentCustomer.StaffStoreId); + } + + #endregion + + #region Checkout attributes + + //list + public IActionResult Index() + { + return RedirectToAction("List"); + } + + public IActionResult List() + { + return View(); + } + + [HttpPost] + [PermissionAuthorizeAction(PermissionActionName.List)] + public async Task List(DataSourceRequest command) + { + var checkoutAttributes = await _checkoutAttributeViewModelService.PrepareCheckoutAttributeListModel(_contextAccessor.WorkContext.CurrentCustomer.StoreId); + var gridModel = new DataSourceResult { + Data = checkoutAttributes.ToList(), + Total = checkoutAttributes.Count() + }; + return Json(gridModel); + } + + //create + [PermissionAuthorizeAction(PermissionActionName.Create)] + public async Task Create() + { + var model = await _checkoutAttributeViewModelService.PrepareCheckoutAttributeModel(); + //locales + await AddLocales(_languageService, model.Locales); + + return View(model); + } + + [HttpPost] + [ArgumentNameFilter(KeyName = "save-continue", Argument = "continueEditing")] + [PermissionAuthorizeAction(PermissionActionName.Create)] + public async Task Create(CheckoutAttributeModel model, bool continueEditing) + { + if (ModelState.IsValid) + { + model.Stores = [_contextAccessor.WorkContext.CurrentCustomer.StaffStoreId]; + model.CustomerGroups = []; + var checkoutAttribute = await _checkoutAttributeViewModelService.InsertCheckoutAttributeModel(model); + Success(_translationService.GetResource("Admin.Orders.CheckoutAttributes.Added")); + return continueEditing + ? RedirectToAction("Edit", new { id = checkoutAttribute.Id }) + : RedirectToAction("List"); + } + + //If we got this far, something failed, redisplay form + //tax categories + await _checkoutAttributeViewModelService.PrepareTaxCategories(model, null, true); + + return View(model); + } + + //edit + [PermissionAuthorizeAction(PermissionActionName.Preview)] + public async Task Edit(string id) + { + var checkoutAttribute = await _checkoutAttributeService.GetCheckoutAttributeById(id); + if (checkoutAttribute == null) + //No checkout attribute found with the specified id + return RedirectToAction("List"); + + if (!CheckAccessPermission(checkoutAttribute)) + return RedirectToAction("List"); + + var model = checkoutAttribute.ToModel(); + //locales + await AddLocales(_languageService, model.Locales, (locale, languageId) => + { + locale.Name = checkoutAttribute.GetTranslation(x => x.Name, languageId, false); + locale.TextPrompt = checkoutAttribute.GetTranslation(x => x.TextPrompt, languageId, false); + }); + + //tax categories + await _checkoutAttributeViewModelService.PrepareTaxCategories(model, checkoutAttribute, false); + + //condition + await _checkoutAttributeViewModelService.PrepareConditionAttributes(model, checkoutAttribute); + + return View(model); + } + + [HttpPost] + [ArgumentNameFilter(KeyName = "save-continue", Argument = "continueEditing")] + [PermissionAuthorizeAction(PermissionActionName.Edit)] + public async Task Edit(CheckoutAttributeModel model, bool continueEditing) + { + var checkoutAttribute = await _checkoutAttributeService.GetCheckoutAttributeById(model.Id); + if (checkoutAttribute == null) + //No checkout attribute found with the specified id + return RedirectToAction("List"); + + if (!checkoutAttribute.AccessToEntityByStore(_contextAccessor.WorkContext.CurrentCustomer.StaffStoreId)) + return RedirectToAction("Edit", new { checkoutAttribute.Id }); + + if (ModelState.IsValid) + { + model.Stores = [_contextAccessor.WorkContext.CurrentCustomer.StaffStoreId]; + model.CustomerGroups = []; + + checkoutAttribute = await _checkoutAttributeViewModelService.UpdateCheckoutAttributeModel(checkoutAttribute, model); + Success(_translationService.GetResource("Admin.Orders.CheckoutAttributes.Updated")); + if (continueEditing) + { + //selected tab + await SaveSelectedTabIndex(); + + return RedirectToAction("Edit", new { id = checkoutAttribute.Id }); + } + + return RedirectToAction("List"); + } + + //If we got this far, something failed, redisplay form + //tax categories + await _checkoutAttributeViewModelService.PrepareTaxCategories(model, checkoutAttribute, true); + return View(model); + } + + //delete + [HttpPost] + [PermissionAuthorizeAction(PermissionActionName.Delete)] + public async Task Delete(string id) + { + var checkoutAttribute = await _checkoutAttributeService.GetCheckoutAttributeById(id); + + if (!checkoutAttribute.AccessToEntityByStore(_contextAccessor.WorkContext.CurrentCustomer.StaffStoreId)) + return RedirectToAction("List"); + + await _checkoutAttributeService.DeleteCheckoutAttribute(checkoutAttribute); + + Success(_translationService.GetResource("Admin.Orders.CheckoutAttributes.Deleted")); + return RedirectToAction("List"); + } + + #endregion + + #region Checkout attribute values + + //list + [HttpPost] + [PermissionAuthorizeAction(PermissionActionName.Preview)] + public async Task ValueList(string checkoutAttributeId, DataSourceRequest command) + { + var ca = await _checkoutAttributeService.GetCheckoutAttributeById(checkoutAttributeId); + if (!CheckAccessPermission(ca)) + return View("AccessDenied", _translationService.GetResource("admin.Catalog.attributes.checkoutAttributes.permissions")); + + var checkoutAttribute = await _checkoutAttributeViewModelService.PrepareCheckoutAttributeValuesModel(checkoutAttributeId); + + var gridModel = new DataSourceResult { + Data = checkoutAttribute.ToList(), + Total = checkoutAttribute.Count() + }; + return Json(gridModel); + } + + //create + [PermissionAuthorizeAction(PermissionActionName.Edit)] + public async Task ValueCreatePopup(string checkoutAttributeId) + { + var ca = await _checkoutAttributeService.GetCheckoutAttributeById(checkoutAttributeId); + if (ca == null) + throw new ArgumentException("No checkout attribute found with the specified id"); + + if (!ca.AccessToEntityByStore(_contextAccessor.WorkContext.CurrentCustomer.StaffStoreId)) + return View("AccessDenied", _translationService.GetResource("admin.Catalog.attributes.checkoutattributes.permissions")); + + var model = await _checkoutAttributeViewModelService.PrepareCheckoutAttributeValueModel(checkoutAttributeId); + //locales + await AddLocales(_languageService, model.Locales); + return View(model); + } + + [HttpPost] + [PermissionAuthorizeAction(PermissionActionName.Edit)] + public async Task ValueCreatePopup(CheckoutAttributeValueModel model) + { + var checkoutAttribute = await _checkoutAttributeService.GetCheckoutAttributeById(model.CheckoutAttributeId); + if (checkoutAttribute == null) + //No checkout attribute found with the specified id + return RedirectToAction("List"); + + if (!checkoutAttribute.AccessToEntityByStore(_contextAccessor.WorkContext.CurrentCustomer.StaffStoreId)) + return View("AccessDenied", _translationService.GetResource("admin.Catalog.attributes.checkoutattributes.permissions")); + + if (ModelState.IsValid) + { + await _checkoutAttributeViewModelService.InsertCheckoutAttributeValueModel(checkoutAttribute, model); + return Content(""); + } + + //If we got this far, something failed, redisplay form + model.PrimaryStoreCurrencyCode = + (await _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId)).CurrencyCode; + model.BaseWeightIn = (await _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId)).Name; + return View(model); + } + + //edit + [PermissionAuthorizeAction(PermissionActionName.Edit)] + public async Task ValueEditPopup(string id, string checkoutAttributeId) + { + var checkoutAttribute = await _checkoutAttributeService.GetCheckoutAttributeById(checkoutAttributeId); + + if (!CheckAccessPermission(checkoutAttribute)) + return View("AccessDenied", _translationService.GetResource("admin.Catalog.attributes.checkoutAttributes.permissions")); + + var cav = checkoutAttribute.CheckoutAttributeValues.FirstOrDefault(x => x.Id == id); + if (cav == null) + //No checkout attribute value found with the specified id + return RedirectToAction("List"); + + var model = await _checkoutAttributeViewModelService.PrepareCheckoutAttributeValueModel(checkoutAttribute, cav); + + //locales + await AddLocales(_languageService, model.Locales, (locale, languageId) => + { + locale.Name = cav.GetTranslation(x => x.Name, languageId, false); + }); + + return View(model); + } + + [HttpPost] + [PermissionAuthorizeAction(PermissionActionName.Edit)] + public async Task ValueEditPopup(CheckoutAttributeValueModel model) + { + var checkoutAttribute = await _checkoutAttributeService.GetCheckoutAttributeById(model.CheckoutAttributeId); + + if (!checkoutAttribute.AccessToEntityByStore(_contextAccessor.WorkContext.CurrentCustomer.StaffStoreId)) + return View("AccessDenied", _translationService.GetResource("admin.Catalog.attributes.checkoutAttributes.permissions")); + + var cav = checkoutAttribute.CheckoutAttributeValues.FirstOrDefault(x => x.Id == model.Id); + if (cav == null) + //No checkout attribute value found with the specified id + return RedirectToAction("List"); + + if (ModelState.IsValid) + { + await _checkoutAttributeViewModelService.UpdateCheckoutAttributeValueModel(checkoutAttribute, cav, model); + return Content(""); + } + + //If we got this far, something failed, redisplay form + model.PrimaryStoreCurrencyCode = + (await _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId)).CurrencyCode; + model.BaseWeightIn = (await _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId)).Name; + + return View(model); + } + + //delete + [HttpPost] + [PermissionAuthorizeAction(PermissionActionName.Edit)] + public async Task ValueDelete(string id, string checkoutAttributeId) + { + var checkoutAttribute = await _checkoutAttributeService.GetCheckoutAttributeById(checkoutAttributeId); + + if (!checkoutAttribute.AccessToEntityByStore(_contextAccessor.WorkContext.CurrentCustomer.StaffStoreId)) + return View("AccessDenied", _translationService.GetResource("admin.Catalog.attributes.checkoutAttributes.permissions")); + + var cav = checkoutAttribute.CheckoutAttributeValues.FirstOrDefault(x => x.Id == id); + if (cav == null) + throw new ArgumentException("No checkout attribute value found with the specified id"); + + if (ModelState.IsValid) + { + checkoutAttribute.CheckoutAttributeValues.Remove(cav); + await _checkoutAttributeService.UpdateCheckoutAttribute(checkoutAttribute); + return new JsonResult(""); + } + + return ErrorForKendoGridJson(ModelState); + } + + #endregion +} \ No newline at end of file diff --git a/src/Web/Grand.Web/App_Data/Resources/DefaultLanguage.xml b/src/Web/Grand.Web/App_Data/Resources/DefaultLanguage.xml index da1c65d7c26e99c00e4a8d6433ee04d9b2d03206..8b59be237692351cb60b7182b85bb79774b9a587 100644 GIT binary patch delta 136 zcmWN=y$ym;6oAoOM8v1)byv<*U_9c|4DJTtgoGs6G? delta 116 zcmccdFm~Gg*oG~PuU1XpaF)?yy1^VSlj#kA_ypQ_tYHLVCLm@8Viq7~1!6WJW(Q&p rAm#*OE+FOxVjdvo1!6uR<_BVsenB7>0%Bnx76D?>?K{?pDI5U+DM>jb From 066f56090c999e29eeb33a839dd09b5281ab80e1 Mon Sep 17 00:00:00 2001 From: Krzysztof Pajak Date: Mon, 16 Jun 2025 20:37:03 +0200 Subject: [PATCH 2/2] Update src/Web/Grand.Web.Store/Controllers/CheckoutAttributeController.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Grand.Web.Store/Controllers/CheckoutAttributeController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Web/Grand.Web.Store/Controllers/CheckoutAttributeController.cs b/src/Web/Grand.Web.Store/Controllers/CheckoutAttributeController.cs index 9f5d2174c..d1510740d 100644 --- a/src/Web/Grand.Web.Store/Controllers/CheckoutAttributeController.cs +++ b/src/Web/Grand.Web.Store/Controllers/CheckoutAttributeController.cs @@ -185,7 +185,7 @@ public async Task Edit(CheckoutAttributeModel model, bool continu return RedirectToAction("List"); if (!checkoutAttribute.AccessToEntityByStore(_contextAccessor.WorkContext.CurrentCustomer.StaffStoreId)) - return RedirectToAction("Edit", new { checkoutAttribute.Id }); + return RedirectToAction("Edit", new { id = checkoutAttribute.Id }); if (ModelState.IsValid) {