diff --git a/src/Business/Grand.Business.Authentication/Services/ExternalAuthenticationService.cs b/src/Business/Grand.Business.Authentication/Services/ExternalAuthenticationService.cs
index e1d42be685..2cb6fd3b4a 100644
--- a/src/Business/Grand.Business.Authentication/Services/ExternalAuthenticationService.cs
+++ b/src/Business/Grand.Business.Authentication/Services/ExternalAuthenticationService.cs
@@ -8,6 +8,7 @@
using Grand.Business.Core.Utilities.Customers;
using Grand.Data;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Stores;
using Grand.Infrastructure.Extensions;
using Grand.SharedKernel.Extensions;
@@ -70,14 +71,15 @@ public ExternalAuthenticationService(
/// Current logged-in user
/// Customer of the request (a guest when nobody is logged in)
/// Store identifier
+ /// Working language of the request
/// URL to which the user will return after authentication
/// Result of an authentication
protected virtual async Task AuthenticateExistingUser(Customer associatedUser,
- Customer currentLoggedInUser, Customer customer, string storeId, string returnUrl)
+ Customer currentLoggedInUser, Customer customer, string storeId, Language language, string returnUrl)
{
//log in guest user
if (currentLoggedInUser == null)
- return await LoginUser(associatedUser, customer, storeId, returnUrl);
+ return await LoginUser(associatedUser, customer, storeId, language, returnUrl);
//account is already assigned to another user
if (currentLoggedInUser.Id != associatedUser.Id)
@@ -95,10 +97,11 @@ protected virtual async Task AuthenticateExistingUser(Customer as
/// Authentication parameters received from external authentication method
/// Customer of the request (a guest when nobody is logged in)
/// Store identifier
+ /// Working language of the request
/// URL to which the user will return after authentication
/// Result of an authentication
protected virtual async Task AuthenticateNewUser(Customer currentLoggedInUser,
- ExternalAuthParam parameters, Customer customer, string storeId, string returnUrl)
+ ExternalAuthParam parameters, Customer customer, string storeId, Language language, string returnUrl)
{
//associate external account with logged-in user
if (currentLoggedInUser != null)
@@ -111,7 +114,7 @@ protected virtual async Task AuthenticateNewUser(Customer current
//or try to register new user
if (_customerSettings.UserRegistrationType != UserRegistrationType.Disabled)
- return await RegisterNewUser(parameters, customer, storeId, returnUrl);
+ return await RegisterNewUser(parameters, customer, storeId, language, returnUrl);
//registration is disabled
return Error(["Registration is disabled"]);
@@ -123,10 +126,11 @@ protected virtual async Task AuthenticateNewUser(Customer current
/// Authentication parameters received from external authentication method
/// Guest customer of the request; registration turns it into the new user
/// Store identifier
+ /// Working language of the request
/// URL to which the user will return after authentication
/// Result of an authentication
protected virtual async Task RegisterNewUser(ExternalAuthParam parameters, Customer customer,
- string storeId, string returnUrl)
+ string storeId, Language language, string returnUrl)
{
var approved =
_customerSettings.UserRegistrationType is UserRegistrationType.Standard
@@ -147,7 +151,7 @@ _customerSettings.UserRegistrationType is UserRegistrationType.Standard
await _mediator.Publish(new RegisteredByExternalMethod(customer, parameters));
//raise customer registered event
- await _mediator.Publish(new CustomerRegisteredEvent(customer));
+ await _mediator.Publish(new CustomerRegisteredEvent(customer, language));
//associate external account with registered user
await AssociateCustomer(customer, parameters);
@@ -169,13 +173,14 @@ _customerSettings.UserRegistrationType is UserRegistrationType.Standard
/// User to login
/// Customer of the request before the login (the guest whose cart is merged)
/// Store identifier
+ /// Working language of the request
/// URL to which the user will return after authentication
/// Result of an authentication
protected virtual async Task LoginUser(Customer user, Customer previousCustomer, string storeId,
- string returnUrl)
+ Language language, string returnUrl)
{
//raise event
- await _mediator.Publish(new CustomerLoggedInEvent(user, previousCustomer, storeId));
+ await _mediator.Publish(new CustomerLoggedInEvent(user, previousCustomer, storeId, language));
//authenticate
await _authenticationService.SignIn(user, false);
@@ -270,14 +275,16 @@ public virtual bool AuthenticationProviderIsAvailable(string systemName, Custome
/// External authentication parameters
/// Customer of the request (a guest when nobody is logged in)
/// Store of the request
+ /// Working language of the request
/// URL to which the user will return after authentication
/// Result of an authentication
public virtual async Task Authenticate(ExternalAuthParam parameters, Customer customer, Store store,
- string returnUrl = null)
+ Language language, string returnUrl = null)
{
ArgumentNullException.ThrowIfNull(parameters);
ArgumentNullException.ThrowIfNull(customer);
ArgumentNullException.ThrowIfNull(store);
+ ArgumentNullException.ThrowIfNull(language);
if (!AuthenticationProviderIsAvailable(parameters.ProviderSystemName, customer, store))
return Error(["External authentication method cannot be loaded"]);
@@ -288,10 +295,11 @@ public virtual async Task Authenticate(ExternalAuthParam paramete
//authenticate associated user if already exists
var associatedUser = await GetCustomer(parameters);
if (associatedUser != null)
- return await AuthenticateExistingUser(associatedUser, currentLoggedInUser, customer, store.Id, returnUrl);
+ return await AuthenticateExistingUser(associatedUser, currentLoggedInUser, customer, store.Id, language,
+ returnUrl);
//or associate and authenticate new user
- return await AuthenticateNewUser(currentLoggedInUser, parameters, customer, store.Id, returnUrl);
+ return await AuthenticateNewUser(currentLoggedInUser, parameters, customer, store.Id, language, returnUrl);
}
#endregion
diff --git a/src/Business/Grand.Business.Authentication/Services/OidcCustomerResolver.cs b/src/Business/Grand.Business.Authentication/Services/OidcCustomerResolver.cs
index 6d4c3871be..41fa7461a5 100644
--- a/src/Business/Grand.Business.Authentication/Services/OidcCustomerResolver.cs
+++ b/src/Business/Grand.Business.Authentication/Services/OidcCustomerResolver.cs
@@ -10,6 +10,7 @@
using Grand.Data;
using Grand.Domain.Common;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Permissions;
using Grand.Infrastructure.Configuration;
using Grand.Mediator;
@@ -146,7 +147,8 @@ await RequiresPassword(linkedCustomer, storeId))
return result;
}
- public virtual async Task ResolveForLogin(string sub, string email, bool emailVerified, string storeId)
+ public virtual async Task ResolveForLogin(string sub, string email, bool emailVerified, string storeId,
+ Language language)
{
if (string.IsNullOrEmpty(sub))
return OidcLoginResult.Refused(OidcLoginRefusal.InvalidIdentity);
@@ -186,7 +188,7 @@ public virtual async Task ResolveForLogin(string sub, string em
{
//provider-only with automatic accounts: the provider decides who gets in, no form and no registration type
if (_loginConfig is { ProviderOnly: true, AutoProvision: true })
- return await ProvisionForLogin(sub, email, emailVerified, storeId);
+ return await ProvisionForLogin(sub, email, emailVerified, storeId, language);
var customerSettings = await _settingService.LoadSetting(storeId);
if (customerSettings.UserRegistrationType == UserRegistrationType.Disabled)
@@ -277,7 +279,8 @@ private async Task Register(string sub, string email, string
if (existing != null)
return await Usable(await _customerService.GetCustomerById(existing.CustomerId), sub, storeId);
- var (outcome, customer, winner) = await CreateLinkedAccount(sub, email, storeId);
+ //an access token carries no working language: the registration falls back to the store's
+ var (outcome, customer, winner) = await CreateLinkedAccount(sub, email, storeId, null);
return outcome switch {
Provisioning.UserNameTaken => OidcCustomerResult.Forbidden("The user name is already taken"),
Provisioning.LinkedToOtherIdentity => LinkedToOtherIdentity(customer, sub),
@@ -290,14 +293,15 @@ private async Task Register(string sub, string email, string
/// ProviderOnly + AutoProvision: the account of an unknown, provider-verified e-mail, created and linked at its first
/// browser sign-in (the profile sync of the sign-in fills in the names)
///
- private async Task ProvisionForLogin(string sub, string email, bool emailVerified, string storeId)
+ private async Task ProvisionForLogin(string sub, string email, bool emailVerified, string storeId,
+ Language language)
{
- var (outcome, customer, _) = await CreateLinkedAccount(sub, email, storeId);
+ var (outcome, customer, _) = await CreateLinkedAccount(sub, email, storeId, language);
return outcome switch {
Provisioning.UserNameTaken => OidcLoginResult.Refused(OidcLoginRefusal.AccountUnusable),
Provisioning.LinkedToOtherIdentity => OidcLoginResult.Refused(OidcLoginRefusal.LinkedToOtherIdentity),
//another request of the same user created the account first: its link answers now
- Provisioning.LostRace => await ResolveForLogin(sub, email, emailVerified, storeId),
+ Provisioning.LostRace => await ResolveForLogin(sub, email, emailVerified, storeId, language),
_ => OidcLoginResult.SignIn(customer)
};
}
@@ -315,7 +319,7 @@ private enum Provisioning
/// e-mail validation / admin approval does not apply: access comes from the provider and from permissions an admin grants
///
private async Task<(Provisioning Outcome, Customer Customer, Customer Winner)> CreateLinkedAccount(string sub,
- string email, string storeId)
+ string email, string storeId, Language language)
{
//the e-mail becomes the user name - another account may hold it as its user name without that e-mail
var normalizedEmail = email.ToLowerInvariant();
@@ -347,7 +351,7 @@ await _customerManagerService.RegisterCustomer(new RegistrationRequest(customer,
}
//only after the link is stored, so the loser of a race sends no welcome message
- await _mediator.Publish(new CustomerRegisteredEvent(customer));
+ await _mediator.Publish(new CustomerRegisteredEvent(customer, language));
_logger.LogInformation("Subject {Subject} registered as customer {CustomerId} (store {StoreId})",
sub, customer.Id, storeId);
return (Provisioning.Created, customer, null);
diff --git a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/AddRequiredProductsCommandHandler.cs b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/AddRequiredProductsCommandHandler.cs
index 48167b835c..64a2d31be2 100644
--- a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/AddRequiredProductsCommandHandler.cs
+++ b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/AddRequiredProductsCommandHandler.cs
@@ -51,7 +51,7 @@ public async Task Handle(AddRequiredProductsCommand request, CancellationT
//not in the cart
if (alreadyInTheCart) continue;
if (!product.AutoAddRequiredProducts) continue;
- var addToCart = await _shoppingCartService.AddToCart(customer,
+ var addToCart = await _shoppingCartService.AddToCart(customer, request.Language,
rp.Id,
shoppingCartType,
storeId,
@@ -63,7 +63,8 @@ await Handle(new AddRequiredProductsCommand {
Customer = customer,
ShoppingCartType = shoppingCartType,
StoreId = storeId,
- Product = rp
+ Product = rp,
+ Language = request.Language
}, cancellationToken);
}
}
diff --git a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/AwardLoyaltyPointsCommandHandler.cs b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/AwardLoyaltyPointsCommandHandler.cs
index aaa7c116d5..0f30e2ddc7 100644
--- a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/AwardLoyaltyPointsCommandHandler.cs
+++ b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/AwardLoyaltyPointsCommandHandler.cs
@@ -1,8 +1,11 @@
using Grand.Business.Core.Commands.Checkout.Orders;
+using Grand.Business.Core.Extensions;
using Grand.Business.Core.Interfaces.Checkout.Orders;
using Grand.Business.Core.Interfaces.Common.Directory;
using Grand.Business.Core.Interfaces.Common.Localization;
+using Grand.Business.Core.Interfaces.Common.Stores;
using Grand.Business.Core.Interfaces.Customers;
+using Grand.Domain.Orders;
using Grand.Mediator;
namespace Grand.Business.Checkout.Commands.Handlers.Orders;
@@ -14,6 +17,8 @@ public class AwardLoyaltyPointsCommandHandler : IRequestHandler GetMessage(string resourceKey, Order order)
+ {
+ var store = await _storeService.GetStoreById(order.StoreId);
+ var language = await _languageService.GetLanguageOrStoreDefault(order.CustomerLanguageId, store);
+ return language != null ? _translationService.GetResource(resourceKey, language.Id) : "";
+ }
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/PlaceOrderCommandHandler.cs b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/PlaceOrderCommandHandler.cs
index f8e8280201..01527bb29d 100644
--- a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/PlaceOrderCommandHandler.cs
+++ b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/PlaceOrderCommandHandler.cs
@@ -458,7 +458,7 @@ await _checkoutAttributeFormatter.FormatAttributes(details.CheckoutAttributes, d
//validate the entire shopping cart
var warnings =
await _shoppingCartValidator.GetShoppingCartWarnings(details.Cart, details.CheckoutAttributes, details.Context,
- true, true);
+ details.WorkingLanguage, true, true);
if (warnings.Any()) throw new GrandException(string.Join(", ", warnings));
//validate individual cart items
@@ -466,7 +466,7 @@ await _shoppingCartValidator.GetShoppingCartWarnings(details.Cart, details.Check
{
var product = await _productService.GetProductById(sci.ProductId);
var sciWarnings = await _shoppingCartValidator.GetShoppingCartItemWarnings(details.Customer, sci, product,
- new ShoppingCartValidatorOptions());
+ new ShoppingCartValidatorOptions(), details.WorkingLanguage);
if (sciWarnings.Any())
{
var warningsSb = new StringBuilder();
diff --git a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/ReOrderCommandHandler.cs b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/ReOrderCommandHandler.cs
index 56dca1e45c..668fcd8a80 100644
--- a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/ReOrderCommandHandler.cs
+++ b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/ReOrderCommandHandler.cs
@@ -32,6 +32,7 @@ public ReOrderCommandHandler(
public async Task> Handle(ReOrderCommand request, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(request.Order);
+ ArgumentNullException.ThrowIfNull(request.Language);
var warnings = new List();
var customer = await _customerService.GetCustomerById(request.Order.CustomerId);
@@ -42,7 +43,8 @@ public async Task> Handle(ReOrderCommand request, CancellationToke
if (product != null)
{
if (product.ProductTypeId == ProductType.SimpleProduct)
- warnings.AddRange((await _shoppingCartService.AddToCart(customer, orderItem.ProductId,
+ warnings.AddRange((await _shoppingCartService.AddToCart(customer,
+ request.Language, orderItem.ProductId,
ShoppingCartType.ShoppingCart, request.Order.StoreId, orderItem.WarehouseId,
orderItem.Attributes,
product.EnteredPrice
diff --git a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/ReduceLoyaltyPointsCommandHandler.cs b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/ReduceLoyaltyPointsCommandHandler.cs
index a9604af1c6..c0586a4469 100644
--- a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/ReduceLoyaltyPointsCommandHandler.cs
+++ b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/ReduceLoyaltyPointsCommandHandler.cs
@@ -1,8 +1,11 @@
using Grand.Business.Core.Commands.Checkout.Orders;
+using Grand.Business.Core.Extensions;
using Grand.Business.Core.Interfaces.Checkout.Orders;
using Grand.Business.Core.Interfaces.Common.Directory;
using Grand.Business.Core.Interfaces.Common.Localization;
+using Grand.Business.Core.Interfaces.Common.Stores;
using Grand.Business.Core.Interfaces.Customers;
+using Grand.Domain.Orders;
using Grand.Mediator;
namespace Grand.Business.Checkout.Commands.Handlers.Orders;
@@ -14,6 +17,8 @@ public class ReduceLoyaltyPointsCommandHandler : IRequestHandler GetMessage(string resourceKey, Order order)
+ {
+ var store = await _storeService.GetStoreById(order.StoreId);
+ var language = await _languageService.GetLanguageOrStoreDefault(order.CustomerLanguageId, store);
+ return language != null ? _translationService.GetResource(resourceKey, language.Id) : "";
+ }
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/ReturnBackRedeemedLoyaltyPointsCommandHandler.cs b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/ReturnBackRedeemedLoyaltyPointsCommandHandler.cs
index 75015db674..4d995c33d2 100644
--- a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/ReturnBackRedeemedLoyaltyPointsCommandHandler.cs
+++ b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/ReturnBackRedeemedLoyaltyPointsCommandHandler.cs
@@ -1,6 +1,9 @@
using Grand.Business.Core.Commands.Checkout.Orders;
+using Grand.Business.Core.Extensions;
using Grand.Business.Core.Interfaces.Checkout.Orders;
using Grand.Business.Core.Interfaces.Common.Localization;
+using Grand.Business.Core.Interfaces.Common.Stores;
+using Grand.Domain.Orders;
using Grand.Mediator;
namespace Grand.Business.Checkout.Commands.Handlers.Orders;
@@ -9,14 +12,20 @@ public class
ReturnBackRedeemedLoyaltyPointsCommandHandler : IRequestHandler
{
private readonly ILoyaltyPointsService _loyaltyPointsService;
+ private readonly ILanguageService _languageService;
+ private readonly IStoreService _storeService;
private readonly ITranslationService _translationService;
public ReturnBackRedeemedLoyaltyPointsCommandHandler(
ILoyaltyPointsService loyaltyPointsService,
- ITranslationService translationService)
+ ITranslationService translationService,
+ ILanguageService languageService,
+ IStoreService storeService)
{
_loyaltyPointsService = loyaltyPointsService;
_translationService = translationService;
+ _languageService = languageService;
+ _storeService = storeService;
}
public async Task Handle(ReturnBackRedeemedLoyaltyPointsCommand request, CancellationToken cancellationToken)
@@ -30,9 +39,17 @@ public async Task Handle(ReturnBackRedeemedLoyaltyPointsCommand request, C
//return back
await _loyaltyPointsService.AddLoyaltyPointsHistory(request.Order.CustomerId,
-request.Order.RedeemedLoyaltyPoints, request.Order.StoreId,
- string.Format(_translationService.GetResource("LoyaltyPoints.Message.ReturnedForOrder"),
+ string.Format(await GetMessage("LoyaltyPoints.Message.ReturnedForOrder", request.Order),
request.Order.OrderNumber));
return true;
}
+
+ //the message is written in the language the order was placed in, or in the store's language when that is gone
+ private async Task GetMessage(string resourceKey, Order order)
+ {
+ var store = await _storeService.GetStoreById(order.StoreId);
+ var language = await _languageService.GetLanguageOrStoreDefault(order.CustomerLanguageId, store);
+ return language != null ? _translationService.GetResource(resourceKey, language.Id) : "";
+ }
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Checkout/Events/Customers/CustomerRegisteredEventHandler.cs b/src/Business/Grand.Business.Checkout/Events/Customers/CustomerRegisteredEventHandler.cs
index dbcaf218d6..602ba90f6e 100644
--- a/src/Business/Grand.Business.Checkout/Events/Customers/CustomerRegisteredEventHandler.cs
+++ b/src/Business/Grand.Business.Checkout/Events/Customers/CustomerRegisteredEventHandler.cs
@@ -1,6 +1,8 @@
using Grand.Business.Core.Events.Customers;
+using Grand.Business.Core.Extensions;
using Grand.Business.Core.Interfaces.Checkout.Orders;
using Grand.Business.Core.Interfaces.Common.Localization;
+using Grand.Business.Core.Interfaces.Common.Stores;
using Grand.Domain.Orders;
using Grand.Mediator;
@@ -8,29 +10,42 @@ namespace Grand.Business.Checkout.Events.Customers;
public class CustomerRegisteredEventHandler : INotificationHandler
{
+ private readonly ILanguageService _languageService;
private readonly ILoyaltyPointsService _loyaltyPointsService;
private readonly LoyaltyPointsSettings _loyaltyPointsSettings;
+ private readonly IStoreService _storeService;
private readonly ITranslationService _translationService;
public CustomerRegisteredEventHandler(
ITranslationService translationService,
ILoyaltyPointsService loyaltyPointsService,
- LoyaltyPointsSettings loyaltyPointsSettings
+ LoyaltyPointsSettings loyaltyPointsSettings,
+ IStoreService storeService,
+ ILanguageService languageService
)
{
_translationService = translationService;
_loyaltyPointsService = loyaltyPointsService;
_loyaltyPointsSettings = loyaltyPointsSettings;
+ _storeService = storeService;
+ _languageService = languageService;
}
public async Task Handle(CustomerRegisteredEvent notification, CancellationToken cancellationToken)
{
//Add loyalty points for customer registration (if enabled)
- if (_loyaltyPointsSettings.Enabled &&
- _loyaltyPointsSettings.PointsForRegistration > 0)
- await _loyaltyPointsService.AddLoyaltyPointsHistory(notification.Customer.Id,
- _loyaltyPointsSettings.PointsForRegistration,
- notification.Customer.StoreId,
- _translationService.GetResource("LoyaltyPoints.Message.EarnedForRegistration"));
+ if (!_loyaltyPointsSettings.Enabled || _loyaltyPointsSettings.PointsForRegistration <= 0)
+ return;
+
+ //the request language; registration without a request (access-token sign-up) writes in the store's language
+ var language = notification.Language ??
+ await _languageService.GetLanguageOrStoreDefault(null,
+ await _storeService.GetStoreById(notification.Customer.StoreId));
+ await _loyaltyPointsService.AddLoyaltyPointsHistory(notification.Customer.Id,
+ _loyaltyPointsSettings.PointsForRegistration,
+ notification.Customer.StoreId,
+ language != null
+ ? _translationService.GetResource("LoyaltyPoints.Message.EarnedForRegistration", language.Id)
+ : "");
}
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Checkout/Events/ShoppingCart/CustomerLoggedInEventHandler.cs b/src/Business/Grand.Business.Checkout/Events/ShoppingCart/CustomerLoggedInEventHandler.cs
index 22e9efb41c..750c49cce1 100644
--- a/src/Business/Grand.Business.Checkout/Events/ShoppingCart/CustomerLoggedInEventHandler.cs
+++ b/src/Business/Grand.Business.Checkout/Events/ShoppingCart/CustomerLoggedInEventHandler.cs
@@ -17,6 +17,6 @@ public async Task Handle(CustomerLoggedInEvent notification, CancellationToken c
{
//migrate shopping cart
await _shoppingCartService.MigrateShoppingCart(notification.PreviousCustomer, notification.Customer,
- notification.StoreId, true);
+ notification.StoreId, notification.Language, true);
}
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Checkout/Services/Orders/ShoppingCartService.cs b/src/Business/Grand.Business.Checkout/Services/Orders/ShoppingCartService.cs
index ba0a992d36..fff4930d34 100644
--- a/src/Business/Grand.Business.Checkout/Services/Orders/ShoppingCartService.cs
+++ b/src/Business/Grand.Business.Checkout/Services/Orders/ShoppingCartService.cs
@@ -7,6 +7,7 @@
using Grand.Domain.Catalog;
using Grand.Domain.Common;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.Infrastructure.Extensions;
using Grand.Mediator;
@@ -170,6 +171,7 @@ public virtual async Task FindShoppingCartItem(IListProduct id
/// ShoppingCartType
/// Store id
+ /// Language the warnings are written in
/// Warehouse id
/// Attributes
/// EnteredPrice
@@ -183,7 +185,7 @@ public virtual async Task FindShoppingCartItem(IListShoppingCartValidatorOptions
/// (warnings, shoppingCartItem)
public virtual async Task<(IList warnings, ShoppingCartItem shoppingCartItem)> AddToCart(Customer customer,
- string productId,
+ Language language, string productId,
ShoppingCartType shoppingCartType, string storeId,
string warehouseId = null, IList attributes = null,
double? customerEnteredPrice = null,
@@ -206,7 +208,7 @@ public virtual async Task FindShoppingCartItem(IList FindShoppingCartItem(IList InsertNewItem(
- Customer customer, Product product, ShoppingCartItem shoppingCartItem,
+ Customer customer, Product product, ShoppingCartItem shoppingCartItem, Language language,
bool automaticallyAddRequiredProductsIfEnabled)
{
customer.ShoppingCartItems.Add(shoppingCartItem);
@@ -289,7 +292,8 @@ await _mediator.Send(new AddRequiredProductsCommand {
Customer = customer,
Product = product,
ShoppingCartType = shoppingCartItem.ShoppingCartTypeId,
- StoreId = shoppingCartItem.StoreId
+ StoreId = shoppingCartItem.StoreId,
+ Language = language
});
return shoppingCartItem;
@@ -302,6 +306,7 @@ await _mediator.Send(new AddRequiredProductsCommand {
/// Shopping cart item identifier
///
/// Attributes
+ /// Language the warnings are written in
/// New customer entered price
/// Rental start date
/// Rental end date
@@ -310,7 +315,7 @@ await _mediator.Send(new AddRequiredProductsCommand {
///
///
/// Warnings
- public virtual async Task> UpdateShoppingCartItem(Customer customer,
+ public virtual async Task> UpdateShoppingCartItem(Customer customer, Language language,
string shoppingCartItemId, string warehouseId, IList attributes,
double? customerEnteredPrice = null,
DateTime? rentalStartDate = null, DateTime? rentalEndDate = null,
@@ -342,7 +347,7 @@ public virtual async Task> UpdateShoppingCartItem(Customer custome
shoppingCartItem.IsGiftVoucher = product.IsGiftVoucher;
//check warnings
warnings.AddRange(await _shoppingCartValidator.GetShoppingCartItemWarnings(customer, shoppingCartItem,
- product, new ShoppingCartValidatorOptions()));
+ product, new ShoppingCartValidatorOptions(), language));
if (warnings.Any()) return warnings;
//if everything is OK, then update a shopping cart item
await _customerService.UpdateShoppingCartItem(customer.Id, shoppingCartItem);
@@ -395,12 +400,13 @@ public virtual async Task DeleteShoppingCartItem(Customer customer, ShoppingCart
///
/// Store whose selected checkout attributes move along, when the source customer has selected any there
///
+ /// Language the items are validated in
///
/// A value indicating whether to coupon codes (discount and gift voucher) should be also
/// re-applied
///
public virtual async Task MigrateShoppingCart(Customer fromCustomer, Customer toCustomer, string storeId,
- bool includeCouponCodes)
+ Language language, bool includeCouponCodes)
{
ArgumentNullException.ThrowIfNull(fromCustomer);
ArgumentNullException.ThrowIfNull(toCustomer);
@@ -416,10 +422,11 @@ public virtual async Task MigrateShoppingCart(Customer fromCustomer, Customer to
{
var product = await _productService.GetProductById(sci.ProductId);
if (product != null &&
- (await _shoppingCartValidator.GetRequiredProductWarnings(fromCustomer, sci, product, sci.StoreId)).Any())
+ (await _shoppingCartValidator.GetRequiredProductWarnings(fromCustomer, sci, product, sci.StoreId,
+ language)).Any())
continue;
- await AddToCart(toCustomer, sci.ProductId, sci.ShoppingCartTypeId, sci.StoreId, sci.WarehouseId,
+ await AddToCart(toCustomer, language, sci.ProductId, sci.ShoppingCartTypeId, sci.StoreId, sci.WarehouseId,
sci.Attributes, sci.EnteredPrice,
sci.RentalStartDateUtc, sci.RentalEndDateUtc, sci.Quantity, false, sci.ReservationId, sci.Parameter,
sci.Duration,
diff --git a/src/Business/Grand.Business.Checkout/Services/Orders/ShoppingCartValidator.cs b/src/Business/Grand.Business.Checkout/Services/Orders/ShoppingCartValidator.cs
index 47c4cfdd6b..24660dded2 100644
--- a/src/Business/Grand.Business.Checkout/Services/Orders/ShoppingCartValidator.cs
+++ b/src/Business/Grand.Business.Checkout/Services/Orders/ShoppingCartValidator.cs
@@ -5,6 +5,7 @@
using Grand.Domain.Catalog;
using Grand.Domain.Common;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.Infrastructure.Validators;
using Grand.Mediator;
@@ -25,15 +26,16 @@ public ShoppingCartValidator(
}
public virtual async Task> GetStandardWarnings(Customer customer, Product product,
- ShoppingCartItem shoppingCartItem)
+ ShoppingCartItem shoppingCartItem, Language language)
{
ArgumentNullException.ThrowIfNull(customer);
ArgumentNullException.ThrowIfNull(product);
+ ArgumentNullException.ThrowIfNull(language);
var warnings = new List();
var result = await _validatorFactory.GetValidator()
- .ValidateAsync(new ShoppingCartStandardValidatorRecord(customer, product, shoppingCartItem));
+ .ValidateAsync(new ShoppingCartStandardValidatorRecord(customer, product, shoppingCartItem, language));
if (!result.IsValid)
warnings.AddRange(result.Errors.Select(x => x.ErrorMessage));
@@ -41,15 +43,16 @@ public virtual async Task> GetStandardWarnings(Customer customer,
}
public virtual async Task> GetShoppingCartItemAttributeWarnings(Customer customer, Product product,
- ShoppingCartItem shoppingCartItem, bool ignoreNonCombinableAttributes = false)
+ ShoppingCartItem shoppingCartItem, Language language, bool ignoreNonCombinableAttributes = false)
{
ArgumentNullException.ThrowIfNull(product);
+ ArgumentNullException.ThrowIfNull(language);
var warnings = new List();
var result = await _validatorFactory.GetValidator()
.ValidateAsync(new ShoppingCartItemAttributeValidatorRecord(customer, product, shoppingCartItem,
- ignoreNonCombinableAttributes));
+ ignoreNonCombinableAttributes, language));
if (!result.IsValid)
warnings.AddRange(result.Errors.Select(x => x.ErrorMessage));
@@ -58,16 +61,17 @@ public virtual async Task> GetShoppingCartItemAttributeWarnings(Cu
public virtual async Task> GetShoppingCartItemGiftVoucherWarnings(Customer customer,
- Product product, ShoppingCartItem shoppingCartItem)
+ Product product, ShoppingCartItem shoppingCartItem, Language language)
{
ArgumentNullException.ThrowIfNull(product);
+ ArgumentNullException.ThrowIfNull(language);
var warnings = new List();
//gift vouchers
if (!product.IsGiftVoucher) return warnings;
var result = await _validatorFactory.GetValidator()
- .ValidateAsync(new ShoppingCartGiftVoucherValidatorRecord(customer, product, shoppingCartItem));
+ .ValidateAsync(new ShoppingCartGiftVoucherValidatorRecord(customer, product, shoppingCartItem, language));
if (!result.IsValid)
warnings.AddRange(result.Errors.Select(x => x.ErrorMessage));
@@ -75,12 +79,15 @@ public virtual async Task> GetShoppingCartItemGiftVoucherWarnings(
}
public virtual async Task> GetInventoryProductWarnings(Customer customer, Product product,
- ShoppingCartItem shoppingCartItem)
+ ShoppingCartItem shoppingCartItem, Language language)
{
+ ArgumentNullException.ThrowIfNull(language);
+
var warnings = new List();
var result = await _validatorFactory.GetValidator()
- .ValidateAsync(new ShoppingCartInventoryProductValidatorRecord(customer, product, shoppingCartItem));
+ .ValidateAsync(new ShoppingCartInventoryProductValidatorRecord(customer, product, shoppingCartItem,
+ language));
if (!result.IsValid)
warnings.AddRange(result.Errors.Select(x => x.ErrorMessage));
@@ -88,12 +95,15 @@ public virtual async Task> GetInventoryProductWarnings(Customer cu
}
- public virtual async Task> GetAuctionProductWarning(double bid, Product product, Customer customer)
+ public virtual async Task> GetAuctionProductWarning(double bid, Product product, Customer customer,
+ Language language)
{
+ ArgumentNullException.ThrowIfNull(language);
+
var warnings = new List();
if (product.ProductTypeId != ProductType.Auction) return warnings;
var result = await _validatorFactory.GetValidator()
- .ValidateAsync(new ShoppingCartAuctionValidatorRecord(customer, product, null, bid));
+ .ValidateAsync(new ShoppingCartAuctionValidatorRecord(customer, product, null, bid, language));
if (!result.IsValid)
warnings.AddRange(result.Errors.Select(x => x.ErrorMessage));
@@ -101,15 +111,18 @@ public virtual async Task> GetAuctionProductWarning(double bid, Pr
}
public virtual async Task> GetReservationProductWarnings(Customer customer, Product product,
- ShoppingCartItem shoppingCartItem)
+ ShoppingCartItem shoppingCartItem, Language language)
{
+ ArgumentNullException.ThrowIfNull(language);
+
var warnings = new List();
if (product.ProductTypeId != ProductType.Reservation)
return warnings;
var result = await _validatorFactory.GetValidator()
- .ValidateAsync(new ShoppingCartReservationProductValidatorRecord(customer, product, shoppingCartItem));
+ .ValidateAsync(new ShoppingCartReservationProductValidatorRecord(customer, product, shoppingCartItem,
+ language));
if (!result.IsValid)
warnings.AddRange(result.Errors.Select(x => x.ErrorMessage));
@@ -117,16 +130,18 @@ public virtual async Task> GetReservationProductWarnings(Customer
}
public virtual async Task> GetShoppingCartWarnings(IList shoppingCart,
- IList checkoutAttributes, CalculationContext context, bool validateCheckoutAttributes,
- bool validateAmount)
+ IList checkoutAttributes, CalculationContext context, Language language,
+ bool validateCheckoutAttributes, bool validateAmount)
{
ArgumentNullException.ThrowIfNull(context);
+ ArgumentNullException.ThrowIfNull(language);
var warnings = new List();
checkoutAttributes ??= new List();
var result = await _validatorFactory.GetValidator()
- .ValidateAsync(new ShoppingCartWarningsValidatorRecord(context.Customer, context.Store, shoppingCart));
+ .ValidateAsync(new ShoppingCartWarningsValidatorRecord(context.Customer, context.Store, shoppingCart,
+ language));
if (!result.IsValid)
warnings.AddRange(result.Errors.Select(x => x.ErrorMessage));
@@ -136,7 +151,7 @@ public virtual async Task> GetShoppingCartWarnings(IList().ValidateAsync(
new ShoppingCartCheckoutAttributesValidatorRecord(context.Customer, context.Store,
- shoppingCart, checkoutAttributes));
+ shoppingCart, checkoutAttributes, language));
if (!resultCheckoutAttributes.IsValid)
warnings.AddRange(resultCheckoutAttributes.Errors.Select(x => x.ErrorMessage));
}
@@ -146,13 +161,14 @@ public virtual async Task> GetShoppingCartWarnings(IList().ValidateAsync(
- new ShoppingCartTotalAmountValidatorRecord(shoppingCart, context));
+ new ShoppingCartTotalAmountValidatorRecord(shoppingCart, context, language));
if (!resultCheckoutAttributes.IsValid)
warnings.AddRange(resultCheckoutAttributes.Errors.Select(x => x.ErrorMessage));
}
//event notification
- await _mediator.ShoppingCartWarningsAdd(warnings, shoppingCart, checkoutAttributes, validateCheckoutAttributes);
+ await _mediator.ShoppingCartWarningsAdd(warnings, shoppingCart, checkoutAttributes, validateCheckoutAttributes,
+ language);
return warnings;
}
@@ -160,13 +176,15 @@ public virtual async Task> GetShoppingCartWarnings(IList> CheckCommonWarnings(Customer customer, IList currentCart,
Product product,
ShoppingCartType shoppingCartType, DateTime? rentalStartDate, DateTime? rentalEndDate,
- int quantity, string reservationId)
+ int quantity, string reservationId, Language language)
{
+ ArgumentNullException.ThrowIfNull(language);
+
var warnings = new List();
var result = await _validatorFactory.GetValidator().ValidateAsync(
new ShoppingCartCommonWarningsValidatorRecord(customer, currentCart,
- product, shoppingCartType, rentalStartDate, rentalEndDate, quantity, reservationId));
+ product, shoppingCartType, rentalStartDate, rentalEndDate, quantity, reservationId, language));
if (!result.IsValid)
warnings.AddRange(result.Errors.Select(x => x.ErrorMessage));
@@ -175,55 +193,60 @@ public async Task> CheckCommonWarnings(Customer customer, IList> GetShoppingCartItemWarnings(Customer customer,
ShoppingCartItem shoppingCartItem,
- Product product, ShoppingCartValidatorOptions options)
+ Product product, ShoppingCartValidatorOptions options, Language language)
{
ArgumentNullException.ThrowIfNull(product);
+ ArgumentNullException.ThrowIfNull(language);
var warnings = new List();
//standard properties
if (options.GetStandardWarnings)
- warnings.AddRange(await GetStandardWarnings(customer, product, shoppingCartItem));
+ warnings.AddRange(await GetStandardWarnings(customer, product, shoppingCartItem, language));
//inventory properties
if (options.GetInventoryWarnings)
- warnings.AddRange(await GetInventoryProductWarnings(customer, product, shoppingCartItem));
+ warnings.AddRange(await GetInventoryProductWarnings(customer, product, shoppingCartItem, language));
//selected attributes
if (options.GetAttributesWarnings)
- warnings.AddRange(await GetShoppingCartItemAttributeWarnings(customer, product, shoppingCartItem));
+ warnings.AddRange(
+ await GetShoppingCartItemAttributeWarnings(customer, product, shoppingCartItem, language));
//gift vouchers
if (options.GetGiftVoucherWarnings)
- warnings.AddRange(await GetShoppingCartItemGiftVoucherWarnings(customer, product, shoppingCartItem));
+ warnings.AddRange(
+ await GetShoppingCartItemGiftVoucherWarnings(customer, product, shoppingCartItem, language));
//required products
if (options.GetRequiredProductWarnings)
warnings.AddRange(await GetRequiredProductWarnings(customer, shoppingCartItem, product,
- shoppingCartItem.StoreId));
+ shoppingCartItem.StoreId, language));
//reservation products
if (options.GetReservationWarnings)
- warnings.AddRange(await GetReservationProductWarnings(customer, product, shoppingCartItem));
+ warnings.AddRange(await GetReservationProductWarnings(customer, product, shoppingCartItem, language));
//event notification
- await _mediator.ShoppingCartItemWarningsAdded(warnings, customer, shoppingCartItem, product);
+ await _mediator.ShoppingCartItemWarningsAdded(warnings, customer, shoppingCartItem, product, language);
return warnings;
}
public virtual async Task> GetRequiredProductWarnings(Customer customer,
- ShoppingCartItem shoppingCartItem, Product product, string storeId)
+ ShoppingCartItem shoppingCartItem, Product product, string storeId, Language language)
{
ArgumentNullException.ThrowIfNull(customer);
ArgumentNullException.ThrowIfNull(product);
+ ArgumentNullException.ThrowIfNull(language);
var warnings = new List();
if (!product.RequireOtherProducts) return warnings;
var result = await _validatorFactory.GetValidator()
- .ValidateAsync(new ShoppingCartRequiredProductValidatorRecord(customer, storeId, product, shoppingCartItem));
+ .ValidateAsync(new ShoppingCartRequiredProductValidatorRecord(customer, storeId, product, shoppingCartItem,
+ language));
if (!result.IsValid)
warnings.AddRange(result.Errors.Select(x => x.ErrorMessage));
return warnings;
diff --git a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartAuctionValidator.cs b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartAuctionValidator.cs
index 2543c9b174..85565c7b2e 100644
--- a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartAuctionValidator.cs
+++ b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartAuctionValidator.cs
@@ -2,6 +2,7 @@
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
namespace Grand.Business.Checkout.Validators;
@@ -10,7 +11,8 @@ public record ShoppingCartAuctionValidatorRecord(
Customer Customer,
Product Product,
ShoppingCartItem ShoppingCartItem,
- double Bid);
+ double Bid,
+ Language Language);
public class ShoppingCartAuctionValidator : AbstractValidator
{
@@ -19,13 +21,13 @@ public ShoppingCartAuctionValidator(ITranslationService translationService)
RuleFor(x => x).Custom((value, context) =>
{
if (value.Bid <= value.Product.HighestBid || value.Bid <= value.Product.StartPrice)
- context.AddFailure(translationService.GetResource("ShoppingCart.BidMustBeHigher"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.BidMustBeHigher", value.Language.Id));
if (!value.Product.AvailableEndDateTimeUtc.HasValue)
- context.AddFailure(translationService.GetResource("ShoppingCart.NotAvailable"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.NotAvailable", value.Language.Id));
if (value.Product.AvailableEndDateTimeUtc < DateTime.UtcNow)
- context.AddFailure(translationService.GetResource("ShoppingCart.NotAvailable"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.NotAvailable", value.Language.Id));
});
}
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartCheckoutAttributesValidator.cs b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartCheckoutAttributesValidator.cs
index 8ff102e60a..4b543f98db 100644
--- a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartCheckoutAttributesValidator.cs
+++ b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartCheckoutAttributesValidator.cs
@@ -5,6 +5,7 @@
using Grand.Domain.Catalog;
using Grand.Domain.Common;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.Domain.Stores;
@@ -14,7 +15,8 @@ public record ShoppingCartCheckoutAttributesValidatorRecord(
Customer Customer,
Store Store,
IList ShoppingCarts,
- IList CheckoutAttributes);
+ IList CheckoutAttributes,
+ Language Language);
public class ShoppingCartCheckoutAttributesValidator : AbstractValidator
{
@@ -50,7 +52,7 @@ await checkoutAttributeService.GetAllCheckoutAttributes(CatalogAccess.For(value.
//if not found
if (!found)
context.AddFailure(a2.TextPrompt ??
- string.Format(translationService.GetResource("ShoppingCart.SelectAttribute"),
+ string.Format(translationService.GetResource("ShoppingCart.SelectAttribute", value.Language.Id),
a2.Name));
}
@@ -69,7 +71,7 @@ await checkoutAttributeService.GetAllCheckoutAttributes(CatalogAccess.For(value.
if (ca.ValidationMinLength.Value > enteredTextLength)
context.AddFailure(string.Format(
- translationService.GetResource("ShoppingCart.TextBoxMinimumLength"), ca.Name,
+ translationService.GetResource("ShoppingCart.TextBoxMinimumLength", value.Language.Id), ca.Name,
ca.ValidationMinLength.Value));
}
@@ -84,7 +86,7 @@ await checkoutAttributeService.GetAllCheckoutAttributes(CatalogAccess.For(value.
if (ca.ValidationMaxLength.Value < enteredTextLength)
context.AddFailure(string.Format(
- translationService.GetResource("ShoppingCart.TextBoxMaximumLength"), ca.Name,
+ translationService.GetResource("ShoppingCart.TextBoxMaximumLength", value.Language.Id), ca.Name,
ca.ValidationMaxLength.Value));
}
}
diff --git a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartCommonWarningsValidator.cs b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartCommonWarningsValidator.cs
index 43bd391384..b3d481a60d 100644
--- a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartCommonWarningsValidator.cs
+++ b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartCommonWarningsValidator.cs
@@ -3,6 +3,7 @@
using Grand.Business.Core.Interfaces.Common.Security;
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.Domain.Permissions;
@@ -16,7 +17,8 @@ public record ShoppingCartCommonWarningsValidatorRecord(
DateTime? RentalStartDate,
DateTime? RentalEndDate,
int Quantity,
- string ReservationId);
+ string ReservationId,
+ Language Language);
public class ShoppingCartCommonWarningsValidator : AbstractValidator
{
@@ -32,7 +34,7 @@ public ShoppingCartCommonWarningsValidator(ITranslationService translationServic
{
if (value.ShoppingCarts.Count >= shoppingCartSettings.MaximumShoppingCartItems)
context.AddFailure(string.Format(
- translationService.GetResource("ShoppingCart.MaximumShoppingCartItems"),
+ translationService.GetResource("ShoppingCart.MaximumShoppingCartItems", value.Language.Id),
shoppingCartSettings.MaximumShoppingCartItems));
}
break;
@@ -40,7 +42,7 @@ public ShoppingCartCommonWarningsValidator(ITranslationService translationServic
{
if (value.ShoppingCarts.Count >= shoppingCartSettings.MaximumWishlistItems)
context.AddFailure(string.Format(
- translationService.GetResource("ShoppingCart.MaximumWishlistItems"),
+ translationService.GetResource("ShoppingCart.MaximumWishlistItems", value.Language.Id),
shoppingCartSettings.MaximumWishlistItems));
}
break;
@@ -59,7 +61,7 @@ public ShoppingCartCommonWarningsValidator(ITranslationService translationServic
}
if (value.Quantity <= 0)
- context.AddFailure(translationService.GetResource("ShoppingCart.QuantityShouldPositive"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.QuantityShouldPositive", value.Language.Id));
});
}
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartGiftVoucherValidator.cs b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartGiftVoucherValidator.cs
index a98e725c34..f156b21ae5 100644
--- a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartGiftVoucherValidator.cs
+++ b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartGiftVoucherValidator.cs
@@ -2,6 +2,7 @@
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.SharedKernel.Extensions;
@@ -10,7 +11,8 @@ namespace Grand.Business.Checkout.Validators;
public record ShoppingCartGiftVoucherValidatorRecord(
Customer Customer,
Product Product,
- ShoppingCartItem ShoppingCartItem);
+ ShoppingCartItem ShoppingCartItem,
+ Language Language);
public class ShoppingCartGiftVoucherValidator : AbstractValidator
{
@@ -23,21 +25,21 @@ public ShoppingCartGiftVoucherValidator(ITranslationService translationService)
out var giftVoucherSenderName, out var giftVoucherSenderEmail, out _);
if (string.IsNullOrEmpty(giftVoucherRecipientName))
- context.AddFailure(translationService.GetResource("ShoppingCart.RecipientNameError"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.RecipientNameError", value.Language.Id));
if (value.Product.GiftVoucherTypeId == GiftVoucherType.Virtual)
//validate for virtual gift vouchers only
if (string.IsNullOrEmpty(giftVoucherRecipientEmail) ||
!CommonHelper.IsValidEmail(giftVoucherRecipientEmail))
- context.AddFailure(translationService.GetResource("ShoppingCart.RecipientEmailError"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.RecipientEmailError", value.Language.Id));
if (string.IsNullOrEmpty(giftVoucherSenderName))
- context.AddFailure(translationService.GetResource("ShoppingCart.SenderNameError"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.SenderNameError", value.Language.Id));
if (value.Product.GiftVoucherTypeId != GiftVoucherType.Virtual) return;
//validate for virtual gift vouchers only
if (string.IsNullOrEmpty(giftVoucherSenderEmail) || !CommonHelper.IsValidEmail(giftVoucherSenderEmail))
- context.AddFailure(translationService.GetResource("ShoppingCart.SenderEmailError"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.SenderEmailError", value.Language.Id));
});
}
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartInventoryProductValidator.cs b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartInventoryProductValidator.cs
index 6373cdb789..fe4b13b822 100644
--- a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartInventoryProductValidator.cs
+++ b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartInventoryProductValidator.cs
@@ -3,6 +3,7 @@
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
namespace Grand.Business.Checkout.Validators;
@@ -10,7 +11,8 @@ namespace Grand.Business.Checkout.Validators;
public record ShoppingCartInventoryProductValidatorRecord(
Customer Customer,
Product Product,
- ShoppingCartItem ShoppingCartItem);
+ ShoppingCartItem ShoppingCartItem,
+ Language Language);
public class ShoppingCartInventoryProductValidator : AbstractValidator
{
@@ -23,25 +25,25 @@ public ShoppingCartInventoryProductValidator(ITranslationService translationServ
var hasQtyWarnings = false;
if (value.ShoppingCartItem.Quantity < value.Product.OrderMinimumQuantity)
{
- context.AddFailure(string.Format(translationService.GetResource("ShoppingCart.MinimumQuantity"),
+ context.AddFailure(string.Format(translationService.GetResource("ShoppingCart.MinimumQuantity", value.Language.Id),
value.Product.OrderMinimumQuantity));
hasQtyWarnings = true;
}
if (value.ShoppingCartItem.Quantity > value.Product.OrderMaximumQuantity)
{
- context.AddFailure(string.Format(translationService.GetResource("ShoppingCart.MaximumQuantity"),
+ context.AddFailure(string.Format(translationService.GetResource("ShoppingCart.MaximumQuantity", value.Language.Id),
value.Product.OrderMaximumQuantity));
hasQtyWarnings = true;
}
var allowedQuantities = value.Product.ParseAllowedQuantities();
if (allowedQuantities.Length > 0 && !allowedQuantities.Contains(value.ShoppingCartItem.Quantity))
- context.AddFailure(string.Format(translationService.GetResource("ShoppingCart.AllowedQuantities"),
+ context.AddFailure(string.Format(translationService.GetResource("ShoppingCart.AllowedQuantities", value.Language.Id),
string.Join(", ", allowedQuantities)));
if (shoppingCartSettings.AllowToSelectWarehouse && string.IsNullOrEmpty(value.ShoppingCartItem.WarehouseId))
- context.AddFailure(translationService.GetResource("ShoppingCart.RequiredWarehouse"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.RequiredWarehouse", value.Language.Id));
var warehouseId = !string.IsNullOrEmpty(value.ShoppingCartItem.WarehouseId)
? value.ShoppingCartItem.WarehouseId
@@ -73,9 +75,9 @@ public ShoppingCartInventoryProductValidator(ITranslationService translationServ
stockQuantityService.GetTotalStockQuantity(value.Product, warehouseId: warehouseId);
if (maximumQuantityCanBeAdded < qty)
context.AddFailure(maximumQuantityCanBeAdded <= 0
- ? translationService.GetResource("ShoppingCart.OutOfStock")
+ ? translationService.GetResource("ShoppingCart.OutOfStock", value.Language.Id)
: string.Format(
- translationService.GetResource("ShoppingCart.QuantityExceedsStock"),
+ translationService.GetResource("ShoppingCart.QuantityExceedsStock", value.Language.Id),
maximumQuantityCanBeAdded));
}
}
@@ -93,7 +95,7 @@ public ShoppingCartInventoryProductValidator(ITranslationService translationServ
stockQuantityService.GetTotalStockQuantity(p1, warehouseId: warehouseId);
if (maximumQuantityCanBeAdded < qty)
context.AddFailure(string.Format(
- translationService.GetResource("ShoppingCart.OutOfStock.BundleProduct"),
+ translationService.GetResource("ShoppingCart.OutOfStock.BundleProduct", value.Language.Id),
p1.Name));
}
@@ -110,15 +112,15 @@ public ShoppingCartInventoryProductValidator(ITranslationService translationServ
context.AddFailure(stockQuantity <= 0
? string.Format(
translationService.GetResource(
- "ShoppingCart.OutOfStock.BundleProduct"), p1.Name)
+ "ShoppingCart.OutOfStock.BundleProduct", value.Language.Id), p1.Name)
: string.Format(
translationService.GetResource(
- "ShoppingCart.QuantityExceedsStock.BundleProduct"), p1.Name,
+ "ShoppingCart.QuantityExceedsStock.BundleProduct", value.Language.Id), p1.Name,
stockQuantity));
}
else
{
- context.AddFailure(translationService.GetResource("ShoppingCart.Combination.NotExist"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.Combination.NotExist", value.Language.Id));
}
}
}
@@ -135,14 +137,14 @@ public ShoppingCartInventoryProductValidator(ITranslationService translationServ
warehouseId: warehouseId);
if (!combination.AllowOutOfStockOrders && stockQuantity < value.ShoppingCartItem.Quantity)
context.AddFailure(stockQuantity <= 0
- ? translationService.GetResource("ShoppingCart.OutOfStock")
+ ? translationService.GetResource("ShoppingCart.OutOfStock", value.Language.Id)
: string.Format(
- translationService.GetResource("ShoppingCart.QuantityExceedsStock"),
+ translationService.GetResource("ShoppingCart.QuantityExceedsStock", value.Language.Id),
stockQuantity));
}
else
{
- context.AddFailure(translationService.GetResource("ShoppingCart.Combination.NotExist"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.Combination.NotExist", value.Language.Id));
}
}
break;
diff --git a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartItemAttributeValidator.cs b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartItemAttributeValidator.cs
index 4473ef08f4..bbf8cff53c 100644
--- a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartItemAttributeValidator.cs
+++ b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartItemAttributeValidator.cs
@@ -3,6 +3,7 @@
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.SharedKernel.Extensions;
@@ -12,7 +13,8 @@ public record ShoppingCartItemAttributeValidatorRecord(
Customer Customer,
Product Product,
ShoppingCartItem ShoppingCartItem,
- bool IgnoreNonCombinableAttributes);
+ bool IgnoreNonCombinableAttributes,
+ Language Language);
public class ShoppingCartItemAttributeValidator : AbstractValidator
{
@@ -73,7 +75,7 @@ await productAttributeService.GetProductAttributeById(productAttributeMapping
var attributeName = productAttribute.Name;
var attributeValueName = attributeValue.Name;
warnings.Add(string.Format(
- translationService.GetResource("ShoppingCart.AssociatedAttributeWarning"),
+ translationService.GetResource("ShoppingCart.AssociatedAttributeWarning", value.Language.Id),
attributeName, attributeValueName, associatedProductWarning));
}
}
@@ -157,7 +159,7 @@ where attributeValuesStr.Any(str1 => !string.IsNullOrEmpty(str1.Trim()))
{
var notFoundWarning = !string.IsNullOrEmpty(a2.TextPrompt)
? a2.TextPrompt
- : string.Format(_translationService.GetResource("ShoppingCart.SelectAttribute"), paa.Name);
+ : string.Format(_translationService.GetResource("ShoppingCart.SelectAttribute", value.Language.Id), paa.Name);
warnings.Add(notFoundWarning);
}
@@ -201,7 +203,7 @@ where attributeValuesStr.Any(str1 => !string.IsNullOrEmpty(str1.Trim()))
{
var productAttribute =
await _productAttributeService.GetProductAttributeById(pam.ProductAttributeId);
- warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.TextboxMinimumLength"),
+ warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.TextboxMinimumLength", value.Language.Id),
productAttribute.Name, pam.ValidationMinLength.Value));
}
}
@@ -217,7 +219,7 @@ where attributeValuesStr.Any(str1 => !string.IsNullOrEmpty(str1.Trim()))
if (pam.ValidationMaxLength.Value >= enteredTextLength) continue;
var productAttribute = await _productAttributeService.GetProductAttributeById(pam.ProductAttributeId);
- warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.TextboxMaximumLength"),
+ warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.TextboxMaximumLength", value.Language.Id),
productAttribute.Name, pam.ValidationMaxLength.Value));
}
}
diff --git a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartRequiredProductValidator.cs b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartRequiredProductValidator.cs
index cc455c493b..fdf6a45f79 100644
--- a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartRequiredProductValidator.cs
+++ b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartRequiredProductValidator.cs
@@ -3,6 +3,7 @@
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
namespace Grand.Business.Checkout.Validators;
@@ -11,7 +12,8 @@ public record ShoppingCartRequiredProductValidatorRecord(
Customer Customer,
string StoreId,
Product Product,
- ShoppingCartItem ShoppingCartItem);
+ ShoppingCartItem ShoppingCartItem,
+ Language Language);
public class ShoppingCartRequiredProductValidator : AbstractValidator
{
@@ -37,7 +39,7 @@ public ShoppingCartRequiredProductValidator(ITranslationService translationServi
let alreadyInTheCart = cart.Any(sci => sci.ProductId == rp.Id)
where !alreadyInTheCart
select rp)
- context.AddFailure(string.Format(translationService.GetResource("ShoppingCart.RequiredProductWarning"),
+ context.AddFailure(string.Format(translationService.GetResource("ShoppingCart.RequiredProductWarning", value.Language.Id),
rp.Name));
});
}
diff --git a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartReservationProductValidator.cs b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartReservationProductValidator.cs
index 4169b68dd9..fd918639d1 100644
--- a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartReservationProductValidator.cs
+++ b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartReservationProductValidator.cs
@@ -4,6 +4,7 @@
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
namespace Grand.Business.Checkout.Validators;
@@ -11,7 +12,8 @@ namespace Grand.Business.Checkout.Validators;
public record ShoppingCartReservationProductValidatorRecord(
Customer Customer,
Product Product,
- ShoppingCartItem ShoppingCartItem);
+ ShoppingCartItem ShoppingCartItem,
+ Language Language);
public class ShoppingCartReservationProductValidator : AbstractValidator
{
@@ -25,7 +27,7 @@ public ShoppingCartReservationProductValidator(ITranslationService translationSe
var reservations = await productReservationService.GetCustomerReservationsHelpers(value.Customer.Id);
if (value.Customer.ShoppingCartItems.All(x => x.Id != value.ShoppingCartItem.Id))
if (reservations.Any(x => x.ReservationId == value.ShoppingCartItem.ReservationId))
- context.AddFailure(translationService.GetResource("ShoppingCart.AlreadyReservation"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.AlreadyReservation", value.Language.Id));
}
if (value.ShoppingCartItem.RentalStartDateUtc.HasValue && value.ShoppingCartItem.RentalEndDateUtc.HasValue)
@@ -46,7 +48,7 @@ public ShoppingCartReservationProductValidator(ITranslationService translationSe
if (!canBeBook)
{
context.AddFailure(
- translationService.GetResource("ShoppingCart.Reservation.NoFreeReservationsInThisPeriod"));
+ translationService.GetResource("ShoppingCart.Reservation.NoFreeReservationsInThisPeriod", value.Language.Id));
return;
}
}
@@ -54,7 +56,7 @@ public ShoppingCartReservationProductValidator(ITranslationService translationSe
if (string.IsNullOrEmpty(value.ShoppingCartItem.ReservationId) &&
value.Product.IntervalUnitId != IntervalUnit.Day)
{
- context.AddFailure(translationService.GetResource("ShoppingCart.Reservation.NoReservationFound"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.Reservation.NoReservationFound", value.Language.Id));
return;
}
@@ -64,12 +66,12 @@ public ShoppingCartReservationProductValidator(ITranslationService translationSe
await productReservationService.GetProductReservation(value.ShoppingCartItem.ReservationId);
if (reservation == null)
{
- context.AddFailure(translationService.GetResource("ShoppingCart.Reservation.ReservationDeleted"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.Reservation.ReservationDeleted", value.Language.Id));
}
else
{
if (!string.IsNullOrEmpty(reservation.OrderId))
- context.AddFailure(translationService.GetResource("ShoppingCart.Reservation.AlreadyReserved"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.Reservation.AlreadyReserved", value.Language.Id));
}
}
else
@@ -77,7 +79,7 @@ public ShoppingCartReservationProductValidator(ITranslationService translationSe
if (!(value.ShoppingCartItem.RentalStartDateUtc.HasValue &&
value.ShoppingCartItem.RentalEndDateUtc.HasValue))
{
- context.AddFailure(translationService.GetResource("ShoppingCart.Reservation.ChooseBothDates"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.Reservation.ChooseBothDates", value.Language.Id));
}
else
{
@@ -87,7 +89,7 @@ public ShoppingCartReservationProductValidator(ITranslationService translationSe
value.ShoppingCartItem.RentalEndDateUtc.Value)
context.AddFailure(
translationService.GetResource(
- "ShoppingCart.Reservation.EndDateMustBeLaterThanStartDate"));
+ "ShoppingCart.Reservation.EndDateMustBeLaterThanStartDate", value.Language.Id));
}
else
{
@@ -95,14 +97,14 @@ public ShoppingCartReservationProductValidator(ITranslationService translationSe
value.ShoppingCartItem.RentalEndDateUtc.Value)
context.AddFailure(
translationService.GetResource(
- "ShoppingCart.Reservation.EndDateMustBeLaterThanStartDate"));
+ "ShoppingCart.Reservation.EndDateMustBeLaterThanStartDate", value.Language.Id));
}
if (value.ShoppingCartItem.RentalStartDateUtc.Value < DateTime.UtcNow ||
value.ShoppingCartItem.RentalEndDateUtc.Value < DateTime.UtcNow)
context.AddFailure(
translationService.GetResource(
- "ShoppingCart.Reservation.ReservationDatesMustBeLaterThanToday"));
+ "ShoppingCart.Reservation.ReservationDatesMustBeLaterThanToday", value.Language.Id));
if (value.Customer.ShoppingCartItems.Any(x => x.Id == value.ShoppingCartItem.Id))
{
@@ -111,7 +113,7 @@ await productReservationService.GetCustomerReservationsHelperBySciId(value.Shopp
.Id);
if (!reserved.Any())
context.AddFailure(
- translationService.GetResource("ShoppingCart.Reservation.ReservationNotExists"));
+ translationService.GetResource("ShoppingCart.Reservation.ReservationNotExists", value.Language.Id));
else
foreach (var item in reserved)
{
@@ -120,13 +122,13 @@ await productReservationService.GetCustomerReservationsHelperBySciId(value.Shopp
if (reservation == null)
{
context.AddFailure(
- translationService.GetResource("ShoppingCart.Reservation.ReservationDeleted"));
+ translationService.GetResource("ShoppingCart.Reservation.ReservationDeleted", value.Language.Id));
break;
}
if (string.IsNullOrEmpty(reservation.OrderId)) continue;
context.AddFailure(
- translationService.GetResource("ShoppingCart.Reservation.AlreadyReserved"));
+ translationService.GetResource("ShoppingCart.Reservation.AlreadyReserved", value.Language.Id));
break;
}
}
diff --git a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartStandardValidator.cs b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartStandardValidator.cs
index 89cdc96c9f..5e20d81224 100644
--- a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartStandardValidator.cs
+++ b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartStandardValidator.cs
@@ -3,6 +3,7 @@
using Grand.Business.Core.Interfaces.Common.Security;
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
namespace Grand.Business.Checkout.Validators;
@@ -10,7 +11,8 @@ namespace Grand.Business.Checkout.Validators;
public record ShoppingCartStandardValidatorRecord(
Customer Customer,
Product Product,
- ShoppingCartItem ShoppingCartItem);
+ ShoppingCartItem ShoppingCartItem,
+ Language Language);
public class ShoppingCartStandardValidator : AbstractValidator
{
@@ -19,7 +21,7 @@ public ShoppingCartStandardValidator(ITranslationService translationService, IAc
RuleFor(x => x).Custom((value, context) =>
{
if (!value.Product.Published)
- context.AddFailure(translationService.GetResource("ShoppingCart.ProductUnpublished"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.ProductUnpublished", value.Language.Id));
//we can't add grouped value.Product
if (value.Product.ProductTypeId == ProductType.GroupedProduct)
@@ -27,27 +29,27 @@ public ShoppingCartStandardValidator(ITranslationService translationService, IAc
//ACL
if (!aclService.Authorize(value.Product, value.Customer))
- context.AddFailure(translationService.GetResource("ShoppingCart.ProductUnpublished"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.ProductUnpublished", value.Language.Id));
//Store acl
if (!aclService.Authorize(value.Product, value.ShoppingCartItem.StoreId))
- context.AddFailure(translationService.GetResource("ShoppingCart.ProductUnpublished"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.ProductUnpublished", value.Language.Id));
switch (value.ShoppingCartItem.ShoppingCartTypeId)
{
//disabled "add to cart" button
case ShoppingCartType.ShoppingCart when value.Product.DisableBuyButton:
- context.AddFailure(translationService.GetResource("ShoppingCart.BuyingDisabled"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.BuyingDisabled", value.Language.Id));
break;
//disabled "add to wishlist" button
case ShoppingCartType.Wishlist when value.Product.DisableWishlistButton:
- context.AddFailure(translationService.GetResource("ShoppingCart.WishlistDisabled"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.WishlistDisabled", value.Language.Id));
break;
}
//call for price
if (value.ShoppingCartItem.ShoppingCartTypeId == ShoppingCartType.ShoppingCart &&
- value.Product.CallForPrice) context.AddFailure(translationService.GetResource("Products.CallForPrice"));
+ value.Product.CallForPrice) context.AddFailure(translationService.GetResource("Products.CallForPrice", value.Language.Id));
//customer entered price
if (value.Product.EnteredPrice)
@@ -56,7 +58,7 @@ public ShoppingCartStandardValidator(ITranslationService translationService, IAc
if (shoppingCartItemEnteredPrice < value.Product.MinEnteredPrice ||
shoppingCartItemEnteredPrice > value.Product.MaxEnteredPrice)
context.AddFailure(string.Format(
- translationService.GetResource("ShoppingCart.CustomerEnteredPrice.RangeError"),
+ translationService.GetResource("ShoppingCart.CustomerEnteredPrice.RangeError", value.Language.Id),
value.Product.MinEnteredPrice,
value.Product.MaxEnteredPrice));
}
@@ -70,7 +72,7 @@ public ShoppingCartStandardValidator(ITranslationService translationService, IAc
DateTime.SpecifyKind(value.Product.AvailableStartDateTimeUtc.Value, DateTimeKind.Utc);
if (availableStartDateTime.CompareTo(now) > 0)
{
- context.AddFailure(translationService.GetResource("ShoppingCart.NotAvailable"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.NotAvailable", value.Language.Id));
availableStartDateError = true;
}
}
@@ -82,7 +84,7 @@ public ShoppingCartStandardValidator(ITranslationService translationService, IAc
var availableEndDateTime =
DateTime.SpecifyKind(value.Product.AvailableEndDateTimeUtc.Value, DateTimeKind.Utc);
if (availableEndDateTime.CompareTo(now) < 0)
- context.AddFailure(translationService.GetResource("ShoppingCart.NotAvailable"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.NotAvailable", value.Language.Id));
}
});
}
diff --git a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartTotalAmountValidator.cs b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartTotalAmountValidator.cs
index ae3966a703..18959add4f 100644
--- a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartTotalAmountValidator.cs
+++ b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartTotalAmountValidator.cs
@@ -4,13 +4,15 @@
using Grand.Business.Core.Interfaces.Common.Directory;
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Business.Core.Utilities.Checkout;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
namespace Grand.Business.Checkout.Validators;
public record ShoppingCartTotalAmountValidatorRecord(
IList Cart,
- CalculationContext Context);
+ CalculationContext Context,
+ Language Language);
public class ShoppingCartTotalAmountValidator : AbstractValidator
{
@@ -40,9 +42,9 @@ public ShoppingCartTotalAmountValidator(
if (shoppingCartTotalBase.HasValue && (shoppingCartTotalBase.Value < minOrderAmount ||
shoppingCartTotalBase.Value > maxOrderAmount))
- context.AddFailure(translationService.GetResource("Checkout.MinMaxOrderTotalAmount"));
+ context.AddFailure(translationService.GetResource("Checkout.MinMaxOrderTotalAmount", value.Language.Id));
else if (shoppingCartTotalBase.HasValue && shoppingCartTotalBase.Value < orderSettings.MinOrderTotalAmount)
- context.AddFailure(translationService.GetResource("Checkout.MinMaxOrderTotalAmount"));
+ context.AddFailure(translationService.GetResource("Checkout.MinMaxOrderTotalAmount", value.Language.Id));
//subtotal
var (_, _, subTotalWithoutDiscount, _, _) =
@@ -52,7 +54,7 @@ public ShoppingCartTotalAmountValidator(
var minOrderSubtotalAmount =
await currencyService.ConvertFromPrimaryStoreCurrency(orderSettings.MinOrderSubtotalAmount,
value.Context.Currency);
- context.AddFailure(string.Format(translationService.GetResource("Checkout.MinOrderSubtotalAmount"),
+ context.AddFailure(string.Format(translationService.GetResource("Checkout.MinOrderSubtotalAmount", value.Language.Id),
priceFormatter.FormatPrice(minOrderSubtotalAmount, value.Context.Currency)));
}
});
diff --git a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartWarningsValidator.cs b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartWarningsValidator.cs
index 78e747b0d8..738f8f716d 100644
--- a/src/Business/Grand.Business.Checkout/Validators/ShoppingCartWarningsValidator.cs
+++ b/src/Business/Grand.Business.Checkout/Validators/ShoppingCartWarningsValidator.cs
@@ -3,6 +3,7 @@
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.Domain.Stores;
@@ -11,7 +12,8 @@ namespace Grand.Business.Checkout.Validators;
public record ShoppingCartWarningsValidatorRecord(
Customer Customer,
Store Store,
- IList ShoppingCarts);
+ IList ShoppingCarts,
+ Language Language);
public class ShoppingCartWarningsValidator : AbstractValidator
{
@@ -31,7 +33,7 @@ public ShoppingCartWarningsValidator(ITranslationService translationService, IPr
var product = await productService.GetProductById(sci.ProductId);
if (product == null)
{
- context.AddFailure(string.Format(translationService.GetResource("ShoppingCart.CannotLoadProduct"),
+ context.AddFailure(string.Format(translationService.GetResource("ShoppingCart.CannotLoadProduct", value.Language.Id),
sci.ProductId));
return;
}
@@ -56,11 +58,11 @@ public ShoppingCartWarningsValidator(ITranslationService translationService, IPr
//don't mix standard and recurring products
if (hasStandardProducts && hasRecurringProducts)
- context.AddFailure(translationService.GetResource("ShoppingCart.CannotMixStandardAndAutoshipProducts"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.CannotMixStandardAndAutoshipProducts", value.Language.Id));
//don't mix recurring products
if (hasRecurringProducts && hasRecurringProductsMix)
- context.AddFailure(translationService.GetResource("ShoppingCart.CannotMixRecurringProducts"));
+ context.AddFailure(translationService.GetResource("ShoppingCart.CannotMixRecurringProducts", value.Language.Id));
});
}
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Core/Commands/Checkout/Orders/AddRequiredProductsCommand.cs b/src/Business/Grand.Business.Core/Commands/Checkout/Orders/AddRequiredProductsCommand.cs
index 763c3747e5..c630f70c17 100644
--- a/src/Business/Grand.Business.Core/Commands/Checkout/Orders/AddRequiredProductsCommand.cs
+++ b/src/Business/Grand.Business.Core/Commands/Checkout/Orders/AddRequiredProductsCommand.cs
@@ -1,5 +1,6 @@
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.Mediator;
@@ -11,4 +12,9 @@ public class AddRequiredProductsCommand : IRequest
public ShoppingCartType ShoppingCartType { get; set; }
public Product Product { get; set; }
public string StoreId { get; set; }
+
+ ///
+ /// Language the cart warnings are written in
+ ///
+ public Language Language { get; set; }
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Core/Commands/Checkout/Orders/ReOrderCommand.cs b/src/Business/Grand.Business.Core/Commands/Checkout/Orders/ReOrderCommand.cs
index 87655bb253..e61701ebbc 100644
--- a/src/Business/Grand.Business.Core/Commands/Checkout/Orders/ReOrderCommand.cs
+++ b/src/Business/Grand.Business.Core/Commands/Checkout/Orders/ReOrderCommand.cs
@@ -1,4 +1,5 @@
-using Grand.Domain.Orders;
+using Grand.Domain.Localization;
+using Grand.Domain.Orders;
using Grand.Mediator;
namespace Grand.Business.Core.Commands.Checkout.Orders;
@@ -6,4 +7,9 @@ namespace Grand.Business.Core.Commands.Checkout.Orders;
public class ReOrderCommand : IRequest>
{
public Order Order { get; set; }
+
+ ///
+ /// Language the returned warnings are written in
+ ///
+ public Language Language { get; set; }
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Core/Events/Checkout/ShoppingCart/ShoppingCartItemWarningsEvent.cs b/src/Business/Grand.Business.Core/Events/Checkout/ShoppingCart/ShoppingCartItemWarningsEvent.cs
index a422b61d49..8078d37b48 100644
--- a/src/Business/Grand.Business.Core/Events/Checkout/ShoppingCart/ShoppingCartItemWarningsEvent.cs
+++ b/src/Business/Grand.Business.Core/Events/Checkout/ShoppingCart/ShoppingCartItemWarningsEvent.cs
@@ -1,5 +1,6 @@
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.Mediator;
@@ -8,12 +9,14 @@ namespace Grand.Business.Core.Events.Checkout.ShoppingCart;
public class ShoppingCartItemWarningsEvent : INotification
where C : Customer where S : ShoppingCartItem where P : Product
{
- public ShoppingCartItemWarningsEvent(IList warnings, C customer, S shoppingCartItem, P product)
+ public ShoppingCartItemWarningsEvent(IList warnings, C customer, S shoppingCartItem, P product,
+ Language language)
{
Warnings = warnings;
Customer = customer;
ShoppingCartItem = shoppingCartItem;
Product = product;
+ Language = language;
}
public IList Warnings { get; }
@@ -23,4 +26,9 @@ public ShoppingCartItemWarningsEvent(IList warnings, C customer, S shopp
public S ShoppingCartItem { get; }
public P Product { get; }
+
+ ///
+ /// Language the warnings are written in
+ ///
+ public Language Language { get; }
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Core/Events/Checkout/ShoppingCart/ShoppingCartWarningsEvent.cs b/src/Business/Grand.Business.Core/Events/Checkout/ShoppingCart/ShoppingCartWarningsEvent.cs
index 638dbf5b83..0ee0b55e8a 100644
--- a/src/Business/Grand.Business.Core/Events/Checkout/ShoppingCart/ShoppingCartWarningsEvent.cs
+++ b/src/Business/Grand.Business.Core/Events/Checkout/ShoppingCart/ShoppingCartWarningsEvent.cs
@@ -1,4 +1,5 @@
using Grand.Domain.Common;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.Mediator;
@@ -7,12 +8,13 @@ namespace Grand.Business.Core.Events.Checkout.ShoppingCart;
public class ShoppingCartWarningsEvent : INotification where U : ShoppingCartItem
{
public ShoppingCartWarningsEvent(IList warnings, IList shoppingCartItems,
- IList checkoutAttributes, bool validateCheckoutAttributes)
+ IList checkoutAttributes, bool validateCheckoutAttributes, Language language)
{
Warnings = warnings;
ShoppingCartItems = shoppingCartItems;
CheckoutAttributes = checkoutAttributes;
ValidateCheckoutAttributes = validateCheckoutAttributes;
+ Language = language;
}
public IList Warnings { get; }
@@ -22,4 +24,9 @@ public ShoppingCartWarningsEvent(IList warnings, IList shoppingCartItems,
public IList CheckoutAttributes { get; }
public bool ValidateCheckoutAttributes { get; }
+
+ ///
+ /// Language the warnings are written in
+ ///
+ public Language Language { get; }
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Core/Events/Customers/CustomerLoggedInEvent.cs b/src/Business/Grand.Business.Core/Events/Customers/CustomerLoggedInEvent.cs
index 7b05608c7d..6984219a94 100644
--- a/src/Business/Grand.Business.Core/Events/Customers/CustomerLoggedInEvent.cs
+++ b/src/Business/Grand.Business.Core/Events/Customers/CustomerLoggedInEvent.cs
@@ -1,4 +1,5 @@
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Mediator;
namespace Grand.Business.Core.Events.Customers;
@@ -8,11 +9,12 @@ namespace Grand.Business.Core.Events.Customers;
///
public class CustomerLoggedInEvent : INotification
{
- public CustomerLoggedInEvent(Customer customer, Customer previousCustomer, string storeId)
+ public CustomerLoggedInEvent(Customer customer, Customer previousCustomer, string storeId, Language language)
{
Customer = customer;
PreviousCustomer = previousCustomer;
StoreId = storeId;
+ Language = language;
}
///
@@ -32,4 +34,9 @@ public Customer Customer {
/// Store of the request the customer logged in through
///
public string StoreId { get; }
+
+ ///
+ /// Working language of the request the customer logged in through
+ ///
+ public Language Language { get; }
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Core/Events/Customers/CustomerRegisteredEvent.cs b/src/Business/Grand.Business.Core/Events/Customers/CustomerRegisteredEvent.cs
index 5815ae0e06..9f98eae665 100644
--- a/src/Business/Grand.Business.Core/Events/Customers/CustomerRegisteredEvent.cs
+++ b/src/Business/Grand.Business.Core/Events/Customers/CustomerRegisteredEvent.cs
@@ -1,4 +1,5 @@
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Mediator;
namespace Grand.Business.Core.Events.Customers;
@@ -8,9 +9,10 @@ namespace Grand.Business.Core.Events.Customers;
///
public class CustomerRegisteredEvent : INotification
{
- public CustomerRegisteredEvent(Customer customer)
+ public CustomerRegisteredEvent(Customer customer, Language language)
{
Customer = customer;
+ Language = language;
}
///
@@ -20,4 +22,10 @@ public Customer Customer {
get;
private set;
}
+
+ ///
+ /// Working language of the request the customer registered through; null when the account was registered
+ /// without one (from an access token)
+ ///
+ public Language Language { get; }
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Core/Extensions/LanguageServiceExtensions.cs b/src/Business/Grand.Business.Core/Extensions/LanguageServiceExtensions.cs
new file mode 100644
index 0000000000..d1cb4631f9
--- /dev/null
+++ b/src/Business/Grand.Business.Core/Extensions/LanguageServiceExtensions.cs
@@ -0,0 +1,33 @@
+using Grand.Business.Core.Interfaces.Common.Localization;
+using Grand.Domain.Localization;
+using Grand.Domain.Stores;
+
+namespace Grand.Business.Core.Extensions;
+
+public static class LanguageServiceExtensions
+{
+ ///
+ /// Gets the language to write in when there is no request: the given language while it still exists,
+ /// otherwise the store's default language when the store offers it, otherwise the store's first published
+ /// language, otherwise any language
+ ///
+ /// Language service
+ /// Preferred language identifier; may be empty or point to a deleted language
+ /// Store whose languages are the fallback; null for all stores
+ /// The language, or null when no language exists at all
+ public static async Task GetLanguageOrStoreDefault(this ILanguageService languageService,
+ string languageId, Store store)
+ {
+ if (!string.IsNullOrEmpty(languageId))
+ {
+ var language = await languageService.GetLanguageById(languageId);
+ if (language != null)
+ return language;
+ }
+
+ var storeLanguages = await languageService.GetAllLanguages(storeId: store?.Id ?? "");
+ return storeLanguages.FirstOrDefault(l => l.Id == store?.DefaultLanguageId)
+ ?? storeLanguages.FirstOrDefault()
+ ?? (await languageService.GetAllLanguages(true)).FirstOrDefault();
+ }
+}
diff --git a/src/Business/Grand.Business.Core/Extensions/ShoppingCartEventPublisherExtensions.cs b/src/Business/Grand.Business.Core/Extensions/ShoppingCartEventPublisherExtensions.cs
index 7b5625eaa4..e311ef9a85 100644
--- a/src/Business/Grand.Business.Core/Extensions/ShoppingCartEventPublisherExtensions.cs
+++ b/src/Business/Grand.Business.Core/Extensions/ShoppingCartEventPublisherExtensions.cs
@@ -2,6 +2,7 @@
using Grand.Domain.Catalog;
using Grand.Domain.Common;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.Mediator;
@@ -10,18 +11,19 @@ namespace Grand.Business.Core.Extensions;
public static class ShoppingCartEventPublisherExtensions
{
public static async Task ShoppingCartWarningsAdd(this IMediator eventPublisher, IList warnings,
- IList shoppingCartItems, IList checkoutAttributes, bool validateCheckoutAttributes)
+ IList shoppingCartItems, IList checkoutAttributes, bool validateCheckoutAttributes,
+ Language language)
where U : ShoppingCartItem
{
await eventPublisher.Publish(new ShoppingCartWarningsEvent(warnings, shoppingCartItems,
- checkoutAttributes, validateCheckoutAttributes));
+ checkoutAttributes, validateCheckoutAttributes, language));
}
public static async Task ShoppingCartItemWarningsAdded(this IMediator eventPublisher,
- IList warnings, C customer, S shoppingCartItem, P product)
+ IList warnings, C customer, S shoppingCartItem, P product, Language language)
where C : Customer where S : ShoppingCartItem where P : Product
{
await eventPublisher.Publish(
- new ShoppingCartItemWarningsEvent(warnings, customer, shoppingCartItem, product));
+ new ShoppingCartItemWarningsEvent(warnings, customer, shoppingCartItem, product, language));
}
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Core/Interfaces/Authentication/IExternalAuthenticationService.cs b/src/Business/Grand.Business.Core/Interfaces/Authentication/IExternalAuthenticationService.cs
index d9037a6936..4d76f2e639 100644
--- a/src/Business/Grand.Business.Core/Interfaces/Authentication/IExternalAuthenticationService.cs
+++ b/src/Business/Grand.Business.Core/Interfaces/Authentication/IExternalAuthenticationService.cs
@@ -1,5 +1,6 @@
using Grand.Business.Core.Utilities.Authentication;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Stores;
using Microsoft.AspNetCore.Mvc;
@@ -18,9 +19,10 @@ public interface IExternalAuthenticationService
/// External authentication parameters
/// Customer of the request (a guest when nobody is logged in)
/// Store of the request
+ /// Working language of the request
/// Defines the URL of user return, when successfully authenticated
/// Result of an authentication
- Task Authenticate(ExternalAuthParam parameters, Customer customer, Store store,
+ Task Authenticate(ExternalAuthParam parameters, Customer customer, Store store, Language language,
string returnUrl = null);
#endregion
diff --git a/src/Business/Grand.Business.Core/Interfaces/Authentication/IOidcCustomerResolver.cs b/src/Business/Grand.Business.Core/Interfaces/Authentication/IOidcCustomerResolver.cs
index 38f70af1d8..51a802b891 100644
--- a/src/Business/Grand.Business.Core/Interfaces/Authentication/IOidcCustomerResolver.cs
+++ b/src/Business/Grand.Business.Core/Interfaces/Authentication/IOidcCustomerResolver.cs
@@ -1,5 +1,6 @@
using Grand.Business.Core.Utilities.Authentication;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using System.Security.Claims;
namespace Grand.Business.Core.Interfaces.Authentication;
@@ -23,7 +24,9 @@ public interface IOidcCustomerResolver
/// E-mail of the provider identity
/// The provider verified the e-mail
/// Current store
- Task ResolveForLogin(string sub, string email, bool emailVerified, string storeId);
+ /// Working language of the sign-in request
+ Task ResolveForLogin(string sub, string email, bool emailVerified, string storeId,
+ Language language);
///
/// Stores the link between the provider subject and the customer (one subject per customer, one customer per subject;
diff --git a/src/Business/Grand.Business.Core/Interfaces/Checkout/Orders/IShoppingCartService.cs b/src/Business/Grand.Business.Core/Interfaces/Checkout/Orders/IShoppingCartService.cs
index cd67b2b767..79732af023 100644
--- a/src/Business/Grand.Business.Core/Interfaces/Checkout/Orders/IShoppingCartService.cs
+++ b/src/Business/Grand.Business.Core/Interfaces/Checkout/Orders/IShoppingCartService.cs
@@ -1,6 +1,7 @@
-using Grand.Business.Core.Utilities.Checkout;
+using Grand.Business.Core.Utilities.Checkout;
using Grand.Domain.Common;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
namespace Grand.Business.Core.Interfaces.Checkout.Orders;
@@ -45,6 +46,7 @@ Task FindShoppingCartItem(IList shoppingCart
/// Add a product to shopping cart
///
/// Customer
+ /// Language the warnings are written in
/// Product id
/// ShoppingCartType
/// Store id
@@ -60,8 +62,8 @@ Task FindShoppingCartItem(IList shoppingCart
/// Duration for reservation
/// ShoppingCartValidatorOptions
/// (warnings, shoppingCartItem)
- Task<(IList warnings, ShoppingCartItem shoppingCartItem)> AddToCart(Customer customer, string productId,
- ShoppingCartType shoppingCartType, string storeId, string warehouseId = null,
+ Task<(IList warnings, ShoppingCartItem shoppingCartItem)> AddToCart(Customer customer, Language language,
+ string productId, ShoppingCartType shoppingCartType, string storeId, string warehouseId = null,
IList attributes = null,
double? customerEnteredPrice = null,
DateTime? rentalStartDate = null, DateTime? rentalEndDate = null,
@@ -74,6 +76,7 @@ Task FindShoppingCartItem(IList shoppingCart
/// Updates the shopping cart item
///
/// Customer
+ /// Language the warnings are written in
/// Shopping cart item identifier
/// Warehouse ident
/// Attributes
@@ -85,7 +88,7 @@ Task FindShoppingCartItem(IList shoppingCart
/// Reservation ident
///
/// Warnings
- Task> UpdateShoppingCartItem(Customer customer,
+ Task> UpdateShoppingCartItem(Customer customer, Language language,
string shoppingCartItemId, string warehouseId, IList attributes,
double? customerEnteredPrice = null,
DateTime? rentalStartDate = null, DateTime? rentalEndDate = null,
@@ -110,9 +113,11 @@ Task DeleteShoppingCartItem(Customer customer, ShoppingCartItem shoppingCartItem
/// From customer
/// To customer
/// Store whose selected checkout attributes move along
+ /// Language the items are validated in
///
/// A value indicating whether to coupon codes (discount and gift voucher) should be also
/// re-applied
///
- Task MigrateShoppingCart(Customer fromCustomer, Customer toCustomer, string storeId, bool includeCouponCodes);
+ Task MigrateShoppingCart(Customer fromCustomer, Customer toCustomer, string storeId, Language language,
+ bool includeCouponCodes);
}
\ No newline at end of file
diff --git a/src/Business/Grand.Business.Core/Interfaces/Checkout/Orders/IShoppingCartValidator.cs b/src/Business/Grand.Business.Core/Interfaces/Checkout/Orders/IShoppingCartValidator.cs
index c80fb60fa5..8e6b3df2aa 100644
--- a/src/Business/Grand.Business.Core/Interfaces/Checkout/Orders/IShoppingCartValidator.cs
+++ b/src/Business/Grand.Business.Core/Interfaces/Checkout/Orders/IShoppingCartValidator.cs
@@ -2,6 +2,7 @@
using Grand.Domain.Catalog;
using Grand.Domain.Common;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
namespace Grand.Business.Core.Interfaces.Checkout.Orders;
@@ -14,8 +15,10 @@ public interface IShoppingCartValidator
/// Customer
/// Product
/// Shopping cart item
+ /// Language the warnings are written in
/// Warnings
- Task> GetStandardWarnings(Customer customer, Product product, ShoppingCartItem shoppingCartItem);
+ Task> GetStandardWarnings(Customer customer, Product product, ShoppingCartItem shoppingCartItem,
+ Language language);
///
/// Validates shopping cart item attributes
@@ -23,10 +26,11 @@ public interface IShoppingCartValidator
/// Customer
/// Product
/// Shopping cart item
+ /// Language the warnings are written in
/// A value indicating whether we should ignore non-combinable attributes
/// Warnings
Task> GetShoppingCartItemAttributeWarnings(Customer customer,
- Product product, ShoppingCartItem shoppingCartItem,
+ Product product, ShoppingCartItem shoppingCartItem, Language language,
bool ignoreNonCombinableAttributes = false);
///
@@ -35,9 +39,10 @@ Task> GetShoppingCartItemAttributeWarnings(Customer customer,
/// Customer
/// Product
/// Shopping cart item
+ /// Language the warnings are written in
/// Warnings
Task> GetShoppingCartItemGiftVoucherWarnings(Customer customer,
- Product product, ShoppingCartItem shoppingCartItem);
+ Product product, ShoppingCartItem shoppingCartItem, Language language);
///
/// Validate bid
@@ -45,8 +50,9 @@ Task> GetShoppingCartItemGiftVoucherWarnings(Customer customer,
///
///
///
+ /// Language the warnings are written in
///
- Task> GetAuctionProductWarning(double bid, Product product, Customer customer);
+ Task> GetAuctionProductWarning(double bid, Product product, Customer customer, Language language);
///
/// Validates shopping cart item
@@ -55,9 +61,10 @@ Task> GetShoppingCartItemGiftVoucherWarnings(Customer customer,
/// Shopping cart type
/// Product
/// Options
+ /// Language the warnings are written in
/// Warnings
Task> GetShoppingCartItemWarnings(Customer customer, ShoppingCartItem shoppingCartItem,
- Product product, ShoppingCartValidatorOptions options);
+ Product product, ShoppingCartValidatorOptions options, Language language);
///
/// Validates whether this shopping cart is valid
@@ -65,12 +72,13 @@ Task> GetShoppingCartItemWarnings(Customer customer, ShoppingCartI
/// Shopping cart
/// Checkout attributes
/// Customer, store, currency and tax display the cart is validated for
+ /// Language the warnings are written in
/// A value indicating whether to validate checkout attributes
/// A value indicating whether to validate subtotal/total attributes
/// Warnings
Task> GetShoppingCartWarnings(IList shoppingCart,
- IList checkoutAttributes, CalculationContext context, bool validateCheckoutAttributes,
- bool validateAmount);
+ IList checkoutAttributes, CalculationContext context, Language language,
+ bool validateCheckoutAttributes, bool validateAmount);
///
/// Validates shopping cart item for reservation products
@@ -78,9 +86,10 @@ Task> GetShoppingCartWarnings(IList shoppingCart
/// Customer
/// product
/// ShoppingCartItem
+ /// Language the warnings are written in
/// Warnings
Task> GetReservationProductWarnings(Customer customer, Product product,
- ShoppingCartItem shoppingCartItem);
+ ShoppingCartItem shoppingCartItem, Language language);
///
/// Validates shopping cart item for inventory
@@ -88,9 +97,10 @@ Task> GetReservationProductWarnings(Customer customer, Product pro
/// Customer
/// product
/// ShoppingCartItem
+ /// Language the warnings are written in
/// Warnings
Task> GetInventoryProductWarnings(Customer customer, Product product,
- ShoppingCartItem shoppingCartItem);
+ ShoppingCartItem shoppingCartItem, Language language);
///
/// Validates common problems
@@ -103,10 +113,11 @@ Task> GetInventoryProductWarnings(Customer customer, Product produ
/// rental end date
/// quantity
/// reservation Id
+ /// Language the warnings are written in
/// Warnings
Task> CheckCommonWarnings(Customer customer, IList currentCart, Product product,
ShoppingCartType shoppingCartType, DateTime? rentalStartDate, DateTime? rentalEndDate, int quantity,
- string reservationId);
+ string reservationId, Language language);
///
/// Validates required products (products which require some other products to be added to the cart)
@@ -115,8 +126,9 @@ Task> CheckCommonWarnings(Customer customer, IListShopping cart type
/// Product
/// Store identifier
+ /// Language the warnings are written in
/// Warnings
Task> GetRequiredProductWarnings(Customer customer,
ShoppingCartItem shoppingCartItem, Product product,
- string storeId);
+ string storeId, Language language);
}
\ No newline at end of file
diff --git a/src/Modules/Grand.Module.ScheduledTasks/BackgroundServices/EndAuctionsTask.cs b/src/Modules/Grand.Module.ScheduledTasks/BackgroundServices/EndAuctionsTask.cs
index 82faf4f22c..a94f66d73b 100644
--- a/src/Modules/Grand.Module.ScheduledTasks/BackgroundServices/EndAuctionsTask.cs
+++ b/src/Modules/Grand.Module.ScheduledTasks/BackgroundServices/EndAuctionsTask.cs
@@ -1,5 +1,7 @@
-using Grand.Business.Core.Interfaces.Catalog.Products;
+using Grand.Business.Core.Extensions;
+using Grand.Business.Core.Interfaces.Catalog.Products;
using Grand.Business.Core.Interfaces.Checkout.Orders;
+using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Business.Core.Interfaces.Customers;
using Grand.Business.Core.Interfaces.Messages;
using Grand.Business.Core.Interfaces.System.ScheduleTasks;
@@ -17,6 +19,7 @@ public class EndAuctionsTask : IScheduleTask
{
private readonly IAuctionService _auctionService;
private readonly ICustomerService _customerService;
+ private readonly ILanguageService _languageService;
private readonly LanguageSettings _languageSettings;
private readonly ILogger _logger;
private readonly IMessageProviderService _messageProviderService;
@@ -25,8 +28,9 @@ public class EndAuctionsTask : IScheduleTask
public EndAuctionsTask(IAuctionService auctionService,
IMessageProviderService messageProviderService, LanguageSettings translationService,
IShoppingCartService shoppingCartService,
- ICustomerService customerService, ILogger logger)
+ ICustomerService customerService, ILanguageService languageService, ILogger logger)
{
+ _languageService = languageService;
_auctionService = auctionService;
_messageProviderService = messageProviderService;
_languageSettings = translationService;
@@ -41,6 +45,11 @@ public EndAuctionsTask(IAuctionService auctionService,
public async Task Execute()
{
var auctionsToEnd = await _auctionService.GetAuctionsToEnd();
+ if (!auctionsToEnd.Any()) return;
+
+ //cart warnings only reach the log, so they are written in the admin language
+ var language =
+ await _languageService.GetLanguageOrStoreDefault(_languageSettings.DefaultAdminLanguageId, null);
foreach (var auctionToEnd in auctionsToEnd)
{
var bid = (await _auctionService.GetBidsByProductId(auctionToEnd.Id)).MaxBy(x => x.Amount);
@@ -52,7 +61,15 @@ await _messageProviderService.SendAuctionEndedStoreOwnerMessage(auctionToEnd,
continue;
}
+ if (language == null)
+ {
+ _logger.LogWarning("EndAuctionTask - Product {Name} - no language exists to validate the bid",
+ auctionToEnd.Name);
+ continue;
+ }
+
var warnings = (await _shoppingCartService.AddToCart(await _customerService.GetCustomerById(bid.CustomerId),
+ language,
bid.ProductId, ShoppingCartType.Auctions,
bid.StoreId, bid.WarehouseId, customerEnteredPrice: bid.Amount,
validator: new ShoppingCartValidatorOptions { GetRequiredProductWarnings = false })).warnings;
diff --git a/src/Plugins/Authentication.Facebook/Controllers/FacebookAuthenticationController.cs b/src/Plugins/Authentication.Facebook/Controllers/FacebookAuthenticationController.cs
index 0e72bc1adc..5dec5505b5 100644
--- a/src/Plugins/Authentication.Facebook/Controllers/FacebookAuthenticationController.cs
+++ b/src/Plugins/Authentication.Facebook/Controllers/FacebookAuthenticationController.cs
@@ -77,7 +77,8 @@ public async Task FacebookLoginCallback(string returnUrl)
//authenticate Grand user
return await _externalAuthenticationService.Authenticate(authenticationParameters,
- _contextAccessor.WorkContext.CurrentCustomer, _contextAccessor.StoreContext.CurrentStore, returnUrl);
+ _contextAccessor.WorkContext.CurrentCustomer, _contextAccessor.StoreContext.CurrentStore,
+ _contextAccessor.WorkContext.WorkingLanguage, returnUrl);
}
public IActionResult FacebookSignInFailed(string error_code, string error_message, string state)
diff --git a/src/Plugins/Authentication.Google/Controllers/GoogleAuthenticationController.cs b/src/Plugins/Authentication.Google/Controllers/GoogleAuthenticationController.cs
index 368c4acdff..686bec8886 100644
--- a/src/Plugins/Authentication.Google/Controllers/GoogleAuthenticationController.cs
+++ b/src/Plugins/Authentication.Google/Controllers/GoogleAuthenticationController.cs
@@ -76,7 +76,8 @@ public async Task GoogleLoginCallback(string returnUrl)
//authenticate grand user
return await _externalAuthenticationService.Authenticate(authenticationParameters,
- _contextAccessor.WorkContext.CurrentCustomer, _contextAccessor.StoreContext.CurrentStore, returnUrl);
+ _contextAccessor.WorkContext.CurrentCustomer, _contextAccessor.StoreContext.CurrentStore,
+ _contextAccessor.WorkContext.WorkingLanguage, returnUrl);
}
public IActionResult GoogleSignInFailed(string error_message)
diff --git a/src/Plugins/Authentication.OpenIdConnect/Controllers/OidcLoginController.cs b/src/Plugins/Authentication.OpenIdConnect/Controllers/OidcLoginController.cs
index 216949c58d..d37e88bc30 100644
--- a/src/Plugins/Authentication.OpenIdConnect/Controllers/OidcLoginController.cs
+++ b/src/Plugins/Authentication.OpenIdConnect/Controllers/OidcLoginController.cs
@@ -106,7 +106,7 @@ public async Task Callback(string returnUrl)
returnUrl = LocalOrNull(returnUrl);
var result = await _oidcCustomerResolver.ResolveForLogin(pending.Subject, pending.Email, pending.EmailVerified,
- _contextAccessor.StoreContext.CurrentStore.Id);
+ _contextAccessor.StoreContext.CurrentStore.Id, _contextAccessor.WorkContext.WorkingLanguage);
//a store session is never replaced by another account, and a signed-in visitor does not register; the same
//account signing in again (for example right after "Connect your account") gets a session-backed cookie
@@ -145,7 +145,8 @@ public async Task Callback(string returnUrl)
//the identity provider is the sign-in: its multi-factor policy applies, not the store's two-factor code
await _mediator.Publish(new CustomerLoggedInEvent(result.Customer,
- _contextAccessor.WorkContext.CurrentCustomer, _contextAccessor.StoreContext.CurrentStore.Id));
+ _contextAccessor.WorkContext.CurrentCustomer, _contextAccessor.StoreContext.CurrentStore.Id,
+ _contextAccessor.WorkContext.WorkingLanguage));
await _authenticationService.SignIn(result.Customer, false, sessionId);
return RedirectLocal(returnUrl);
}
diff --git a/src/Tests/Grand.Business.Authentication.Tests/Services/ExternalAuthenticationServiceTests.cs b/src/Tests/Grand.Business.Authentication.Tests/Services/ExternalAuthenticationServiceTests.cs
index 065e400a0f..3a9e786213 100644
--- a/src/Tests/Grand.Business.Authentication.Tests/Services/ExternalAuthenticationServiceTests.cs
+++ b/src/Tests/Grand.Business.Authentication.Tests/Services/ExternalAuthenticationServiceTests.cs
@@ -8,6 +8,7 @@
using Grand.Data;
using Grand.Data.Tests.MongoDb;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Stores;
using Grand.Mediator;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -123,18 +124,20 @@ public void AuthenticationProviderIsAvailable_NullCustomerOrStore_Throws()
}
[TestMethod]
- public async Task Authenticate_AssociatedUserFromGuest_PublishesLoggedInEventWithPassedCustomerAndStore()
+ public async Task Authenticate_AssociatedUserFromGuest_PublishesLoggedInEventWithPassedCustomerStoreAndLanguage()
{
var user = new Customer { Id = "user" };
var guest = new Customer { Id = "guest" };
+ var language = new Language { Id = "lang1" };
_customerServiceMock.Setup(c => c.GetCustomerById("user")).ReturnsAsync(user);
var parameters = new ExternalAuthParam { ProviderSystemName = "ExternalAuthenticationProviderTest", Identifier = "1" };
await _externalAuthenticationService.AssociateCustomer(user, parameters);
- await _externalAuthenticationService.Authenticate(parameters, guest, new Store { Id = "store1" });
+ await _externalAuthenticationService.Authenticate(parameters, guest, new Store { Id = "store1" }, language);
_mediatorMock.Verify(m => m.Publish(It.Is(e =>
- e.Customer == user && e.PreviousCustomer == guest && e.StoreId == "store1"), default), Times.Once);
+ e.Customer == user && e.PreviousCustomer == guest && e.StoreId == "store1" && e.Language == language),
+ default), Times.Once);
_authenticationServiceMock.Verify(a => a.SignIn(user, false), Times.Once);
}
@@ -142,15 +145,17 @@ public async Task Authenticate_AssociatedUserFromGuest_PublishesLoggedInEventWit
public async Task Authenticate_UnknownIdentity_RegistersPassedCustomerInPassedStore()
{
var guest = new Customer { Id = "guest" };
+ var language = new Language { Id = "lang1" };
var parameters = new ExternalAuthParam {
ProviderSystemName = "ExternalAuthenticationProviderTest", Identifier = "new", Email = "new@test.com"
};
- await _externalAuthenticationService.Authenticate(parameters, guest, new Store { Id = "store1" });
+ await _externalAuthenticationService.Authenticate(parameters, guest, new Store { Id = "store1" }, language);
_customerManagerServiceMock.Verify(m => m.RegisterCustomer(It.Is(r =>
r.Customer == guest && r.StoreId == "store1" && r.Email == "new@test.com")), Times.Once);
- _mediatorMock.Verify(m => m.Publish(It.Is(e => e.Customer == guest), default),
+ _mediatorMock.Verify(m => m.Publish(It.Is(e =>
+ e.Customer == guest && e.Language == language), default),
Times.Once);
_authenticationServiceMock.Verify(a => a.SignIn(guest, false), Times.Once);
Assert.AreEqual("guest", _externalAuthenticationRecordRepository.Table.Single().CustomerId);
diff --git a/src/Tests/Grand.Business.Authentication.Tests/Services/OidcCustomerResolverLoginTests.cs b/src/Tests/Grand.Business.Authentication.Tests/Services/OidcCustomerResolverLoginTests.cs
index 8b5e5529bd..f9f0c73dd1 100644
--- a/src/Tests/Grand.Business.Authentication.Tests/Services/OidcCustomerResolverLoginTests.cs
+++ b/src/Tests/Grand.Business.Authentication.Tests/Services/OidcCustomerResolverLoginTests.cs
@@ -11,6 +11,7 @@
using Grand.Data.Tests.MongoDb;
using Grand.Domain.Common;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Permissions;
using Grand.Infrastructure.Configuration;
using Grand.Mediator;
@@ -77,10 +78,11 @@ private List LinksOf(string sub) =>
private const string Store = "store1";
+ private static readonly Language Language = new() { Id = "lang1" };
[TestMethod]
public async Task NoSubject_Refused_InvalidIdentity() =>
- Assert.AreEqual(OidcLoginRefusal.InvalidIdentity, (await Resolver().ResolveForLogin(null, "a@b.c", true, Store)).Refusal);
+ Assert.AreEqual(OidcLoginRefusal.InvalidIdentity, (await Resolver().ResolveForLogin(null, "a@b.c", true, Store, Language)).Refusal);
[TestMethod]
public async Task ProviderChanged_Linked_RefusedNothingLoaded()
@@ -89,7 +91,7 @@ public async Task ProviderChanged_Linked_RefusedNothingLoaded()
await Link("c1");
Assert.AreEqual(OidcLoginRefusal.ProviderChanged,
- (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Refusal);
+ (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Refusal);
_customerService.Verify(x => x.GetCustomerById(It.IsAny()), Times.Never);
}
@@ -109,7 +111,7 @@ public async Task Linked_SignIn_WithoutEmail()
var customer = new Customer { Id = "c1", Active = true };
_customerService.Setup(x => x.GetCustomerById("c1")).ReturnsAsync(customer);
await Link("c1");
- var result = await Resolver().ResolveForLogin("sub-1", null, false, Store);
+ var result = await Resolver().ResolveForLogin("sub-1", null, false, Store, Language);
Assert.AreEqual(OidcLoginOutcome.SignIn, result.Outcome);
Assert.AreSame(customer, result.Customer);
}
@@ -119,19 +121,19 @@ public async Task LinkedInactive_Refused_AccountInactive()
{
_customerService.Setup(x => x.GetCustomerById("c1")).ReturnsAsync(new Customer { Id = "c1", Active = false });
await Link("c1");
- Assert.AreEqual(OidcLoginRefusal.AccountInactive, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Refusal);
+ Assert.AreEqual(OidcLoginRefusal.AccountInactive, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Refusal);
}
[TestMethod]
public async Task NoEmail_Refused_InvalidIdentity() =>
- Assert.AreEqual(OidcLoginRefusal.InvalidIdentity, (await Resolver().ResolveForLogin("sub-1", " ", true, Store)).Refusal);
+ Assert.AreEqual(OidcLoginRefusal.InvalidIdentity, (await Resolver().ResolveForLogin("sub-1", " ", true, Store, Language)).Refusal);
[TestMethod]
public async Task ExistingCustomer_Verified_LinkedAndSignIn()
{
var customer = new Customer { Id = "c1", Active = true, Email = "john@example.com" };
_customerService.Setup(x => x.GetCustomerByEmail("John@Example.com", "")).ReturnsAsync(customer);
- var result = await Resolver().ResolveForLogin("sub-1", "John@Example.com", true, Store);
+ var result = await Resolver().ResolveForLogin("sub-1", "John@Example.com", true, Store, Language);
Assert.AreEqual(OidcLoginOutcome.SignIn, result.Outcome);
var link = await _links.GetOneAsync(x => x.ExternalIdentifier == "sub-1");
Assert.AreEqual("c1", link.CustomerId);
@@ -144,7 +146,7 @@ public async Task ExistingCustomer_Unverified_LinkRequired_NotLinked()
{
var customer = new Customer { Id = "c1", Active = true };
_customerService.Setup(x => x.GetCustomerByEmail(It.IsAny(), It.IsAny())).ReturnsAsync(customer);
- var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", false, Store);
+ var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", false, Store, Language);
Assert.AreEqual(OidcLoginOutcome.LinkRequired, result.Outcome);
Assert.AreSame(customer, result.Customer);
Assert.IsNull(await _links.GetOneAsync(x => x.ExternalIdentifier == "sub-1"));
@@ -156,7 +158,7 @@ public async Task ExistingAdmin_Verified_LinkRequired_NotLinked()
var customer = new Customer { Id = "c1", Active = true };
_customerService.Setup(x => x.GetCustomerByEmail(It.IsAny(), It.IsAny())).ReturnsAsync(customer);
_permissionService.Setup(x => x.Authorize(StandardPermission.ManageAccessAdminPanel, customer)).ReturnsAsync(true);
- Assert.AreEqual(OidcLoginOutcome.LinkRequired, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.LinkRequired, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Outcome);
Assert.IsNull(await _links.GetOneAsync(x => x.ExternalIdentifier == "sub-1"));
}
@@ -167,7 +169,7 @@ public async Task ExistingVendorOrStoreStaff_Verified_LinkRequired(string vendor
{
var customer = new Customer { Id = "c1", Active = true, VendorId = vendorId, StaffStoreId = staffStoreId };
_customerService.Setup(x => x.GetCustomerByEmail(It.IsAny(), It.IsAny())).ReturnsAsync(customer);
- Assert.AreEqual(OidcLoginOutcome.LinkRequired, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.LinkRequired, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Outcome);
Assert.IsNull(await _links.GetOneAsync(x => x.ExternalIdentifier == "sub-1"));
}
@@ -184,7 +186,7 @@ public async Task ExistingTwoFactorCustomer_Verified_LinkRequired_NotLinked()
_customerSettings.TwoFactorAuthenticationEnabled = true;
var customer = TwoFactorCustomer();
_customerService.Setup(x => x.GetCustomerByEmail(It.IsAny(), It.IsAny())).ReturnsAsync(customer);
- var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store);
+ var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language);
Assert.AreEqual(OidcLoginOutcome.LinkRequired, result.Outcome);
Assert.AreSame(customer, result.Customer);
Assert.IsNull(await _links.GetOneAsync(x => x.ExternalIdentifier == "sub-1"));
@@ -195,7 +197,7 @@ public async Task ExistingTwoFactorCustomer_StoreTwoFactorOff_Verified_SignIn()
{
_customerSettings.TwoFactorAuthenticationEnabled = false;
_customerService.Setup(x => x.GetCustomerByEmail(It.IsAny(), It.IsAny())).ReturnsAsync(TwoFactorCustomer());
- Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Outcome);
Assert.IsNotNull(await _links.GetOneAsync(x => x.ExternalIdentifier == "sub-1"));
}
@@ -206,7 +208,7 @@ public async Task LinkedTwoFactorCustomer_SignIn()
var customer = TwoFactorCustomer();
_customerService.Setup(x => x.GetCustomerById("c1")).ReturnsAsync(customer);
await Link("c1");
- Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Outcome);
}
[TestMethod]
@@ -214,7 +216,7 @@ public async Task LinkMadeByEmail_PlainCustomer_Verified_SignIn()
{
_customerService.Setup(x => x.GetCustomerById("c1")).ReturnsAsync(new Customer { Id = "c1", Active = true });
await Link("c1", byEmail: true);
- Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Outcome);
}
[TestMethod]
@@ -224,7 +226,7 @@ public async Task LinkMadeByEmail_Unverified_LinkRequired()
var customer = new Customer { Id = "c1", Active = true };
_customerService.Setup(x => x.GetCustomerById("c1")).ReturnsAsync(customer);
await Link("c1", byEmail: true);
- var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", false, Store);
+ var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", false, Store, Language);
Assert.AreEqual(OidcLoginOutcome.LinkRequired, result.Outcome);
Assert.AreSame(customer, result.Customer);
}
@@ -235,7 +237,7 @@ public async Task LinkMadeByEmail_TwoFactorCustomer_LinkRequired()
_customerSettings.TwoFactorAuthenticationEnabled = true;
_customerService.Setup(x => x.GetCustomerById("c1")).ReturnsAsync(TwoFactorCustomer());
await Link("c1", byEmail: true);
- Assert.AreEqual(OidcLoginOutcome.LinkRequired, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.LinkRequired, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Outcome);
}
[TestMethod]
@@ -250,7 +252,7 @@ public async Task LinkMadeByEmail_BackOfficeAccount_LinkRequired(string vendorId
if (vendorId == null && staffStoreId == null)
_permissionService.Setup(x => x.Authorize(StandardPermission.ManageAccessAdminPanel, customer)).ReturnsAsync(true);
await Link("c1", byEmail: true);
- Assert.AreEqual(OidcLoginOutcome.LinkRequired, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.LinkRequired, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Outcome);
}
[TestMethod]
@@ -260,7 +262,7 @@ public async Task LinkedAdmin_SignIn()
_customerService.Setup(x => x.GetCustomerById("c1")).ReturnsAsync(customer);
_permissionService.Setup(x => x.Authorize(StandardPermission.ManageAccessAdminPanel, customer)).ReturnsAsync(true);
await Link("c1");
- Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Outcome);
}
[TestMethod]
@@ -269,7 +271,7 @@ public async Task ProviderOnly_Staff_LinkedByVerifiedEmail()
_loginConfig.SignInMode = OidcSignInMode.ProviderOnly;
var staff = new Customer { Id = "c1", Active = true, StaffStoreId = "s1" };
_customerService.Setup(x => x.GetCustomerByEmail(It.IsAny(), It.IsAny())).ReturnsAsync(staff);
- var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store);
+ var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language);
Assert.AreEqual(OidcLoginOutcome.SignIn, result.Outcome);
Assert.AreEqual("c1", result.Customer.Id);
Assert.IsTrue((await _links.GetOneAsync(x => x.ExternalIdentifier == "sub-1")).LinkedByEmail);
@@ -281,7 +283,7 @@ public async Task ProviderOnly_TwoFactor_LinkedByVerifiedEmail()
_loginConfig.SignInMode = OidcSignInMode.ProviderOnly;
_customerSettings.TwoFactorAuthenticationEnabled = true;
_customerService.Setup(x => x.GetCustomerByEmail(It.IsAny(), It.IsAny())).ReturnsAsync(TwoFactorCustomer());
- var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store);
+ var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language);
Assert.AreEqual(OidcLoginOutcome.SignIn, result.Outcome);
Assert.IsNotNull(await _links.GetOneAsync(x => x.ExternalIdentifier == "sub-1"));
}
@@ -292,7 +294,7 @@ public async Task ProviderOnly_UnverifiedEmail_ExistingAccount_Refused()
_loginConfig.SignInMode = OidcSignInMode.ProviderOnly;
_customerService.Setup(x => x.GetCustomerByEmail(It.IsAny(), It.IsAny()))
.ReturnsAsync(new Customer { Id = "c1", Active = true });
- var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", false, Store);
+ var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", false, Store, Language);
Assert.AreEqual(OidcLoginRefusal.EmailNotVerified, result.Refusal);
Assert.IsNull(await _links.GetOneAsync(x => x.ExternalIdentifier == "sub-1"));
}
@@ -301,7 +303,7 @@ public async Task ProviderOnly_UnverifiedEmail_ExistingAccount_Refused()
public async Task ProviderOnly_UnverifiedEmail_Unknown_RefusedNotRegistered()
{
_loginConfig.SignInMode = OidcSignInMode.ProviderOnly;
- var result = await Resolver().ResolveForLogin("sub-new", "new@shop.com", false, Store);
+ var result = await Resolver().ResolveForLogin("sub-new", "new@shop.com", false, Store, Language);
Assert.AreEqual(OidcLoginOutcome.Refused, result.Outcome);
Assert.AreEqual(OidcLoginRefusal.EmailNotVerified, result.Refusal);
}
@@ -311,7 +313,7 @@ public async Task ProviderOnly_VerifiedEmail_Unknown_RegistrationRequired()
{
_loginConfig.SignInMode = OidcSignInMode.ProviderOnly;
Assert.AreEqual(OidcLoginOutcome.RegistrationRequired,
- (await Resolver().ResolveForLogin("sub-new", "new@shop.com", true, Store)).Outcome);
+ (await Resolver().ResolveForLogin("sub-new", "new@shop.com", true, Store, Language)).Outcome);
}
[TestMethod]
@@ -324,7 +326,7 @@ public async Task ProviderOnly_AutoProvision_Unknown_AccountCreatedLinkedAndSign
_loginConfig.AutoProvision = true;
_customerSettings.UserRegistrationType = type;
- var result = await Resolver().ResolveForLogin("sub-new", "New@Shop.com", true, Store);
+ var result = await Resolver().ResolveForLogin("sub-new", "New@Shop.com", true, Store, Language);
Assert.AreEqual(OidcLoginOutcome.SignIn, result.Outcome);
Assert.AreEqual("new1", result.Customer.Id);
@@ -334,7 +336,9 @@ public async Task ProviderOnly_AutoProvision_Unknown_AccountCreatedLinkedAndSign
var link = LinksOf("sub-new").Single();
Assert.AreEqual("new1", link.CustomerId);
Assert.IsFalse(link.LinkedByEmail);
- _mediator.Verify(x => x.Publish(It.IsAny(), It.IsAny()), Times.Once);
+ //the event carries the language of the sign-in request
+ _mediator.Verify(x => x.Publish(It.Is(e => e.Language == Language),
+ It.IsAny()), Times.Once);
}
[TestMethod]
@@ -344,7 +348,7 @@ public async Task ProviderOnly_AutoProvision_UnverifiedEmail_RefusedNothingCreat
_loginConfig.AutoProvision = true;
Assert.AreEqual(OidcLoginRefusal.EmailNotVerified,
- (await Resolver().ResolveForLogin("sub-new", "new@shop.com", false, Store)).Refusal);
+ (await Resolver().ResolveForLogin("sub-new", "new@shop.com", false, Store, Language)).Refusal);
_customerService.Verify(x => x.InsertGuestCustomer(It.IsAny()), Times.Never);
}
@@ -357,7 +361,7 @@ public async Task ProviderOnly_AutoProvision_UserNameTaken_RefusedNothingCreated
.ReturnsAsync(new Customer { Id = "other" });
Assert.AreEqual(OidcLoginRefusal.AccountUnusable,
- (await Resolver().ResolveForLogin("sub-new", "new@shop.com", true, Store)).Refusal);
+ (await Resolver().ResolveForLogin("sub-new", "new@shop.com", true, Store, Language)).Refusal);
_customerService.Verify(x => x.InsertGuestCustomer(It.IsAny()), Times.Never);
}
@@ -367,7 +371,7 @@ public async Task Mixed_AutoProvisionIgnored_RegistrationRequired()
_loginConfig.AutoProvision = true;
Assert.AreEqual(OidcLoginOutcome.RegistrationRequired,
- (await Resolver().ResolveForLogin("sub-new", "new@shop.com", true, Store)).Outcome);
+ (await Resolver().ResolveForLogin("sub-new", "new@shop.com", true, Store, Language)).Outcome);
_customerService.Verify(x => x.InsertGuestCustomer(It.IsAny()), Times.Never);
}
@@ -378,7 +382,7 @@ public async Task ProviderOnly_LinkMadeByEmail_StaffSignsIn()
_customerService.Setup(x => x.GetCustomerById("c1"))
.ReturnsAsync(new Customer { Id = "c1", Active = true, StaffStoreId = "s1" });
await Link("c1", byEmail: true);
- Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Outcome);
}
[TestMethod]
@@ -388,7 +392,7 @@ public async Task ProviderOnly_LinkMadeByEmail_Unverified_SignsIn()
_loginConfig.SignInMode = OidcSignInMode.ProviderOnly;
_customerService.Setup(x => x.GetCustomerById("c1")).ReturnsAsync(new Customer { Id = "c1", Active = true });
await Link("c1", byEmail: true);
- Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", false, Store)).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", false, Store, Language)).Outcome);
}
[TestMethod]
@@ -398,7 +402,7 @@ public async Task ProviderOnly_ExistingAccount_LinkedToOtherSubject_Refused()
_customerService.Setup(x => x.GetCustomerByEmail(It.IsAny(), It.IsAny()))
.ReturnsAsync(new Customer { Id = "c1", Active = true });
await Link("c1", "other-sub");
- Assert.AreEqual(OidcLoginRefusal.LinkedToOtherIdentity, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Refusal);
+ Assert.AreEqual(OidcLoginRefusal.LinkedToOtherIdentity, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Refusal);
}
[TestMethod]
@@ -406,7 +410,7 @@ public async Task Mixed_Staff_LinkRequired()
{
var staff = new Customer { Id = "c1", Active = true, StaffStoreId = "s1" };
_customerService.Setup(x => x.GetCustomerByEmail(It.IsAny(), It.IsAny())).ReturnsAsync(staff);
- Assert.AreEqual(OidcLoginOutcome.LinkRequired, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.LinkRequired, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Outcome);
}
[TestMethod]
@@ -415,7 +419,7 @@ public async Task ExistingCustomerLinkedToOtherSubject_Refused()
_customerService.Setup(x => x.GetCustomerByEmail(It.IsAny(), It.IsAny()))
.ReturnsAsync(new Customer { Id = "c1", Active = true });
await Link("c1", "other-sub");
- Assert.AreEqual(OidcLoginRefusal.LinkedToOtherIdentity, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Refusal);
+ Assert.AreEqual(OidcLoginRefusal.LinkedToOtherIdentity, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Refusal);
}
[TestMethod]
@@ -424,14 +428,14 @@ public async Task ExistingGuestOrSystem_Refused_AccountUnusable()
_customerService.Setup(x => x.GetCustomerByEmail(It.IsAny(), It.IsAny()))
.ReturnsAsync(new Customer { Id = "c1", Active = true });
_groupService.Setup(x => x.IsRegistered(It.IsAny())).ReturnsAsync(false);
- Assert.AreEqual(OidcLoginRefusal.AccountUnusable, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Refusal);
+ Assert.AreEqual(OidcLoginRefusal.AccountUnusable, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Refusal);
}
[TestMethod]
public async Task PerStore_EmailLookupScopedToStore()
{
_customerConfig.RegisterCustomersPerStore = true;
- await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store);
+ await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language);
_customerService.Verify(x => x.GetCustomerByEmail("a@b.c", Store), Times.Once);
}
@@ -439,7 +443,7 @@ public async Task PerStore_EmailLookupScopedToStore()
public async Task NoCustomer_RegistrationRequired_NothingCreated_EvenWithAutoRegister()
{
_config.AutoRegister = true;
- Assert.AreEqual(OidcLoginOutcome.RegistrationRequired, (await Resolver().ResolveForLogin("sub-1", "a@b.c", false, Store)).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.RegistrationRequired, (await Resolver().ResolveForLogin("sub-1", "a@b.c", false, Store, Language)).Outcome);
_customerService.Verify(x => x.InsertGuestCustomer(It.IsAny()), Times.Never);
}
@@ -447,7 +451,7 @@ public async Task NoCustomer_RegistrationRequired_NothingCreated_EvenWithAutoReg
public async Task NoCustomer_RegistrationDisabled_Refused()
{
_customerSettings.UserRegistrationType = UserRegistrationType.Disabled;
- Assert.AreEqual(OidcLoginRefusal.RegistrationDisabled, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Refusal);
+ Assert.AreEqual(OidcLoginRefusal.RegistrationDisabled, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Refusal);
}
[TestMethod]
@@ -519,7 +523,7 @@ public async Task ResolveForLogin_PerStore_SubjectLinkedInAnotherStore_SignsInTh
var customer = new Customer { Id = "c1", Active = true, StoreId = Store };
_customerService.Setup(x => x.GetCustomerByEmail("a@b.c", Store)).ReturnsAsync(customer);
- var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store);
+ var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language);
Assert.AreEqual(OidcLoginOutcome.SignIn, result.Outcome);
Assert.AreEqual("c1", result.Customer.Id);
@@ -534,8 +538,8 @@ public async Task ResolveForLogin_PerStore_GlobalCustomerLink_SignsInInEveryStor
await Link("c1", storeId: "");
_customerService.Setup(x => x.GetCustomerById("c1")).ReturnsAsync(new Customer { Id = "c1", Active = true, StoreId = "" });
- Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store)).Outcome);
- Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, "store2")).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language)).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, "store2", Language)).Outcome);
}
[TestMethod]
@@ -577,12 +581,12 @@ public async Task ResolveForLogin_PerStore_LegacyGlobalLinkToOtherStoreCustomer_
_customerService.Setup(x => x.GetCustomerById("c9")).ReturnsAsync(new Customer { Id = "c9", Active = true, StoreId = "store2" });
_customerService.Setup(x => x.GetCustomerByEmail("a@b.c", Store)).ReturnsAsync(new Customer { Id = "c1", Active = true, StoreId = Store });
- var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store);
+ var result = await Resolver().ResolveForLogin("sub-1", "a@b.c", true, Store, Language);
Assert.AreEqual(OidcLoginOutcome.SignIn, result.Outcome);
Assert.AreEqual("c1", result.Customer.Id);
Assert.AreEqual(Store, LinksOf("sub-1").Single(x => x.CustomerId == "c1").StoreId);
- Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, "store2")).Outcome);
+ Assert.AreEqual(OidcLoginOutcome.SignIn, (await Resolver().ResolveForLogin("sub-1", "a@b.c", true, "store2", Language)).Outcome);
}
[TestMethod]
@@ -647,7 +651,7 @@ public async Task ResolveForLogin_LostRaceToOtherSubjectOfTheCustomer_RefusedLin
_customerService.Setup(x => x.GetCustomerByEmail(It.IsAny(), It.IsAny()))
.ReturnsAsync(new Customer { Id = "c1", Active = true });
- var result = await Resolver(links).ResolveForLogin("sub-1", "a@b.c", true, Store);
+ var result = await Resolver(links).ResolveForLogin("sub-1", "a@b.c", true, Store, Language);
Assert.AreEqual(OidcLoginRefusal.LinkedToOtherIdentity, result.Refusal);
var stored = links.Table.Where(x => x.CustomerId == "c1").ToList();
diff --git a/src/Tests/Grand.Business.Checkout.Tests/Commands/Handlers/Orders/AddRequiredProductsCommandHandlerTests.cs b/src/Tests/Grand.Business.Checkout.Tests/Commands/Handlers/Orders/AddRequiredProductsCommandHandlerTests.cs
index 148483d7b4..6610c123d8 100644
--- a/src/Tests/Grand.Business.Checkout.Tests/Commands/Handlers/Orders/AddRequiredProductsCommandHandlerTests.cs
+++ b/src/Tests/Grand.Business.Checkout.Tests/Commands/Handlers/Orders/AddRequiredProductsCommandHandlerTests.cs
@@ -5,6 +5,7 @@
using Grand.Business.Core.Utilities.Checkout;
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
@@ -42,6 +43,7 @@ public async Task HandleTest()
{ Id = "1", RequireOtherProducts = true, RequiredProductIds = "2,3", AutoAddRequiredProducts = true };
command.ShoppingCartType = ShoppingCartType.ShoppingCart;
command.StoreId = "";
+ command.Language = new Language { Id = "lang1" };
_productServiceMock.Setup(a => a.GetProductById("2", false)).Returns(() =>
Task.FromResult(new Product { Id = "2", Published = true, Price = 10 }));
@@ -53,7 +55,8 @@ public async Task HandleTest()
//Assert
_shoppingCartServiceMock.Verify(
- c => c.AddToCart(It.IsAny(), It.IsAny(), ShoppingCartType.ShoppingCart, "", null, null,
+ c => c.AddToCart(It.IsAny(),
+ command.Language, It.IsAny(), ShoppingCartType.ShoppingCart, "", null, null,
null, null, null, 1, false, "", "", "", It.IsAny()), Times.AtLeastOnce);
}
}
\ No newline at end of file
diff --git a/src/Tests/Grand.Business.Checkout.Tests/Commands/Handlers/Orders/AwardLoyaltyPointsCommandHandlerTests.cs b/src/Tests/Grand.Business.Checkout.Tests/Commands/Handlers/Orders/AwardLoyaltyPointsCommandHandlerTests.cs
index d1055d0dcd..cd38ade64c 100644
--- a/src/Tests/Grand.Business.Checkout.Tests/Commands/Handlers/Orders/AwardLoyaltyPointsCommandHandlerTests.cs
+++ b/src/Tests/Grand.Business.Checkout.Tests/Commands/Handlers/Orders/AwardLoyaltyPointsCommandHandlerTests.cs
@@ -3,9 +3,11 @@
using Grand.Business.Core.Interfaces.Checkout.Orders;
using Grand.Business.Core.Interfaces.Common.Directory;
using Grand.Business.Core.Interfaces.Common.Localization;
+using Grand.Business.Core.Interfaces.Common.Stores;
using Grand.Business.Core.Interfaces.Customers;
using Grand.Domain.Customers;
using Grand.Domain.Directory;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.Mediator;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -22,6 +24,8 @@ public class AwardLoyaltyPointsCommandHandlerTests
private Mock _loyaltyPointsServiceMock;
private Mock _mediatorMock;
private Mock _orderServiceMock;
+ private Mock _languageServiceMock;
+ private Mock _storeServiceMock;
private Mock _translationServiceMock;
@@ -34,10 +38,13 @@ public void Init()
_currencyServiceMock = new Mock();
_orderServiceMock = new Mock();
_translationServiceMock = new Mock();
+ _languageServiceMock = new Mock();
+ _languageServiceMock.Setup(c => c.GetLanguageById("lang1")).ReturnsAsync(new Language { Id = "lang1" });
+ _storeServiceMock = new Mock();
_handler = new AwardLoyaltyPointsCommandHandler(_customerServiceMock.Object, _loyaltyPointsServiceMock.Object,
_mediatorMock.Object, _orderServiceMock.Object, _translationServiceMock.Object,
- _currencyServiceMock.Object);
+ _languageServiceMock.Object, _storeServiceMock.Object, _currencyServiceMock.Object);
}
@@ -46,7 +53,7 @@ public async Task HandleTest()
{
//Arrange
var command = new AwardLoyaltyPointsCommand {
- Order = new Order { StoreId = "", OrderNumber = 1 }
+ Order = new Order { StoreId = "", OrderNumber = 1, CustomerLanguageId = "lang1" }
};
var expectedCustomer = new Customer { Username = "John", Active = true };
_customerServiceMock.Setup(c => c.GetCustomerById(It.IsAny()))
@@ -55,7 +62,9 @@ public async Task HandleTest()
.Returns(() => Task.FromResult(new Currency()));
_currencyServiceMock.Setup(c => c.ConvertToPrimaryStoreCurrency(100, It.IsAny()))
.Returns(() => Task.FromResult((double)100));
- _translationServiceMock.Setup(x => x.GetResource(It.IsAny())).Returns("Name");
+ //the message is written in the language the order was placed in
+ _translationServiceMock.Setup(x => x.GetResource(It.IsAny(), "lang1", It.IsAny(),
+ It.IsAny())).Returns("Name");
_mediatorMock.Setup(x => x.Send(It.IsAny(), default))
.Returns(Task.FromResult(100));
@@ -64,7 +73,7 @@ public async Task HandleTest()
//Assert
_loyaltyPointsServiceMock.Verify(
- c => c.AddLoyaltyPointsHistory(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(),
+ c => c.AddLoyaltyPointsHistory(It.IsAny(), It.IsAny(), It.IsAny(), "Name",
It.IsAny(), It.IsAny()), Times.Once);
}
}
\ No newline at end of file
diff --git a/src/Tests/Grand.Business.Checkout.Tests/Commands/Handlers/Orders/ReduceLoyaltyPointsCommandHandlerTests.cs b/src/Tests/Grand.Business.Checkout.Tests/Commands/Handlers/Orders/ReduceLoyaltyPointsCommandHandlerTests.cs
index ccc1885de0..fdc6636b85 100644
--- a/src/Tests/Grand.Business.Checkout.Tests/Commands/Handlers/Orders/ReduceLoyaltyPointsCommandHandlerTests.cs
+++ b/src/Tests/Grand.Business.Checkout.Tests/Commands/Handlers/Orders/ReduceLoyaltyPointsCommandHandlerTests.cs
@@ -3,9 +3,11 @@
using Grand.Business.Core.Interfaces.Checkout.Orders;
using Grand.Business.Core.Interfaces.Common.Directory;
using Grand.Business.Core.Interfaces.Common.Localization;
+using Grand.Business.Core.Interfaces.Common.Stores;
using Grand.Business.Core.Interfaces.Customers;
using Grand.Domain.Customers;
using Grand.Domain.Directory;
+using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.Mediator;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -22,6 +24,8 @@ public class ReduceLoyaltyPointsCommandHandlerTests
private Mock _loyaltyPointsServiceMock;
private Mock _mediatorMock;
private Mock _orderServiceMock;
+ private Mock _languageServiceMock;
+ private Mock _storeServiceMock;
private Mock _translationServiceMock;
@@ -34,10 +38,13 @@ public void Init()
_currencyServiceMock = new Mock();
_orderServiceMock = new Mock();
_translationServiceMock = new Mock();
+ _languageServiceMock = new Mock();
+ _languageServiceMock.Setup(c => c.GetLanguageById("lang1")).ReturnsAsync(new Language { Id = "lang1" });
+ _storeServiceMock = new Mock();
_handler = new ReduceLoyaltyPointsCommandHandler(_customerServiceMock.Object, _loyaltyPointsServiceMock.Object,
_mediatorMock.Object, _orderServiceMock.Object, _translationServiceMock.Object,
- _currencyServiceMock.Object);
+ _languageServiceMock.Object, _storeServiceMock.Object, _currencyServiceMock.Object);
}
@@ -46,7 +53,7 @@ public async Task HandleTest()
{
//Arrange
var command = new ReduceLoyaltyPointsCommand {
- Order = new Order { StoreId = "", OrderNumber = 1, CalcLoyaltyPoints = 100 }
+ Order = new Order { StoreId = "", OrderNumber = 1, CalcLoyaltyPoints = 100, CustomerLanguageId = "lang1" }
};
var expectedCustomer = new Customer { Username = "John", Active = true };
_customerServiceMock.Setup(c => c.GetCustomerById(It.IsAny()))
@@ -55,7 +62,9 @@ public async Task HandleTest()
.Returns(() => Task.FromResult(new Currency()));
_currencyServiceMock.Setup(c => c.ConvertToPrimaryStoreCurrency(100, It.IsAny()))
.Returns(() => Task.FromResult((double)100));
- _translationServiceMock.Setup(x => x.GetResource(It.IsAny