diff --git a/Directory.Packages.props b/Directory.Packages.props
index 448f8b957d..bb1c0d7b65 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -5,75 +5,80 @@
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Common/Grand.Business.Common.csproj b/src/Business/Grand.Business.Common/Grand.Business.Common.csproj
index 39e4981080..e9684ff464 100644
--- a/src/Business/Grand.Business.Common/Grand.Business.Common.csproj
+++ b/src/Business/Grand.Business.Common/Grand.Business.Common.csproj
@@ -1,7 +1,8 @@
-
+
+
diff --git a/src/Business/Grand.Business.Common/Utilities/PowerExcelExtensions.cs b/src/Business/Grand.Business.Common/Utilities/PowerExcelExtensions.cs
index efd1757ba8..33ace35212 100644
--- a/src/Business/Grand.Business.Common/Utilities/PowerExcelExtensions.cs
+++ b/src/Business/Grand.Business.Common/Utilities/PowerExcelExtensions.cs
@@ -7,14 +7,6 @@ namespace Grand.Business.Common.Utilities;
internal static class PowerExcelExtensions
{
- [Obsolete("This method is obsolete. Use the new method instead.")]
- internal static IEnumerable Rows(this ISheet sheet)
- {
- var e = sheet.GetRowEnumerator();
- while (e.MoveNext())
- yield return e.Current as IRow;
- }
-
internal static bool IsIdenticalTo(this MemberInfo memberInfo, MemberInfo other)
{
if (memberInfo == null || other == null) return false;
diff --git a/src/Business/Grand.Business.Common/Utilities/PowerExcelMapper.cs b/src/Business/Grand.Business.Common/Utilities/PowerExcelMapper.cs
index 33406fd154..9f0cfe8fcf 100644
--- a/src/Business/Grand.Business.Common/Utilities/PowerExcelMapper.cs
+++ b/src/Business/Grand.Business.Common/Utilities/PowerExcelMapper.cs
@@ -47,7 +47,7 @@ private IEnumerable Fetch(ISheet sheet, Type type, Func
var firstRowNumber = HeaderRowNumber;
if (!HeaderRow)
- firstRowNumber = sheet.Rows().Where(r => r.RowNum >= MinRowNumber && r.RowNum <= MaxRowNumber)
+ firstRowNumber = sheet.Where(r => r.RowNum >= MinRowNumber && r.RowNum <= MaxRowNumber)
.OrderByDescending(r => r.LastCellNum).FirstOrDefault()?.RowNum ?? 0;
var firstRow = sheet.GetRow(firstRowNumber);
diff --git a/src/Tests/Grand.Infrastructure.Tests/Caching/Redis/RedisMessageBusTests.cs b/src/Tests/Grand.Infrastructure.Tests/Caching/Redis/RedisMessageBusTests.cs
index 656c4fcbbd..22187b0890 100644
--- a/src/Tests/Grand.Infrastructure.Tests/Caching/Redis/RedisMessageBusTests.cs
+++ b/src/Tests/Grand.Infrastructure.Tests/Caching/Redis/RedisMessageBusTests.cs
@@ -110,7 +110,8 @@ public async Task PublishAsync_SubscriberThrows_DoesNotPropagateException()
{
_subscriberMock.Setup(s =>
s.PublishAsync(It.IsAny(), It.IsAny(), It.IsAny()))
- .ThrowsAsync(new RedisConnectionException(ConnectionFailureType.SocketFailure, "connection lost"));
+ .ThrowsAsync(new RedisConnectionException(ConnectionFailureType.SocketFailure, CommandFlags.None,
+ "connection lost", null, CommandStatus.Unknown));
await _bus.PublishAsync(new MessageEvent { Key = "key", MessageType = (int)MessageEventType.RemoveKey });
}
diff --git a/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseAddressAttributeControllerTests.cs b/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseAddressAttributeControllerTests.cs
index ea684ba0aa..be386517da 100644
--- a/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseAddressAttributeControllerTests.cs
+++ b/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseAddressAttributeControllerTests.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using Grand.Business.Core.Interfaces.Common.Addresses;
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Domain.Common;
diff --git a/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseCheckoutAttributeControllerTests.cs b/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseCheckoutAttributeControllerTests.cs
index 2cbcb740e2..7fa92346fa 100644
--- a/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseCheckoutAttributeControllerTests.cs
+++ b/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseCheckoutAttributeControllerTests.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using Grand.Business.Core.Interfaces.Catalog.Directory;
using Grand.Business.Core.Interfaces.Checkout.CheckoutAttributes;
using Grand.Business.Core.Interfaces.Common.Directory;
diff --git a/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseDiscountControllerTests.cs b/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseDiscountControllerTests.cs
index a8709fa610..44f39f0ea7 100644
--- a/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseDiscountControllerTests.cs
+++ b/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseDiscountControllerTests.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using Grand.Business.Core.Interfaces.Catalog.Brands;
using Grand.Business.Core.Interfaces.Catalog.Categories;
using Grand.Business.Core.Interfaces.Catalog.Collections;
diff --git a/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseEmailAccountControllerTests.cs b/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseEmailAccountControllerTests.cs
index 32c4edcfd7..b007b83a04 100644
--- a/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseEmailAccountControllerTests.cs
+++ b/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseEmailAccountControllerTests.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Business.Core.Interfaces.Messages;
using Grand.Domain.Messages;
diff --git a/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseProductAttributeControllerTests.cs b/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseProductAttributeControllerTests.cs
index 6779f9df21..7333738f85 100644
--- a/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseProductAttributeControllerTests.cs
+++ b/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseProductAttributeControllerTests.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using Grand.Business.Core.Interfaces.Catalog.Products;
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Domain;
diff --git a/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseTaxCategoryControllerTests.cs b/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseTaxCategoryControllerTests.cs
index f52e80a3d3..28c1fc6483 100644
--- a/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseTaxCategoryControllerTests.cs
+++ b/src/Tests/Grand.Web.Admin.Tests/Controllers/BaseTaxCategoryControllerTests.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using Grand.Business.Core.Interfaces.Catalog.Tax;
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Business.Core.Interfaces.Common.Stores;
diff --git a/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedBlogCategoryDataScopeTests.cs b/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedBlogCategoryDataScopeTests.cs
index ef73e7cbb2..52c8dabcc4 100644
--- a/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedBlogCategoryDataScopeTests.cs
+++ b/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedBlogCategoryDataScopeTests.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using Grand.Domain.Blogs;
using Grand.Web.AdminShared.Interfaces;
using Grand.Web.AdminShared.Services;
diff --git a/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedBlogPostDataScopeTests.cs b/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedBlogPostDataScopeTests.cs
index 066b512ad3..e87b48444b 100644
--- a/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedBlogPostDataScopeTests.cs
+++ b/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedBlogPostDataScopeTests.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using Grand.Domain.Blogs;
using Grand.Web.AdminShared.Interfaces;
using Grand.Web.AdminShared.Services;
diff --git a/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedEmailAccountDataScopeTests.cs b/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedEmailAccountDataScopeTests.cs
index 678892bf77..369ae92695 100644
--- a/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedEmailAccountDataScopeTests.cs
+++ b/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedEmailAccountDataScopeTests.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using Grand.Domain.Customers;
using Grand.Domain.Messages;
using Grand.Infrastructure;
diff --git a/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedTaxCategoryDataScopeTests.cs b/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedTaxCategoryDataScopeTests.cs
index 3046425383..0626705d68 100644
--- a/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedTaxCategoryDataScopeTests.cs
+++ b/src/Tests/Grand.Web.Admin.Tests/Controllers/RoutedTaxCategoryDataScopeTests.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using Grand.Domain.Customers;
using Grand.Domain.Tax;
using Grand.Infrastructure;
diff --git a/src/Web/Grand.Web.Admin/Controllers/DiscountController.cs b/src/Web/Grand.Web.Admin/Controllers/DiscountController.cs
index 67163fb9ab..569ef6eda0 100644
--- a/src/Web/Grand.Web.Admin/Controllers/DiscountController.cs
+++ b/src/Web/Grand.Web.Admin/Controllers/DiscountController.cs
@@ -56,10 +56,10 @@ public class DiscountController(
public async Task VendorList(DataSourceRequest command, string discountId,
[FromServices] IVendorService vendorService)
{
- var discount = await discountService.GetDiscountById(discountId);
+ var discount = await DiscountService.GetDiscountById(discountId);
if (discount == null)
throw new Exception("No discount found with the specified id");
- if (!await scope.CanView(discount))
+ if (!await Scope.CanView(discount))
return new JsonResult(new DataSourceResult { Errors = "Access denied" });
var vendors = await vendorService.GetAllVendorsByDiscount(discount.Id);
@@ -73,10 +73,10 @@ public async Task VendorList(DataSourceRequest command, string di
public async Task VendorDelete(string discountId, string vendorId,
[FromServices] IVendorService vendorService)
{
- var discount = await discountService.GetDiscountById(discountId);
+ var discount = await DiscountService.GetDiscountById(discountId);
if (discount == null)
throw new Exception("No discount found with the specified id");
- if (!await scope.HasAccess(discount))
+ if (!await Scope.HasAccess(discount))
return new JsonResult(new DataSourceResult { Errors = "Access denied" });
var vendor = await vendorService.GetVendorById(vendorId);
@@ -84,7 +84,7 @@ public async Task VendorDelete(string discountId, string vendorId
throw new Exception("No vendor found with the specified id");
if (ModelState.IsValid)
{
- await discountViewModelService.DeleteVendor(discount, vendor);
+ await DiscountViewModelService.DeleteVendor(discount, vendor);
return new JsonResult("");
}
return ErrorForKendoGridJson(ModelState);
@@ -93,10 +93,10 @@ public async Task VendorDelete(string discountId, string vendorId
[PermissionAuthorizeAction(PermissionActionName.Edit)]
public async Task VendorAddPopup(string discountId)
{
- var discount = await discountService.GetDiscountById(discountId);
+ var discount = await DiscountService.GetDiscountById(discountId);
if (discount == null)
throw new Exception("No discount found with the specified id");
- if (!await scope.HasAccess(discount))
+ if (!await Scope.HasAccess(discount))
return new JsonResult(new DataSourceResult { Errors = "Access denied" });
return View(new DiscountModel.AddVendorToDiscountModel());
@@ -122,13 +122,13 @@ public async Task VendorAddPopupList(DataSourceRequest command,
[HttpPost]
public async Task VendorAddPopup(DiscountModel.AddVendorToDiscountModel model)
{
- var discount = await discountService.GetDiscountById(model.DiscountId);
+ var discount = await DiscountService.GetDiscountById(model.DiscountId);
if (discount == null)
throw new Exception("No discount found with the specified id");
- if (!await scope.HasAccess(discount))
+ if (!await Scope.HasAccess(discount))
return Content("Access denied");
- if (model.SelectedVendorIds != null) await discountViewModelService.InsertVendorToDiscountModel(model);
+ if (model.SelectedVendorIds != null) await DiscountViewModelService.InsertVendorToDiscountModel(model);
return Content("");
}
diff --git a/src/Web/Grand.Web.Admin/Controllers/EmailAccountController.cs b/src/Web/Grand.Web.Admin/Controllers/EmailAccountController.cs
index 2906ac8687..0f137ff6af 100644
--- a/src/Web/Grand.Web.Admin/Controllers/EmailAccountController.cs
+++ b/src/Web/Grand.Web.Admin/Controllers/EmailAccountController.cs
@@ -42,7 +42,7 @@ public IActionResult List()
[PermissionAuthorizeAction(PermissionActionName.List)]
public async Task List(DataSourceRequest command)
{
- var emailAccounts = await emailAccountService.GetAllEmailAccounts(scope.DefaultStoreId ?? "",
+ var emailAccounts = await EmailAccountService.GetAllEmailAccounts(Scope.DefaultStoreId ?? "",
pageIndex: command.Page - 1, pageSize: command.PageSize);
var emailAccountModels = emailAccounts.Select(x => x.ToModel()).ToList();
foreach (var eam in emailAccountModels)
@@ -59,7 +59,7 @@ public async Task List(DataSourceRequest command)
[PermissionAuthorizeAction(PermissionActionName.Edit)]
public async Task MarkAsDefaultEmail(string id)
{
- var defaultEmailAccount = await emailAccountService.GetEmailAccountById(id);
+ var defaultEmailAccount = await EmailAccountService.GetEmailAccountById(id);
if (defaultEmailAccount != null)
{
emailAccountSettings.DefaultEmailAccountId = defaultEmailAccount.Id;
diff --git a/src/Web/Grand.Web.Admin/Controllers/MessageTemplateController.cs b/src/Web/Grand.Web.Admin/Controllers/MessageTemplateController.cs
index a1ebd1c8eb..0ec8513955 100644
--- a/src/Web/Grand.Web.Admin/Controllers/MessageTemplateController.cs
+++ b/src/Web/Grand.Web.Admin/Controllers/MessageTemplateController.cs
@@ -43,7 +43,7 @@ public async Task List()
{
var model = new MessageTemplateListModel();
model.AvailableStores.Add(new SelectListItem
- { Text = translationService.GetResource("Admin.Common.All"), Value = "" });
+ { Text = TranslationService.GetResource("Admin.Common.All"), Value = "" });
foreach (var s in await storeService.GetAllStores())
model.AvailableStores.Add(new SelectListItem { Text = s.Shortcut, Value = s.Id });
@@ -54,7 +54,7 @@ public async Task List()
[HttpPost]
public async Task List(DataSourceRequest command, MessageTemplateListModel model)
{
- var messageTemplates = await messageTemplateService.GetAllMessageTemplates(
+ var messageTemplates = await MessageTemplateService.GetAllMessageTemplates(
model.SearchStoreId,
keywords: model.Name,
pageIndex: command.Page - 1,
diff --git a/src/Web/Grand.Web.Admin/Controllers/OnlineCustomerController.cs b/src/Web/Grand.Web.Admin/Controllers/OnlineCustomerController.cs
index eee3393560..8ece19efe5 100644
--- a/src/Web/Grand.Web.Admin/Controllers/OnlineCustomerController.cs
+++ b/src/Web/Grand.Web.Admin/Controllers/OnlineCustomerController.cs
@@ -28,5 +28,5 @@ public class OnlineCustomerController(
IContextAccessor contextAccessor)
: BaseOnlineCustomerController(customerService, dateTimeService, customerSettings, translationService, contextAccessor)
{
- protected override string SalesEmployeeIdFilter => contextAccessor.WorkContext.CurrentCustomer.SeId;
+ protected override string SalesEmployeeIdFilter => ContextAccessor.WorkContext.CurrentCustomer.SeId;
}
diff --git a/src/Web/Grand.Web.Admin/Controllers/ReportsController.cs b/src/Web/Grand.Web.Admin/Controllers/ReportsController.cs
index 79ff93d9b3..c7a058b45b 100644
--- a/src/Web/Grand.Web.Admin/Controllers/ReportsController.cs
+++ b/src/Web/Grand.Web.Admin/Controllers/ReportsController.cs
@@ -64,7 +64,7 @@ public class ReportsController(
/// this check explicitly.
public override async Task CountryReport()
{
- if (!await permissionService.Authorize(StandardPermission.ManageCustomers))
+ if (!await PermissionService.Authorize(StandardPermission.ManageCustomers))
return AccessDeniedView();
return await base.CountryReport();
}
@@ -74,7 +74,7 @@ public override async Task CountryReport()
/// this one) but not on Store's or Vendor's.
public override async Task Customer()
{
- if (!await permissionService.Authorize(StandardPermission.ManageCustomers))
+ if (!await PermissionService.Authorize(StandardPermission.ManageCustomers))
return AccessDeniedView();
return await base.Customer();
}
@@ -84,7 +84,7 @@ public override async Task Customer()
[HttpPost]
public async Task PopularSearchTermsReport(DataSourceRequest command)
{
- if (!await permissionService.Authorize(StandardPermission.ManageProducts))
+ if (!await PermissionService.Authorize(StandardPermission.ManageProducts))
return AccessDeniedView();
var searchTermRecordLines = await searchTermService.GetStats(command.Page - 1, command.PageSize);
diff --git a/src/Web/Grand.Web.Admin/Controllers/TaxController.cs b/src/Web/Grand.Web.Admin/Controllers/TaxController.cs
index dae826550d..d3cb6cb4e5 100644
--- a/src/Web/Grand.Web.Admin/Controllers/TaxController.cs
+++ b/src/Web/Grand.Web.Admin/Controllers/TaxController.cs
@@ -129,16 +129,16 @@ public async Task Settings()
model.TaxDisplayTypeValues = enumTranslationService.ToSelectList(taxSettings.TaxDisplayType);
//tax categories
- var taxCategories = await taxCategoryService.GetAllTaxCategories();
+ var taxCategories = await TaxCategoryService.GetAllTaxCategories();
model.TaxCategories.Add(new SelectListItem {
- Text = translationService.GetResource("Admin.Configuration.Tax.Settings.TaxCategories.None"), Value = ""
+ Text = TranslationService.GetResource("Admin.Configuration.Tax.Settings.TaxCategories.None"), Value = ""
});
foreach (var tc in taxCategories)
model.TaxCategories.Add(new SelectListItem { Text = tc.Name, Value = tc.Id });
//EU VAT countries
model.EuVatShopCountries.Add(new SelectListItem
- { Text = translationService.GetResource("Admin.Address.SelectCountry"), Value = "" });
+ { Text = TranslationService.GetResource("Admin.Address.SelectCountry"), Value = "" });
foreach (var c in await countryService.GetAllCountries(showHidden: true))
model.EuVatShopCountries.Add(new SelectListItem
{ Text = c.Name, Value = c.Id, Selected = c.Id == taxSettings.EuVatShopCountryId });
@@ -151,7 +151,7 @@ public async Task Settings()
model.DefaultTaxAddress = new AddressModel();
model.DefaultTaxAddress.AvailableCountries.Add(new SelectListItem
- { Text = translationService.GetResource("Admin.Address.SelectCountry"), Value = "" });
+ { Text = TranslationService.GetResource("Admin.Address.SelectCountry"), Value = "" });
foreach (var c in await countryService.GetAllCountries(showHidden: true))
model.DefaultTaxAddress.AvailableCountries.Add(new SelectListItem
{ Text = c.Name, Value = c.Id, Selected = defaultAddress != null && c.Id == defaultAddress.CountryId });
@@ -185,7 +185,7 @@ public async Task Settings(TaxSettingsModel model)
//now clear cache
await ClearCache();
- Success(translationService.GetResource("Admin.Configuration.Updated"));
+ Success(TranslationService.GetResource("Admin.Configuration.Updated"));
return RedirectToAction("Settings");
}
@@ -197,8 +197,8 @@ public async Task Categories()
{
var model = new TaxCategoryListModel();
model.AvailableStores.Add(new SelectListItem
- { Text = translationService.GetResource("Admin.Common.All"), Value = "" });
- foreach (var s in await storeService.GetAllStores())
+ { Text = TranslationService.GetResource("Admin.Common.All"), Value = "" });
+ foreach (var s in await StoreService.GetAllStores())
model.AvailableStores.Add(new SelectListItem { Text = s.Shortcut, Value = s.Id });
return View(model);
}
diff --git a/src/Web/Grand.Web.AdminShared/Controllers/BaseBlogController.cs b/src/Web/Grand.Web.AdminShared/Controllers/BaseBlogController.cs
index 0737726e96..d705531288 100644
--- a/src/Web/Grand.Web.AdminShared/Controllers/BaseBlogController.cs
+++ b/src/Web/Grand.Web.AdminShared/Controllers/BaseBlogController.cs
@@ -50,6 +50,8 @@ protected virtual void EditWarningCheck(BlogPost blogPost) { }
// Exposed for host subclasses: primary-constructor parameters are not visible to derived classes
// by name in C#.
+ protected IBlogService BlogService => blogService;
+ protected IDateTimeService DateTimeService => dateTimeService;
protected ITranslationService TranslationService => translationService;
protected IAdminDataScope PostScope => postScope;
protected IAdminDataScope CategoryScope => categoryScope;
diff --git a/src/Web/Grand.Web.AdminShared/Controllers/BaseDiscountController.cs b/src/Web/Grand.Web.AdminShared/Controllers/BaseDiscountController.cs
index f0c7ce20f0..4b7a3663ca 100644
--- a/src/Web/Grand.Web.AdminShared/Controllers/BaseDiscountController.cs
+++ b/src/Web/Grand.Web.AdminShared/Controllers/BaseDiscountController.cs
@@ -33,6 +33,18 @@ public abstract class BaseDiscountController(
IAdminDataScope scope)
: BaseController
{
+ /// Exposed so host controllers that extend this base (Admin's DiscountController) can
+ /// reuse the same instance for their own vendor-region actions instead of re-capturing their
+ /// own copy of the primary-constructor parameter (which would trigger CS9107 - the parameter
+ /// stored twice, once here and once in the derived class).
+ protected IDiscountViewModelService DiscountViewModelService => discountViewModelService;
+
+ /// See .
+ protected IDiscountService DiscountService => discountService;
+
+ /// See .
+ protected IAdminDataScope Scope => scope;
+
#region Discounts
public IActionResult Index() => RedirectToAction("List");
diff --git a/src/Web/Grand.Web.AdminShared/Controllers/BaseEmailAccountController.cs b/src/Web/Grand.Web.AdminShared/Controllers/BaseEmailAccountController.cs
index c3ffec27cd..e04e73d5fd 100644
--- a/src/Web/Grand.Web.AdminShared/Controllers/BaseEmailAccountController.cs
+++ b/src/Web/Grand.Web.AdminShared/Controllers/BaseEmailAccountController.cs
@@ -22,6 +22,15 @@ public abstract class BaseEmailAccountController(
IAdminDataScope scope)
: BaseController
{
+ /// Exposed so host controllers that extend this base (Admin/Store's
+ /// EmailAccountController) can reuse the same instance for their own List action instead of
+ /// re-capturing their own copy of the primary-constructor parameter (which would trigger
+ /// CS9107 - the parameter stored twice, once here and once in the derived class).
+ protected IEmailAccountService EmailAccountService => emailAccountService;
+
+ /// See .
+ protected IAdminDataScope Scope => scope;
+
#region Create
[PermissionAuthorizeAction(PermissionActionName.Create)]
diff --git a/src/Web/Grand.Web.AdminShared/Controllers/BaseFullReportsController.cs b/src/Web/Grand.Web.AdminShared/Controllers/BaseFullReportsController.cs
index 975fff3102..1b10620a38 100644
--- a/src/Web/Grand.Web.AdminShared/Controllers/BaseFullReportsController.cs
+++ b/src/Web/Grand.Web.AdminShared/Controllers/BaseFullReportsController.cs
@@ -101,9 +101,9 @@ protected virtual async Task> GetReportOrderPe
(30, "Admin.Reports.Period.month"), (365, "Admin.Reports.Period.year")
})
{
- var reportPeriod = await orderReportService.GetOrderPeriodReport(days, scope.StoreId);
+ var reportPeriod = await OrderReportService.GetOrderPeriodReport(days, Scope.StoreId);
report.Add(new OrderPeriodReportLineModel {
- Period = translationService.GetResource(resourceKey),
+ Period = TranslationService.GetResource(resourceKey),
Count = reportPeriod.Count,
Amount = reportPeriod.Amount
});
@@ -130,7 +130,7 @@ public virtual async Task ReportOrderTimeChart(DataSourceRequest
if (!await permissionService.Authorize(StandardPermission.ManageOrders))
return Content("");
- var model = await orderReportService.GetOrderByTimeReport(scope.StoreId, startDate, endDate);
+ var model = await OrderReportService.GetOrderByTimeReport(Scope.StoreId, startDate, endDate);
return Json(new DataSourceResult { Data = model });
}
@@ -141,22 +141,22 @@ public virtual async Task OrderAverageReportList(DataSourceReques
return Content("");
var report = new List {
- await orderReportService.OrderAverageReport(scope.StoreId, (int)OrderStatusSystem.Pending),
- await orderReportService.OrderAverageReport(scope.StoreId, (int)OrderStatusSystem.Processing),
- await orderReportService.OrderAverageReport(scope.StoreId, (int)OrderStatusSystem.Complete),
- await orderReportService.OrderAverageReport(scope.StoreId, (int)OrderStatusSystem.Cancelled)
+ await OrderReportService.OrderAverageReport(Scope.StoreId, (int)OrderStatusSystem.Pending),
+ await OrderReportService.OrderAverageReport(Scope.StoreId, (int)OrderStatusSystem.Processing),
+ await OrderReportService.OrderAverageReport(Scope.StoreId, (int)OrderStatusSystem.Complete),
+ await OrderReportService.OrderAverageReport(Scope.StoreId, (int)OrderStatusSystem.Cancelled)
};
- var statuses = await orderStatusService.GetAll();
+ var statuses = await OrderStatusService.GetAll();
var model = new List();
foreach (var x in report)
model.Add(new OrderAverageReportLineSummaryModel {
OrderStatus = statuses.FirstOrDefault(y => y.StatusId == x.OrderStatus)?.Name,
- SumTodayOrders = priceFormatter.FormatPrice(x.SumTodayOrders, await currencyService.GetPrimaryStoreCurrency()),
- SumThisWeekOrders = priceFormatter.FormatPrice(x.SumThisWeekOrders, await currencyService.GetPrimaryStoreCurrency()),
- SumThisMonthOrders = priceFormatter.FormatPrice(x.SumThisMonthOrders, await currencyService.GetPrimaryStoreCurrency()),
- SumThisYearOrders = priceFormatter.FormatPrice(x.SumThisYearOrders, await currencyService.GetPrimaryStoreCurrency()),
- SumAllTimeOrders = priceFormatter.FormatPrice(x.SumAllTimeOrders, await currencyService.GetPrimaryStoreCurrency())
+ SumTodayOrders = PriceFormatter.FormatPrice(x.SumTodayOrders, await CurrencyService.GetPrimaryStoreCurrency()),
+ SumThisWeekOrders = PriceFormatter.FormatPrice(x.SumThisWeekOrders, await CurrencyService.GetPrimaryStoreCurrency()),
+ SumThisMonthOrders = PriceFormatter.FormatPrice(x.SumThisMonthOrders, await CurrencyService.GetPrimaryStoreCurrency()),
+ SumThisYearOrders = PriceFormatter.FormatPrice(x.SumThisYearOrders, await CurrencyService.GetPrimaryStoreCurrency()),
+ SumAllTimeOrders = PriceFormatter.FormatPrice(x.SumAllTimeOrders, await CurrencyService.GetPrimaryStoreCurrency())
});
var gridModel = new DataSourceResult { Data = model, Total = model.Count };
@@ -171,28 +171,28 @@ public virtual async Task ReportLatestOrder(DataSourceRequest com
return Content("");
var orders = await orderService.SearchOrders(
- storeId: scope.StoreId,
+ storeId: Scope.StoreId,
createdFromUtc: startDate,
createdToUtc: endDate,
pageIndex: command.Page - 1,
pageSize: command.PageSize);
- var statuses = await orderStatusService.GetAll();
+ var statuses = await OrderStatusService.GetAll();
var items = new List();
foreach (var x in orders)
{
- var store = await storeService.GetStoreById(x.StoreId);
+ var store = await StoreService.GetStoreById(x.StoreId);
items.Add(new OrderModel {
Id = x.Id,
OrderNumber = x.OrderNumber,
StoreName = store != null ? store.Shortcut : "Unknown",
- OrderTotal = priceFormatter.FormatPrice(x.OrderTotal, await currencyService.GetPrimaryStoreCurrency()),
+ OrderTotal = PriceFormatter.FormatPrice(x.OrderTotal, await CurrencyService.GetPrimaryStoreCurrency()),
OrderStatus = statuses.FirstOrDefault(y => y.StatusId == x.OrderStatusId)?.Name,
- PaymentStatus = enumTranslationService.GetTranslationEnum(x.PaymentStatusId),
- ShippingStatus = enumTranslationService.GetTranslationEnum(x.ShippingStatusId),
+ PaymentStatus = EnumTranslationService.GetTranslationEnum(x.PaymentStatusId),
+ ShippingStatus = EnumTranslationService.GetTranslationEnum(x.ShippingStatusId),
CustomerEmail = x.BillingAddress.Email,
CustomerFullName = $"{x.BillingAddress.FirstName} {x.BillingAddress.LastName}",
- CreatedOn = dateTimeService.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc)
+ CreatedOn = DateTimeService.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc)
});
}
@@ -202,7 +202,7 @@ public virtual async Task ReportLatestOrder(DataSourceRequest com
/// Area constant for the "View" link on each row: reproduces each host's original
/// literal (Admin used Constants.AreaAdmin, Store used Constants.AreaStore) via
- /// scope.ResourceKeyPrefix, which happens to already equal "Admin" for both Admin and Store (Store
+ /// Scope.ResourceKeyPrefix, which happens to already equal "Admin" for both Admin and Store (Store
/// reuses Admin's resource keys — Task 2's StoreReportDataScope) — but the *area* value must be
/// the actual routing area, not the resource-key prefix, so this uses
/// ViewContext.RouteData.Values["area"] directly instead, matching the same
@@ -218,30 +218,30 @@ public virtual async Task OrderIncompleteReportList(DataSourceReq
var area = ControllerContext.RouteData.Values["area"]?.ToString();
var model = new List();
- var psPending = await orderReportService.GetOrderAverageReportLine(scope.StoreId, ps: PaymentStatus.Pending,
+ var psPending = await OrderReportService.GetOrderAverageReportLine(Scope.StoreId, ps: PaymentStatus.Pending,
ignoreCancelledOrders: true);
model.Add(new OrderIncompleteReportLineModel {
- Item = translationService.GetResource("Admin.Reports.Incomplete.TotalUnpaidOrders"),
+ Item = TranslationService.GetResource("Admin.Reports.Incomplete.TotalUnpaidOrders"),
Count = psPending.CountOrders,
- Total = priceFormatter.FormatPrice(psPending.SumOrders, await currencyService.GetPrimaryStoreCurrency()),
+ Total = PriceFormatter.FormatPrice(psPending.SumOrders, await CurrencyService.GetPrimaryStoreCurrency()),
ViewLink = Url.Action("List", "Order", new { paymentStatusId = ((int)PaymentStatus.Pending).ToString(), area })
});
- var ssPending = await orderReportService.GetOrderAverageReportLine(scope.StoreId, ss: ShippingStatus.Pending,
+ var ssPending = await OrderReportService.GetOrderAverageReportLine(Scope.StoreId, ss: ShippingStatus.Pending,
ignoreCancelledOrders: true);
model.Add(new OrderIncompleteReportLineModel {
- Item = translationService.GetResource("Admin.Reports.Incomplete.TotalNotShippedOrders"),
+ Item = TranslationService.GetResource("Admin.Reports.Incomplete.TotalNotShippedOrders"),
Count = ssPending.CountOrders,
- Total = priceFormatter.FormatPrice(ssPending.SumOrders, await currencyService.GetPrimaryStoreCurrency()),
+ Total = PriceFormatter.FormatPrice(ssPending.SumOrders, await CurrencyService.GetPrimaryStoreCurrency()),
ViewLink = Url.Action("List", "Order", new { shippingStatusId = ((int)ShippingStatus.Pending).ToString(), area })
});
- var osPending = await orderReportService.GetOrderAverageReportLine(scope.StoreId, os: (int)OrderStatusSystem.Pending,
+ var osPending = await OrderReportService.GetOrderAverageReportLine(Scope.StoreId, os: (int)OrderStatusSystem.Pending,
ignoreCancelledOrders: true);
model.Add(new OrderIncompleteReportLineModel {
- Item = translationService.GetResource("Admin.Reports.Incomplete.TotalIncompleteOrders"),
+ Item = TranslationService.GetResource("Admin.Reports.Incomplete.TotalIncompleteOrders"),
Count = osPending.CountOrders,
- Total = priceFormatter.FormatPrice(osPending.SumOrders, await currencyService.GetPrimaryStoreCurrency()),
+ Total = PriceFormatter.FormatPrice(osPending.SumOrders, await CurrencyService.GetPrimaryStoreCurrency()),
ViewLink = Url.Action("List", "Order", new { orderStatusId = ((int)OrderStatusSystem.Pending).ToString(), area })
});
@@ -257,10 +257,10 @@ public virtual async Task OrderIncompleteReportList(DataSourceReq
public virtual async Task ReportBestCustomersByNumberOfOrdersList(DataSourceRequest command,
BestCustomersReportModel model)
{
- if (!string.IsNullOrEmpty(scope.StoreId)) model.StoreId = scope.StoreId;
+ if (!string.IsNullOrEmpty(Scope.StoreId)) model.StoreId = Scope.StoreId;
- var (bestCustomerReportLineModels, totalCount) = await customerReportViewModelService
- .PrepareBestCustomerReportLineModel(model, 2, command.Page, command.PageSize, scope.VendorId);
+ var (bestCustomerReportLineModels, totalCount) = await CustomerReportViewModelService
+ .PrepareBestCustomerReportLineModel(model, 2, command.Page, command.PageSize, Scope.VendorId);
var gridModel = new DataSourceResult { Data = bestCustomerReportLineModels.ToList(), Total = totalCount };
return Json(gridModel);
@@ -269,7 +269,7 @@ public virtual async Task ReportBestCustomersByNumberOfOrdersList
[HttpPost]
public virtual async Task ReportRegisteredCustomersList(DataSourceRequest command)
{
- var model = await customerReportViewModelService.GetReportRegisteredCustomersModel(scope.StoreId, scope.VendorId);
+ var model = await CustomerReportViewModelService.GetReportRegisteredCustomersModel(Scope.StoreId, Scope.VendorId);
var gridModel = new DataSourceResult { Data = model, Total = model.Count };
return Json(gridModel);
}
@@ -278,7 +278,7 @@ public virtual async Task ReportRegisteredCustomersList(DataSourc
public virtual async Task ReportCustomerTimeChart(DataSourceRequest command, DateTime? startDate,
DateTime? endDate)
{
- var model = await customerReportService.GetCustomerByTimeReport(scope.StoreId, startDate, endDate);
+ var model = await customerReportService.GetCustomerByTimeReport(Scope.StoreId, startDate, endDate);
return Json(new DataSourceResult { Data = model });
}
diff --git a/src/Web/Grand.Web.AdminShared/Controllers/BaseMessageTemplateController.cs b/src/Web/Grand.Web.AdminShared/Controllers/BaseMessageTemplateController.cs
index 08d81071ad..7029562e48 100644
--- a/src/Web/Grand.Web.AdminShared/Controllers/BaseMessageTemplateController.cs
+++ b/src/Web/Grand.Web.AdminShared/Controllers/BaseMessageTemplateController.cs
@@ -27,6 +27,15 @@ public abstract class BaseMessageTemplateController(
EmailAccountSettings emailAccountSettings)
: BaseController
{
+ /// Exposed so host controllers that extend this base (Admin/Store's
+ /// MessageTemplateController) can reuse the same instance for their own List actions instead
+ /// of re-capturing their own copy of the primary-constructor parameter (which would trigger
+ /// CS9107 - the parameter stored twice, once here and once in the derived class).
+ protected IMessageTemplateService MessageTemplateService => messageTemplateService;
+
+ /// See .
+ protected ITranslationService TranslationService => translationService;
+
public IActionResult Index() => RedirectToAction("List");
#region Create
diff --git a/src/Web/Grand.Web.AdminShared/Controllers/BaseNewsController.cs b/src/Web/Grand.Web.AdminShared/Controllers/BaseNewsController.cs
index edf63ebff9..7857d4a6d7 100644
--- a/src/Web/Grand.Web.AdminShared/Controllers/BaseNewsController.cs
+++ b/src/Web/Grand.Web.AdminShared/Controllers/BaseNewsController.cs
@@ -36,6 +36,8 @@ protected virtual void EditWarningCheck(NewsItem newsItem) { }
// Exposed for host subclasses: primary-constructor parameters are not visible to derived classes
// by name in C#.
+ protected INewsService NewsService => newsService;
+ protected IDateTimeService DateTimeService => dateTimeService;
protected ITranslationService TranslationService => translationService;
protected IAdminDataScope Scope => scope;
diff --git a/src/Web/Grand.Web.AdminShared/Controllers/BaseOnlineCustomerController.cs b/src/Web/Grand.Web.AdminShared/Controllers/BaseOnlineCustomerController.cs
index 0ac9b553c8..0057b7d1f5 100644
--- a/src/Web/Grand.Web.AdminShared/Controllers/BaseOnlineCustomerController.cs
+++ b/src/Web/Grand.Web.AdminShared/Controllers/BaseOnlineCustomerController.cs
@@ -29,6 +29,13 @@ public abstract class BaseOnlineCustomerController(
IContextAccessor contextAccessor)
: BaseController
{
+ /// Exposed so host controllers that extend this base (Admin's
+ /// OnlineCustomerController) can reuse the same instance for their own SalesEmployeeIdFilter
+ /// override instead of re-capturing their own copy of the primary-constructor parameter (which
+ /// would trigger CS9107 - the parameter stored twice, once here and once in the derived
+ /// class).
+ protected IContextAccessor ContextAccessor => contextAccessor;
+
///
/// Sales-Manager restriction on the online-customers list. Admin restricts to the current
/// Sales-Manager's own customers; Store has no such concept and never restricts by it.
diff --git a/src/Web/Grand.Web.AdminShared/Controllers/BaseOrderManagementController.cs b/src/Web/Grand.Web.AdminShared/Controllers/BaseOrderManagementController.cs
index 82315436c1..cd6fc4dc89 100644
--- a/src/Web/Grand.Web.AdminShared/Controllers/BaseOrderManagementController.cs
+++ b/src/Web/Grand.Web.AdminShared/Controllers/BaseOrderManagementController.cs
@@ -68,9 +68,9 @@ public async Task SaveOrderTags(OrderModel orderModel)
try
{
- await orderViewModelService.SaveOrderTags(order, orderModel.OrderTags);
+ await OrderViewModelService.SaveOrderTags(order, orderModel.OrderTags);
var model = new OrderModel();
- await orderViewModelService.PrepareOrderDetailsModel(model, order);
+ await OrderViewModelService.PrepareOrderDetailsModel(model, order);
return RedirectToAction("Edit", "Order", new { id = order.Id });
}
catch (Exception exception)
@@ -93,15 +93,15 @@ public async Task ChangeOrderStatus(string id, OrderModel model)
ArgumentNullException.ThrowIfNull(status);
order.OrderStatusId = model.OrderStatusId;
- await orderService.UpdateOrder(order);
+ await OrderService.UpdateOrder(order);
- await orderService.InsertOrderNote(new OrderNote {
+ await OrderService.InsertOrderNote(new OrderNote {
Note = $"Order status has been edited. New status: {status.Name}",
DisplayToCustomer = false,
OrderId = order.Id
});
model = new OrderModel();
- await orderViewModelService.PrepareOrderDetailsModel(model, order);
+ await OrderViewModelService.PrepareOrderDetailsModel(model, order);
return RedirectToAction("Edit", "Order", new { id });
}
catch (Exception exc)
@@ -134,15 +134,15 @@ public async Task EditOrderTotals(string id, OrderModel model)
order.OrderDiscount = model.OrderTotalDiscountValue;
order.OrderTotal = model.OrderTotalValue;
order.CurrencyRate = model.CurrencyRate;
- await orderService.UpdateOrder(order);
+ await OrderService.UpdateOrder(order);
- await orderService.InsertOrderNote(new OrderNote {
+ await OrderService.InsertOrderNote(new OrderNote {
Note = "Order totals have been edited",
DisplayToCustomer = false,
OrderId = order.Id
});
- await orderViewModelService.PrepareOrderDetailsModel(model, order);
+ await OrderViewModelService.PrepareOrderDetailsModel(model, order);
return RedirectToAction("Edit", "Order", new { id });
}
@@ -154,14 +154,14 @@ public async Task EditShippingMethod(string id, OrderModel model)
if (denied != null) return denied;
order.ShippingMethod = model.ShippingMethod;
- await orderService.UpdateOrder(order);
+ await OrderService.UpdateOrder(order);
- await orderService.InsertOrderNote(new OrderNote {
+ await OrderService.InsertOrderNote(new OrderNote {
Note = "Shipping method has been edited",
DisplayToCustomer = false,
OrderId = order.Id
});
- await orderViewModelService.PrepareOrderDetailsModel(model, order);
+ await OrderViewModelService.PrepareOrderDetailsModel(model, order);
await SaveSelectedTabIndex(persistForTheNextRequest: true);
return RedirectToAction("Edit", "Order", new { id });
@@ -174,8 +174,8 @@ public async Task EditUserFields(string id, OrderModel model)
if (denied != null) return denied;
order.UserFields = model.UserFields;
- await orderService.UpdateOrder(order);
- await orderViewModelService.PrepareOrderDetailsModel(model, order);
+ await OrderService.UpdateOrder(order);
+ await OrderViewModelService.PrepareOrderDetailsModel(model, order);
await SaveSelectedTabIndex(persistForTheNextRequest: true);
return RedirectToAction("Edit", "Order", new { id });
@@ -305,9 +305,9 @@ public async Task ResetDownloadCount(string id, string orderItemI
var orderItem = order.OrderItems.FirstOrDefault(x => x.Id == orderItemId)
?? throw new ArgumentException("No order item found with the specified id");
orderItem.DownloadCount = 0;
- await orderService.UpdateOrder(order);
+ await OrderService.UpdateOrder(order);
var model = new OrderModel();
- await orderViewModelService.PrepareOrderDetailsModel(model, order);
+ await OrderViewModelService.PrepareOrderDetailsModel(model, order);
await SaveSelectedTabIndex(persistForTheNextRequest: true);
return RedirectToAction("Edit", "Order", new { id });
@@ -323,9 +323,9 @@ public async Task ActivateDownloadItem(string id, string orderIte
var orderItem = order.OrderItems.FirstOrDefault(x => x.Id == orderItemId)
?? throw new ArgumentException("No order item found with the specified id");
orderItem.IsDownloadActivated = !orderItem.IsDownloadActivated;
- await orderService.UpdateOrder(order);
+ await OrderService.UpdateOrder(order);
var model = new OrderModel();
- await orderViewModelService.PrepareOrderDetailsModel(model, order);
+ await OrderViewModelService.PrepareOrderDetailsModel(model, order);
await SaveSelectedTabIndex(persistForTheNextRequest: true);
return RedirectToAction("Edit", "Order", new { id });
@@ -365,7 +365,7 @@ public async Task UploadLicenseFilePopup(OrderModel.UploadLicense
var orderItem = order.OrderItems.FirstOrDefault(x => x.Id == model.OrderItemId)
?? throw new ArgumentException("No order item found with the specified id");
orderItem.LicenseDownloadId = !string.IsNullOrEmpty(model.LicenseDownloadId) ? model.LicenseDownloadId : null;
- await orderService.UpdateOrder(order);
+ await OrderService.UpdateOrder(order);
model.RefreshPage = true;
return View(model);
@@ -381,7 +381,7 @@ public async Task DeleteLicenseFilePopup(OrderModel.UploadLicense
var orderItem = order.OrderItems.FirstOrDefault(x => x.Id == model.OrderItemId)
?? throw new ArgumentException("No order item found with the specified id");
orderItem.LicenseDownloadId = null;
- await orderService.UpdateOrder(order);
+ await OrderService.UpdateOrder(order);
return RedirectToAction("Edit", "Order", new { id = model.OrderId });
}
@@ -396,7 +396,7 @@ public async Task AddProductToOrder(string orderId)
var (order, denied) = await LoadAuthorizedOrder(orderId);
if (denied != null) return denied;
- var model = await orderViewModelService.PrepareAddOrderProductModel(order);
+ var model = await OrderViewModelService.PrepareAddOrderProductModel(order);
return View(model);
}
@@ -411,7 +411,7 @@ public async Task AddProductToOrder(
var gridModel = new Grand.Web.Common.DataSource.DataSourceResult();
var products = (await productService.SearchProducts(categoryIds: categoryIds,
- storeId: scope.DefaultStoreId,
+ storeId: Scope.DefaultStoreId,
brandId: model.SearchBrandId,
collectionId: model.SearchCollectionId,
productType: model.SearchProductTypeId > 0 ? (Grand.Domain.Catalog.ProductType?)model.SearchProductTypeId : null,
@@ -434,7 +434,7 @@ public async Task AddProductToOrderDetails(string orderId, string
var (order, denied) = await LoadAuthorizedOrder(orderId);
if (denied != null) return denied;
- var model = await orderViewModelService.PrepareAddProductToOrderModel(order, productId);
+ var model = await OrderViewModelService.PrepareAddProductToOrderModel(order, productId);
return View(model);
}
@@ -445,10 +445,10 @@ public async Task AddProductToOrderDetails(AddProductToOrderModel
var (order, denied) = await LoadAuthorizedOrder(model.OrderId);
if (denied != null) return denied;
- var warnings = await orderViewModelService.AddProductToOrderDetails(model);
+ var warnings = await OrderViewModelService.AddProductToOrderDetails(model);
if (!warnings.Any()) return RedirectToAction("Edit", "Order", new { id = model.OrderId });
- var result = await orderViewModelService.PrepareAddProductToOrderModel(order, model.ProductId);
+ var result = await OrderViewModelService.PrepareAddProductToOrderModel(order, model.ProductId);
result.Warnings.AddRange(warnings);
return View(result);
}
@@ -477,7 +477,7 @@ public async Task AddressEdit(string addressId, string orderId, b
if (address == null)
throw new ArgumentException("No address found with the specified id", nameof(addressId));
- var model = await orderViewModelService.PrepareOrderAddressModel(order, address);
+ var model = await OrderViewModelService.PrepareOrderAddressModel(order, address);
model.BillingAddress = billingAddress;
return View(model);
}
@@ -505,12 +505,12 @@ public async Task AddressEdit(OrderAddressModel model,
if (ModelState.IsValid)
{
var customAttributes = await model.Address.ParseCustomAddressAttributes(addressAttributeParser, addressAttributeService);
- await orderViewModelService.UpdateOrderAddress(order, address, model, customAttributes);
+ await OrderViewModelService.UpdateOrderAddress(order, address, model, customAttributes);
return RedirectToAction("AddressEdit",
new { addressId = model.Address.Id, orderId = model.OrderId, model.BillingAddress });
}
- model = await orderViewModelService.PrepareOrderAddressModel(order, address);
+ model = await OrderViewModelService.PrepareOrderAddressModel(order, address);
return View(model);
}
@@ -522,15 +522,15 @@ public async Task AddressEdit(OrderAddressModel model,
[HttpPost]
public async Task OrderNotesSelect(string orderId, Grand.Web.Common.DataSource.DataSourceRequest command)
{
- var order = await orderService.GetOrderById(orderId)
+ var order = await OrderService.GetOrderById(orderId)
?? throw new ArgumentException("No order found with the specified id");
// Preserved host divergence: Admin's original throws for both not-found and Sales-Manager
// denial; Store's original throws only for not-found and soft-denies (empty content) for
// store-mismatch. Unifying these into one behavior would be a real change for one host -
// deliberately not done here. See plan's Global Constraints.
- if (!await scope.HasAccess(order)) return Content("");
+ if (!await Scope.HasAccess(order)) return Content("");
- var orderNoteModels = await orderViewModelService.PrepareOrderNotes(order);
+ var orderNoteModels = await OrderViewModelService.PrepareOrderNotes(order);
var gridModel = new Grand.Web.Common.DataSource.DataSourceResult {
Data = orderNoteModels,
Total = orderNoteModels.Count
@@ -541,10 +541,10 @@ public async Task OrderNotesSelect(string orderId, Grand.Web.Comm
[PermissionAuthorizeAction(PermissionActionName.Edit)]
public async Task OrderNoteAdd(string orderId, string downloadId, bool displayToCustomer, string message)
{
- var order = await orderService.GetOrderById(orderId);
- if (order == null || !await scope.HasAccess(order)) return Json(new { Result = false });
+ var order = await OrderService.GetOrderById(orderId);
+ if (order == null || !await Scope.HasAccess(order)) return Json(new { Result = false });
- await orderViewModelService.InsertOrderNote(order, downloadId, displayToCustomer, message);
+ await OrderViewModelService.InsertOrderNote(order, downloadId, displayToCustomer, message);
return Json(new { Result = true });
}
@@ -552,11 +552,11 @@ public async Task OrderNoteAdd(string orderId, string downloadId,
[HttpPost]
public async Task OrderNoteDelete(string id, string orderId)
{
- var order = await orderService.GetOrderById(orderId)
+ var order = await OrderService.GetOrderById(orderId)
?? throw new ArgumentException("No order found with the specified id");
- if (!await scope.HasAccess(order)) return Json(new { Result = false });
+ if (!await Scope.HasAccess(order)) return Json(new { Result = false });
- await orderViewModelService.DeleteOrderNote(order, id);
+ await OrderViewModelService.DeleteOrderNote(order, id);
return new JsonResult("");
}
diff --git a/src/Web/Grand.Web.AdminShared/Controllers/BasePageController.cs b/src/Web/Grand.Web.AdminShared/Controllers/BasePageController.cs
index 77477cda70..4addf52f32 100644
--- a/src/Web/Grand.Web.AdminShared/Controllers/BasePageController.cs
+++ b/src/Web/Grand.Web.AdminShared/Controllers/BasePageController.cs
@@ -33,6 +33,10 @@ protected virtual void EditWarningCheck(Page page) { }
// Exposed for host subclasses: primary-constructor parameters are not visible to derived classes
// by name in C#.
+ protected IPageViewModelService PageViewModelService => pageViewModelService;
+ protected IPageService PageService => pageService;
+ protected ILanguageService LanguageService => languageService;
+ protected IDateTimeService DateTimeService => dateTimeService;
protected ITranslationService TranslationService => translationService;
protected IAdminDataScope Scope => scope;
diff --git a/src/Web/Grand.Web.AdminShared/Controllers/BaseTaxCategoryController.cs b/src/Web/Grand.Web.AdminShared/Controllers/BaseTaxCategoryController.cs
index b58740ffcd..85f3735988 100644
--- a/src/Web/Grand.Web.AdminShared/Controllers/BaseTaxCategoryController.cs
+++ b/src/Web/Grand.Web.AdminShared/Controllers/BaseTaxCategoryController.cs
@@ -30,6 +30,21 @@ public abstract class BaseTaxCategoryController(
IAdminDataScope scope)
: BaseController
{
+ /// Exposed so host controllers that extend this base (Admin/Store's TaxController)
+ /// can reuse the same instance for their own Providers/Settings actions instead of re-capturing
+ /// their own copy of the primary-constructor parameter (which would trigger CS9107 - the
+ /// parameter stored twice, once here and once in the derived class).
+ protected ITaxCategoryService TaxCategoryService => taxCategoryService;
+
+ /// See .
+ protected IStoreService StoreService => storeService;
+
+ /// See .
+ protected ITranslationService TranslationService => translationService;
+
+ /// See .
+ protected IAdminDataScope Scope => scope;
+
[HttpPost]
[PermissionAuthorizeAction(PermissionActionName.List)]
public virtual async Task Categories(DataSourceRequest command)
diff --git a/src/Web/Grand.Web.AdminShared/Interfaces/IReportDataScope.cs b/src/Web/Grand.Web.AdminShared/Interfaces/IReportDataScope.cs
index 9ac616e8f4..1c4fb13ed7 100644
--- a/src/Web/Grand.Web.AdminShared/Interfaces/IReportDataScope.cs
+++ b/src/Web/Grand.Web.AdminShared/Interfaces/IReportDataScope.cs
@@ -40,5 +40,5 @@ public interface IReportDataScope
/// filtering) for Admin and Store. Vendor overrides this to additionally drop rows whose product
/// the current vendor sub-account has no access to (WorkContext.HasAccessToProduct) — preserves
/// Vendor's existing BestsellersReportList behavior exactly (see spec §2.3).
- bool CanIncludeProduct(Grand.Domain.Catalog.Product product) => true;
+ bool CanIncludeProduct(Grand.Domain.Catalog.Product? product) => true;
}
diff --git a/src/Web/Grand.Web.AdminShared/Services/AdminReportDataScope.cs b/src/Web/Grand.Web.AdminShared/Services/AdminReportDataScope.cs
index f3a9540a67..8af5d35fe9 100644
--- a/src/Web/Grand.Web.AdminShared/Services/AdminReportDataScope.cs
+++ b/src/Web/Grand.Web.AdminShared/Services/AdminReportDataScope.cs
@@ -19,5 +19,5 @@ public class AdminReportDataScope : IReportDataScope
public bool ShowStoreSelector => true;
public bool ShowVendorSelector => true;
public string ResourceKeyPrefix => "Admin";
- public bool CanIncludeProduct(Product product) => true;
+ public bool CanIncludeProduct(Product? product) => true;
}
diff --git a/src/Web/Grand.Web.AdminShared/Services/RoutedReportDataScope.cs b/src/Web/Grand.Web.AdminShared/Services/RoutedReportDataScope.cs
index 4051f92db3..4ea54da815 100644
--- a/src/Web/Grand.Web.AdminShared/Services/RoutedReportDataScope.cs
+++ b/src/Web/Grand.Web.AdminShared/Services/RoutedReportDataScope.cs
@@ -43,5 +43,5 @@ private IReportDataScope Resolved
public bool ShowStoreSelector => Resolved.ShowStoreSelector;
public bool ShowVendorSelector => Resolved.ShowVendorSelector;
public string ResourceKeyPrefix => Resolved.ResourceKeyPrefix;
- public bool CanIncludeProduct(Product product) => Resolved.CanIncludeProduct(product);
+ public bool CanIncludeProduct(Product? product) => Resolved.CanIncludeProduct(product);
}
diff --git a/src/Web/Grand.Web.AdminShared/Services/StoreReportDataScope.cs b/src/Web/Grand.Web.AdminShared/Services/StoreReportDataScope.cs
index 5c933dbce9..d9a4fc15c2 100644
--- a/src/Web/Grand.Web.AdminShared/Services/StoreReportDataScope.cs
+++ b/src/Web/Grand.Web.AdminShared/Services/StoreReportDataScope.cs
@@ -19,5 +19,5 @@ public class StoreReportDataScope(IContextAccessor contextAccessor) : IReportDat
public bool ShowStoreSelector => false;
public bool ShowVendorSelector => false;
public string ResourceKeyPrefix => "Admin";
- public bool CanIncludeProduct(Product product) => true;
+ public bool CanIncludeProduct(Product? product) => true;
}
diff --git a/src/Web/Grand.Web.AdminShared/Services/VendorReportDataScope.cs b/src/Web/Grand.Web.AdminShared/Services/VendorReportDataScope.cs
index 115ed84f4e..2329274a36 100644
--- a/src/Web/Grand.Web.AdminShared/Services/VendorReportDataScope.cs
+++ b/src/Web/Grand.Web.AdminShared/Services/VendorReportDataScope.cs
@@ -24,6 +24,6 @@ public class VendorReportDataScope(IContextAccessor contextAccessor) : IReportDa
public bool ShowVendorSelector => false;
public string ResourceKeyPrefix => "Vendor";
- public bool CanIncludeProduct(Product product) =>
+ public bool CanIncludeProduct(Product? product) =>
product is not null && product.VendorId == contextAccessor.WorkContext.CurrentVendor.Id;
}
diff --git a/src/Web/Grand.Web.Store/Controllers/BlogController.cs b/src/Web/Grand.Web.Store/Controllers/BlogController.cs
index 8b2284667e..a80d15b0d3 100644
--- a/src/Web/Grand.Web.Store/Controllers/BlogController.cs
+++ b/src/Web/Grand.Web.Store/Controllers/BlogController.cs
@@ -56,11 +56,11 @@ protected override void EditWarningCheck(BlogPost blogPost)
[PermissionAuthorizeAction(PermissionActionName.Preview)]
public async Task Preview(string id)
{
- var blogPost = await blogService.GetBlogPostById(id);
+ var blogPost = await BlogService.GetBlogPostById(id);
if (blogPost == null) return RedirectToAction("List");
if (!await PostScope.HasAccess(blogPost)) return RedirectToAction("List");
- var model = blogPost.ToModel(dateTimeService);
+ var model = blogPost.ToModel(DateTimeService);
return View(model);
}
}
diff --git a/src/Web/Grand.Web.Store/Controllers/CustomerController.cs b/src/Web/Grand.Web.Store/Controllers/CustomerController.cs
index b6d8cf5636..3436368ceb 100644
--- a/src/Web/Grand.Web.Store/Controllers/CustomerController.cs
+++ b/src/Web/Grand.Web.Store/Controllers/CustomerController.cs
@@ -73,12 +73,12 @@ public override async Task OnActionExecutionAsync(ActionExecutingContext context
/// ApplyPostConstraints_CraftedPost_CannotSmuggleOwnershipFields for the exact contract.
protected override async Task ApplyPostConstraints(CustomerModel model)
{
- model.StoreId = scope.DefaultStoreId;
+ model.StoreId = Scope.DefaultStoreId;
model.Owner = "";
model.VendorId = "";
model.StaffStoreId = "";
model.SeId = "";
- var registered = await groupService.GetCustomerGroupBySystemName(SystemCustomerGroupNames.Registered);
+ var registered = await GroupService.GetCustomerGroupBySystemName(SystemCustomerGroupNames.Registered);
model.CustomerGroups = registered != null ? new[] { registered.Id } : Array.Empty();
}
}
diff --git a/src/Web/Grand.Web.Store/Controllers/EmailAccountController.cs b/src/Web/Grand.Web.Store/Controllers/EmailAccountController.cs
index e25ba71b9e..8b19554a65 100644
--- a/src/Web/Grand.Web.Store/Controllers/EmailAccountController.cs
+++ b/src/Web/Grand.Web.Store/Controllers/EmailAccountController.cs
@@ -36,7 +36,7 @@ public IActionResult List()
[PermissionAuthorizeAction(PermissionActionName.List)]
public async Task List(DataSourceRequest command)
{
- var emailAccounts = await emailAccountService.GetAllEmailAccounts(scope.DefaultStoreId ?? "",
+ var emailAccounts = await EmailAccountService.GetAllEmailAccounts(Scope.DefaultStoreId ?? "",
pageIndex: command.Page - 1, pageSize: command.PageSize);
var emailAccountModels = emailAccounts.Select(x => x.ToModel()).ToList();
diff --git a/src/Web/Grand.Web.Store/Controllers/MessageTemplateController.cs b/src/Web/Grand.Web.Store/Controllers/MessageTemplateController.cs
index 6b7d9e44af..8a66f0268d 100644
--- a/src/Web/Grand.Web.Store/Controllers/MessageTemplateController.cs
+++ b/src/Web/Grand.Web.Store/Controllers/MessageTemplateController.cs
@@ -43,7 +43,7 @@ public class MessageTemplateController(
[HttpPost]
public async Task ListGlobal(DataSourceRequest command)
{
- var allTemplates = await messageTemplateService.GetAllMessageTemplates("");
+ var allTemplates = await MessageTemplateService.GetAllMessageTemplates("");
var globalTemplates = allTemplates
.Where(t => !t.LimitedToStores)
.ToList();
@@ -62,7 +62,7 @@ public async Task ListGlobal(DataSourceRequest command)
[HttpPost]
public async Task ListStore(DataSourceRequest command)
{
- var allTemplates = await messageTemplateService.GetAllMessageTemplates("");
+ var allTemplates = await MessageTemplateService.GetAllMessageTemplates("");
var storeTemplates = allTemplates
.Where(t => t.LimitedToStores && t.Stores.Contains(CurrentStoreId))
.ToList();
diff --git a/src/Web/Grand.Web.Store/Controllers/NewsController.cs b/src/Web/Grand.Web.Store/Controllers/NewsController.cs
index a5012ac112..82e3cfedba 100644
--- a/src/Web/Grand.Web.Store/Controllers/NewsController.cs
+++ b/src/Web/Grand.Web.Store/Controllers/NewsController.cs
@@ -51,11 +51,11 @@ protected override void EditWarningCheck(NewsItem newsItem)
[PermissionAuthorizeAction(PermissionActionName.Preview)]
public async Task Preview(string id)
{
- var newsItem = await newsService.GetNewsById(id);
+ var newsItem = await NewsService.GetNewsById(id);
if (newsItem == null) return RedirectToAction("List");
if (!await Scope.HasAccess(newsItem)) return RedirectToAction("List");
- var model = newsItem.ToModel(dateTimeService);
+ var model = newsItem.ToModel(DateTimeService);
return View(model);
}
}
diff --git a/src/Web/Grand.Web.Store/Controllers/PageController.cs b/src/Web/Grand.Web.Store/Controllers/PageController.cs
index e34390cb8b..ec0be4b668 100644
--- a/src/Web/Grand.Web.Store/Controllers/PageController.cs
+++ b/src/Web/Grand.Web.Store/Controllers/PageController.cs
@@ -52,11 +52,11 @@ protected override void EditWarningCheck(Page page)
[HttpPost]
public async Task StorePagesList(DataSourceRequest command, PageListModel model)
{
- var pages = await pageService.GetAllPages(Scope.DefaultStoreId, true);
+ var pages = await PageService.GetAllPages(Scope.DefaultStoreId, true);
var pageModels = pages
.Where(x => x.LimitedToStores && x.Stores.Count == 1)
- .Select(x => x.ToModel(dateTimeService))
+ .Select(x => x.ToModel(DateTimeService))
.ToList();
if (!string.IsNullOrEmpty(model.Name))
@@ -75,11 +75,11 @@ public async Task StorePagesList(DataSourceRequest command, PageL
[HttpPost]
public async Task GlobalPagesList(DataSourceRequest command, PageListModel model)
{
- var pages = await pageService.GetAllPages(Scope.DefaultStoreId, true);
+ var pages = await PageService.GetAllPages(Scope.DefaultStoreId, true);
var pageModels = pages
.Where(x => !x.LimitedToStores || x.Stores.Count > 1)
- .Select(x => x.ToModel(dateTimeService))
+ .Select(x => x.ToModel(DateTimeService))
.ToList();
if (!string.IsNullOrEmpty(model.Name))
@@ -99,7 +99,7 @@ public async Task GlobalPagesList(DataSourceRequest command, Page
public async Task Copy(string id)
{
var storeId = Scope.DefaultStoreId;
- var page = await pageService.GetPageById(id);
+ var page = await PageService.GetPageById(id);
if (page == null) return RedirectToAction("List");
// A page is copyable only while it is still readable here and not yet owned by this store, so
@@ -111,19 +111,19 @@ public async Task Copy(string id)
if (page.LimitedToStores && page.Stores.Count <= 1)
return RedirectToAction("Edit", new { id });
- var storePages = await pageService.GetAllPages(storeId, true);
+ var storePages = await PageService.GetAllPages(storeId, true);
if (storePages.Any(p => p.Id != page.Id &&
p.SystemName.Equals(page.SystemName, StringComparison.OrdinalIgnoreCase)))
{
- Error(translationService.GetResource("Admin.Content.Pages.Copy.DuplicateSystemName"));
+ Error(TranslationService.GetResource("Admin.Content.Pages.Copy.DuplicateSystemName"));
return RedirectToAction("Edit", new { id });
}
- var model = page.ToModel(dateTimeService);
+ var model = page.ToModel(DateTimeService);
model.Id = "";
model.Stores = [storeId];
- await AddLocales(languageService, model.Locales, (locale, languageId) =>
+ await AddLocales(LanguageService, model.Locales, (locale, languageId) =>
{
locale.Title = page.GetTranslation(x => x.Title, languageId, false);
locale.Body = page.GetTranslation(x => x.Body, languageId, false);
@@ -133,8 +133,8 @@ await AddLocales(languageService, model.Locales, (locale, languageId) =>
locale.SeName = page.GetSeName(languageId, false);
});
- var newPage = await pageViewModelService.InsertPageModel(model);
- Success(translationService.GetResource("Admin.Content.Pages.Added"));
+ var newPage = await PageViewModelService.InsertPageModel(model);
+ Success(TranslationService.GetResource("Admin.Content.Pages.Added"));
return RedirectToAction("Edit", new { id = newPage.Id });
}
}
diff --git a/src/Web/Grand.Web.Store/Controllers/TaxController.cs b/src/Web/Grand.Web.Store/Controllers/TaxController.cs
index b3d2141302..bb45034873 100644
--- a/src/Web/Grand.Web.Store/Controllers/TaxController.cs
+++ b/src/Web/Grand.Web.Store/Controllers/TaxController.cs
@@ -111,15 +111,15 @@ public async Task Settings()
model.TaxBasedOnValues = enumTranslationService.ToSelectList(taxSettings.TaxBasedOn);
model.TaxDisplayTypeValues = enumTranslationService.ToSelectList(taxSettings.TaxDisplayType);
- var taxCategories = await taxCategoryService.GetAllTaxCategories(CurrentStoreId);
+ var taxCategories = await TaxCategoryService.GetAllTaxCategories(CurrentStoreId);
model.TaxCategories.Add(new SelectListItem {
- Text = translationService.GetResource("Admin.Configuration.Tax.Settings.TaxCategories.None"), Value = ""
+ Text = TranslationService.GetResource("Admin.Configuration.Tax.Settings.TaxCategories.None"), Value = ""
});
foreach (var tc in taxCategories)
model.TaxCategories.Add(new SelectListItem { Text = tc.Name, Value = tc.Id });
model.EuVatShopCountries.Add(new SelectListItem
- { Text = translationService.GetResource("Admin.Address.SelectCountry"), Value = "" });
+ { Text = TranslationService.GetResource("Admin.Address.SelectCountry"), Value = "" });
foreach (var c in await countryService.GetAllCountries(showHidden: true))
model.EuVatShopCountries.Add(new SelectListItem
{ Text = c.Name, Value = c.Id, Selected = c.Id == taxSettings.EuVatShopCountryId });
@@ -130,7 +130,7 @@ public async Task Settings()
: new AddressModel();
model.DefaultTaxAddress.AvailableCountries.Add(new SelectListItem
- { Text = translationService.GetResource("Admin.Address.SelectCountry"), Value = "" });
+ { Text = TranslationService.GetResource("Admin.Address.SelectCountry"), Value = "" });
foreach (var c in await countryService.GetAllCountries(showHidden: true))
model.DefaultTaxAddress.AvailableCountries.Add(new SelectListItem
{ Text = c.Name, Value = c.Id, Selected = defaultAddress != null && c.Id == defaultAddress.CountryId });
@@ -159,7 +159,7 @@ public async Task Settings(TaxSettingsModel model)
taxSettings = model.ToEntity(taxSettings);
await settingService.SaveSetting(taxSettings, CurrentStoreId);
await cacheBase.Clear();
- Success(translationService.GetResource("Admin.Configuration.Updated"));
+ Success(TranslationService.GetResource("Admin.Configuration.Updated"));
return RedirectToAction("Settings");
}