diff --git a/src/Web/Grand.SharedUIResources/wwwroot/administration/build/css/custom-rtl.css b/src/Web/Grand.SharedUIResources/wwwroot/administration/build/css/custom-rtl.css
index 1d943f991..28d0ff2e6 100644
--- a/src/Web/Grand.SharedUIResources/wwwroot/administration/build/css/custom-rtl.css
+++ b/src/Web/Grand.SharedUIResources/wwwroot/administration/build/css/custom-rtl.css
@@ -6986,7 +6986,6 @@ h2 {
color: #f9f9f9;
}
[data-theme="dark"] .k-grid table a, [data-theme="dark"] .premium-list li span {
- color: #A5D8FE;
font-weight: 500;
}
[data-theme="dark"] .toggle a span {
diff --git a/src/Web/Grand.SharedUIResources/wwwroot/administration/build/css/custom.css b/src/Web/Grand.SharedUIResources/wwwroot/administration/build/css/custom.css
index 2d86c5d94..ab00b02d3 100644
--- a/src/Web/Grand.SharedUIResources/wwwroot/administration/build/css/custom.css
+++ b/src/Web/Grand.SharedUIResources/wwwroot/administration/build/css/custom.css
@@ -7094,7 +7094,6 @@ table.tableFixHead {
color: #f9f9f9;
}
[data-theme="dark"] .k-grid table a, [data-theme="dark"] .premium-list li span {
- color: #A5D8FE;
font-weight: 500;
}
[data-theme="dark"] .toggle a span {
diff --git a/src/Web/Grand.Web.Store/Areas/Store/Views/Currency/List.cshtml b/src/Web/Grand.Web.Store/Areas/Store/Views/Currency/List.cshtml
new file mode 100644
index 000000000..5a658fb1f
--- /dev/null
+++ b/src/Web/Grand.Web.Store/Areas/Store/Views/Currency/List.cshtml
@@ -0,0 +1,216 @@
+@inject AdminAreaSettings adminAreaSettings
+@{
+ ViewBag.Title = Loc["Admin.Configuration.Currencies"];
+ Layout = Constants.LayoutStore;
+}
+
+
+
+
diff --git a/src/Web/Grand.Web.Store/Controllers/CurrencyController.cs b/src/Web/Grand.Web.Store/Controllers/CurrencyController.cs
new file mode 100644
index 000000000..3004601f1
--- /dev/null
+++ b/src/Web/Grand.Web.Store/Controllers/CurrencyController.cs
@@ -0,0 +1,164 @@
+using Grand.Business.Core.Interfaces.Common.Directory;
+using Grand.Business.Core.Interfaces.Common.Localization;
+using Grand.Business.Core.Interfaces.Common.Stores;
+using Grand.Domain.Directory;
+using Grand.Domain.Permissions;
+using Grand.Infrastructure;
+using Grand.Web.Common.DataSource;
+using Grand.Web.Common.Security.Authorization;
+using Grand.Web.Store.Models;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Grand.Web.Store.Controllers;
+
+[PermissionAuthorize(PermissionSystemName.Currencies)]
+public class CurrencyController(
+ ICurrencyService currencyService,
+ CurrencySettings currencySettings,
+ ITranslationService translationService,
+ IStoreService storeService,
+ IContextAccessor contextAccessor) : BaseStoreController
+{
+ private string CurrentStoreId => contextAccessor.WorkContext.CurrentCustomer.StaffStoreId;
+
+ public IActionResult Index()
+ {
+ return RedirectToAction("List");
+ }
+
+ [PermissionAuthorizeAction(PermissionActionName.List)]
+ public IActionResult List()
+ {
+ return View();
+ }
+
+ [HttpPost]
+ [PermissionAuthorizeAction(PermissionActionName.List)]
+ public async Task ListData()
+ {
+ var storeId = CurrentStoreId;
+ var primaryStoreCurrencyId = currencySettings.PrimaryStoreCurrencyId;
+
+ var store = await storeService.GetStoreById(storeId);
+ var defaultCurrencyId = store?.DefaultCurrencyId;
+
+ var currencies = await currencyService.GetAllCurrencies(showHidden: false);
+
+ var items = currencies
+ .Select(c => new StoreCurrencyModel {
+ Id = c.Id,
+ Name = c.Name,
+ CurrencyCode = c.CurrencyCode,
+ Published = c.Published,
+ DisplayOrder = c.DisplayOrder,
+ LimitedToStores = c.LimitedToStores,
+ IsAssignedToCurrentStore = !c.LimitedToStores || c.Stores.Contains(storeId),
+ IsPrimaryStoreCurrency = c.Id == primaryStoreCurrencyId,
+ IsDefaultStoreCurrency = c.Id == defaultCurrencyId,
+ CanManage = c.LimitedToStores
+ })
+ .ToList();
+
+ var gridModel = new DataSourceResult {
+ Data = items,
+ Total = items.Count
+ };
+
+ return Json(gridModel);
+ }
+
+ [HttpPost]
+ [PermissionAuthorizeAction(PermissionActionName.Edit)]
+ public async Task AssignStore(string id)
+ {
+ var currency = await currencyService.GetCurrencyById(id);
+ if (currency == null)
+ return Json(new { success = false, message = translationService.GetResource("Admin.Configuration.Currencies.NotFound") });
+
+ if (!currency.LimitedToStores)
+ return Json(new { success = false, message = translationService.GetResource("Admin.Configuration.Currencies.CannotModifyGlobal") });
+
+ var storeId = CurrentStoreId;
+ if (!currency.Stores.Contains(storeId))
+ {
+ currency.Stores.Add(storeId);
+ await currencyService.UpdateCurrency(currency);
+ }
+
+ return Json(new { success = true });
+ }
+
+ [HttpPost]
+ [PermissionAuthorizeAction(PermissionActionName.Edit)]
+ public async Task UnassignStore(string id)
+ {
+ var currency = await currencyService.GetCurrencyById(id);
+ if (currency == null)
+ return Json(new { success = false, message = translationService.GetResource("Admin.Configuration.Currencies.NotFound") });
+
+ if (!currency.LimitedToStores)
+ return Json(new { success = false, message = translationService.GetResource("Admin.Configuration.Currencies.CannotModifyGlobal") });
+
+ if (currency.Id == currencySettings.PrimaryStoreCurrencyId)
+ return Json(new { success = false, message = translationService.GetResource("Admin.Configuration.Currencies.CantDeletePrimary") });
+
+ var storeId = CurrentStoreId;
+
+ var store = await storeService.GetStoreById(storeId);
+ if (store?.DefaultCurrencyId == currency.Id)
+ return Json(new { success = false, message = translationService.GetResource("Admin.Configuration.Currencies.CantUnassignDefault") });
+
+ if (currency.Stores.Remove(storeId))
+ await currencyService.UpdateCurrency(currency);
+
+ return Json(new { success = true });
+ }
+
+ [HttpPost]
+ [PermissionAuthorizeAction(PermissionActionName.Edit)]
+ public async Task SetDefaultCurrency(string id)
+ {
+ var currency = await currencyService.GetCurrencyById(id);
+ if (currency == null)
+ return Json(new { success = false, message = translationService.GetResource("Admin.Configuration.Currencies.NotFound") });
+
+ if (!currency.Published)
+ return Json(new { success = false, message = translationService.GetResource("Admin.Configuration.Currencies.NotPublished") });
+
+ var storeId = CurrentStoreId;
+
+ if (currency.LimitedToStores && !currency.Stores.Contains(storeId))
+ return Json(new { success = false, message = translationService.GetResource("Admin.Configuration.Currencies.NotAssignedToStore") });
+
+ var store = await storeService.GetStoreById(storeId);
+ if (store == null)
+ return Json(new { success = false, message = translationService.GetResource("Admin.Configuration.Stores.NotFound") });
+
+ store.DefaultCurrencyId = currency.Id;
+ await storeService.UpdateStore(store);
+
+ return Json(new { success = true });
+ }
+
+ [HttpPost]
+ [PermissionAuthorizeAction(PermissionActionName.Edit)]
+ public async Task UnsetDefaultCurrency(string id)
+ {
+ var currency = await currencyService.GetCurrencyById(id);
+ if (currency == null)
+ return Json(new { success = false, message = translationService.GetResource("Admin.Configuration.Currencies.NotFound") });
+
+ var storeId = CurrentStoreId;
+ var store = await storeService.GetStoreById(storeId);
+ if (store == null)
+ return Json(new { success = false, message = translationService.GetResource("Admin.Configuration.Stores.NotFound") });
+
+ if (store.DefaultCurrencyId != currency.Id)
+ return Json(new { success = false, message = translationService.GetResource("Admin.Configuration.Currencies.NotDefaultCurrency") });
+
+ store.DefaultCurrencyId = string.Empty;
+ await storeService.UpdateStore(store);
+
+ return Json(new { success = true });
+ }
+}
diff --git a/src/Web/Grand.Web.Store/Models/StoreCurrencyModel.cs b/src/Web/Grand.Web.Store/Models/StoreCurrencyModel.cs
new file mode 100644
index 000000000..50e569202
--- /dev/null
+++ b/src/Web/Grand.Web.Store/Models/StoreCurrencyModel.cs
@@ -0,0 +1,15 @@
+namespace Grand.Web.Store.Models;
+
+public class StoreCurrencyModel
+{
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public string CurrencyCode { get; set; }
+ public bool Published { get; set; }
+ public int DisplayOrder { get; set; }
+ public bool LimitedToStores { get; set; }
+ public bool IsAssignedToCurrentStore { get; set; }
+ public bool IsPrimaryStoreCurrency { get; set; }
+ public bool IsDefaultStoreCurrency { get; set; }
+ public bool CanManage { get; set; }
+}
diff --git a/src/Web/Grand.Web/App_Data/Resources/DefaultLanguage.xml b/src/Web/Grand.Web/App_Data/Resources/DefaultLanguage.xml
index 527aba66d..94f18f262 100644
Binary files a/src/Web/Grand.Web/App_Data/Resources/DefaultLanguage.xml and b/src/Web/Grand.Web/App_Data/Resources/DefaultLanguage.xml differ