Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2f4c39d
Pass the customer explicitly when listing brands
Oct 16, 2026
9959df8
Pass the customer and store explicitly when listing collections
Oct 16, 2026
ff002d8
Pass the customer and store explicitly to category queries
Oct 16, 2026
ceec7b9
Pass the customer and store explicitly to product mapping queries
Oct 16, 2026
afc911c
Pass the customer and store explicitly to product queries
Oct 16, 2026
32fdc93
Reject recommendation queries without a customer
Oct 16, 2026
3ed5514
Add a calculation context for checkout calculations
Oct 16, 2026
5fe2334
Format order email tax rates in the order currency
Oct 16, 2026
1ce5574
Format prices in an explicitly given currency
Oct 16, 2026
892d865
Pass tax display, customer and store explicitly to tax calculations
Oct 16, 2026
669ce7b
Pass customer, store and currency explicitly to cart item pricing
Oct 16, 2026
d1d5651
Pass a calculation context to the checkout calculations
Oct 16, 2026
10d8f73
Derive recommended products customer groups from the query customer
Oct 16, 2026
6120ba0
Merge branch 'refactor/business-explicit-context-catalog' into refact…
Oct 16, 2026
08983ce
Take customer details from the query customer and stop reading the cu…
Oct 16, 2026
87916a3
Price a customer's cart in the admin panel for that customer
Oct 16, 2026
81c3eed
Round loyalty points amounts in the primary store currency
Oct 16, 2026
6d38be6
Build calculation contexts from the request and drop duplicated state
Oct 16, 2026
b4914b4
Pass a CatalogAccess to the catalog read services
Oct 16, 2026
dc3061a
Merge remote-tracking branch 'origin/refactor/business-explicit-conte…
Oct 16, 2026
51dd676
Merge remote-tracking branch 'origin/develop' into refactor/business-…
Oct 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Grand.Business.Core.Interfaces.Catalog.Prices;
using Grand.Domain.Directory;
using Grand.Infrastructure;
using System.Globalization;

namespace Grand.Business.Catalog.Services.Prices;
Expand All @@ -10,21 +9,6 @@ namespace Grand.Business.Catalog.Services.Prices;
/// </summary>
public class PriceFormatter : IPriceFormatter
{
#region Fields

private readonly IContextAccessor _contextAccessor;

#endregion

#region Constructors

public PriceFormatter(IContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}

#endregion

#region Utilities

/// <summary>
Expand Down Expand Up @@ -66,16 +50,6 @@ protected virtual string GetCurrencyString(double amount, Currency targetCurrenc

#region Methods

/// <summary>
/// Formats the price
/// </summary>
/// <param name="price">Price</param>
/// <returns>Price</returns>
public virtual string FormatPrice(double price)
{
return FormatPrice(price, _contextAccessor.WorkContext.WorkingCurrency);
}

/// <summary>
/// Formats the price
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Grand.Domain.Directory;
using Grand.Domain.Orders;
using Grand.Domain.Stores;
using Grand.Infrastructure;
using Grand.Mediator;
using System.Globalization;

Expand All @@ -25,15 +24,13 @@ public class PricingService : IPricingService
#region Ctor

public PricingService(
IContextAccessor contextAccessor,
IProductService productService,
IMediator mediator,
ICurrencyService currencyService,
IDiscountHandlerService discountApplicationService,
ShoppingCartSettings shoppingCartSettings,
CatalogSettings catalogSettings)
{
_contextAccessor = contextAccessor;
_productService = productService;
_mediator = mediator;
_currencyService = currencyService;
Expand All @@ -58,7 +55,6 @@ protected class ProductPrice

#region Fields

private readonly IContextAccessor _contextAccessor;
private readonly IProductService _productService;
private readonly IMediator _mediator;
private readonly ICurrencyService _currencyService;
Expand Down Expand Up @@ -210,18 +206,21 @@ await _currencyService.ConvertToPrimaryStoreCurrency(price, currency))
/// </summary>
/// <param name="shoppingCartItem">The shopping cart item</param>
/// <param name="product">Product</param>
/// <param name="customer">Customer the price is for</param>
/// <param name="store">Store the price is for</param>
/// <param name="currency">Currency the price is converted to</param>
/// <param name="includeDiscounts">Include discounts or not for price</param>
/// <returns>Shopping cart unit price (one item)</returns>
public virtual async Task<(double unitprice, double discountAmount, List<ApplyDiscount> appliedDiscounts)>
GetUnitPrice(ShoppingCartItem shoppingCartItem,
Product product, bool includeDiscounts = true)
GetUnitPrice(ShoppingCartItem shoppingCartItem, Product product, Customer customer, Store store,
Currency currency, bool includeDiscounts = true)
{
ArgumentNullException.ThrowIfNull(shoppingCartItem);

return await GetUnitPrice(product,
_contextAccessor.WorkContext.CurrentCustomer,
_contextAccessor.StoreContext.CurrentStore,
_contextAccessor.WorkContext.WorkingCurrency,
customer,
store,
currency,
shoppingCartItem.ShoppingCartTypeId,
shoppingCartItem.Quantity,
shoppingCartItem.Attributes,
Expand Down Expand Up @@ -303,7 +302,8 @@ await _currencyService.ConvertFromPrimaryStoreCurrency(combination.OverriddenPri
var attributeValues = product.ParseProductAttributeValues(attributes);
if (attributeValues != null)
foreach (var attributeValue in attributeValues)
attributesTotalPrice += await GetProductAttributeValuePriceAdjustment(attributeValue);
attributesTotalPrice +=
await GetProductAttributeValuePriceAdjustment(attributeValue, customer, store, currency);
if (product.ProductTypeId == ProductType.BundledProduct)
foreach (var item in product.BundleProducts)
{
Expand All @@ -312,7 +312,8 @@ await _currencyService.ConvertFromPrimaryStoreCurrency(combination.OverriddenPri
if (bundledProductsAttributeValues == null) continue;
foreach (var attributeValue in bundledProductsAttributeValues)
attributesTotalPrice += item.Quantity *
await GetProductAttributeValuePriceAdjustment(attributeValue);
await GetProductAttributeValuePriceAdjustment(attributeValue,
customer, store, currency);
}
}

Expand Down Expand Up @@ -355,17 +356,20 @@ await _currencyService.ConvertFromPrimaryStoreCurrency(combination.OverriddenPri
/// </summary>
/// <param name="shoppingCartItem">The shopping cart item</param>
/// <param name="product">Product</param>
/// <param name="customer">Customer the price is for</param>
/// <param name="store">Store the price is for</param>
/// <param name="currency">Currency the price is converted to</param>
/// <param name="includeDiscounts">Include discounts or not for price</param>
/// <returns>Shopping cart item sub total</returns>
public virtual async Task<(double subTotal, double discountAmount, List<ApplyDiscount> appliedDiscounts)>
GetSubTotal(ShoppingCartItem shoppingCartItem, Product product,
bool includeDiscounts = true)
GetSubTotal(ShoppingCartItem shoppingCartItem, Product product, Customer customer, Store store,
Currency currency, bool includeDiscounts = true)
{
ArgumentNullException.ThrowIfNull(shoppingCartItem);

double subTotal = 0;
//unit price
var getUnitPrice = await GetUnitPrice(shoppingCartItem, product, includeDiscounts);
var getUnitPrice = await GetUnitPrice(shoppingCartItem, product, customer, store, currency, includeDiscounts);
var unitPrice = getUnitPrice.unitprice;
var discountAmount = getUnitPrice.discountAmount;
var appliedDiscounts = getUnitPrice.appliedDiscounts;
Expand All @@ -385,7 +389,8 @@ await _currencyService.ConvertFromPrimaryStoreCurrency(combination.OverriddenPri
discountAmount *= discountedQuantity;

var notDiscountedQuantity = shoppingCartItem.Quantity - discountedQuantity;
var notDiscountedUnitPrice = await GetUnitPrice(shoppingCartItem, product, false);
var notDiscountedUnitPrice =
await GetUnitPrice(shoppingCartItem, product, customer, store, currency, false);
var notDiscountedSubTotal = notDiscountedUnitPrice.unitprice * notDiscountedQuantity;

subTotal = discountedSubTotal + notDiscountedSubTotal;
Expand Down Expand Up @@ -445,8 +450,12 @@ public virtual async Task<double> GetProductCost(Product product, IList<CustomAt
/// Get a price adjustment of a product attribute value
/// </summary>
/// <param name="value">Product attribute value</param>
/// <param name="customer">Customer the price is for</param>
/// <param name="store">Store the price is for</param>
/// <param name="currency">Currency the price is converted to</param>
/// <returns>Price adjustment</returns>
public virtual async Task<double> GetProductAttributeValuePriceAdjustment(ProductAttributeValue value)
public virtual async Task<double> GetProductAttributeValuePriceAdjustment(ProductAttributeValue value,
Customer customer, Store store, Currency currency)
{
ArgumentNullException.ThrowIfNull(value);

Expand All @@ -459,8 +468,7 @@ public virtual async Task<double> GetProductAttributeValuePriceAdjustment(Produc
adjustment = value.PriceAdjustment;
if (adjustment > 0)
adjustment =
await _currencyService.ConvertFromPrimaryStoreCurrency(adjustment,
_contextAccessor.WorkContext.WorkingCurrency);
await _currencyService.ConvertFromPrimaryStoreCurrency(adjustment, currency);
}
break;
case AttributeValueType.AssociatedToProduct:
Expand All @@ -469,9 +477,9 @@ await _currencyService.ConvertFromPrimaryStoreCurrency(adjustment,
var associatedProduct = await _productService.GetProductById(value.AssociatedProductId);
if (associatedProduct != null)
adjustment = (await GetFinalPrice(associatedProduct,
_contextAccessor.WorkContext.CurrentCustomer,
_contextAccessor.StoreContext.CurrentStore,
_contextAccessor.WorkContext.WorkingCurrency,
customer,
store,
currency,
value.PriceAdjustment)).finalPrice * value.Quantity;
}
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Grand.Domain.Common;
using Grand.Domain.Customers;
using Grand.Domain.Orders;
using Grand.Domain.Tax;
using Grand.Infrastructure;
using Grand.SharedKernel.Extensions;
using System.Net;
Expand Down Expand Up @@ -85,8 +86,8 @@ public virtual async Task<string> FormatAttributes(Product product, IList<Custom
//attributes
if (renderProductAttributes)
{
result = await PrepareFormattedAttribute(product, customAttributes, langId, separator, htmlEncode,
renderPrices, allowHyperlinks, showInAdmin);
result = await PrepareFormattedAttribute(product, customAttributes, customer, langId, separator,
htmlEncode, renderPrices, allowHyperlinks, showInAdmin);

if (product.ProductTypeId == ProductType.BundledProduct)
{
Expand All @@ -105,8 +106,8 @@ public virtual async Task<string> FormatAttributes(Product product, IList<Custom
else
result.Append(
$"{(htmlEncode ? WebUtility.HtmlEncode(p1.GetTranslation(x => x.Name, langId)) : p1.GetTranslation(x => x.Name, langId))}");
var formattedAttribute = await PrepareFormattedAttribute(p1, customAttributes, langId, separator,
htmlEncode,
var formattedAttribute = await PrepareFormattedAttribute(p1, customAttributes, customer, langId,
separator, htmlEncode,
renderPrices, allowHyperlinks, showInAdmin);
if (formattedAttribute.Length > 0)
{
Expand Down Expand Up @@ -150,10 +151,11 @@ public virtual async Task<string> FormatAttributes(Product product, IList<Custom
}

private async Task<StringBuilder> PrepareFormattedAttribute(Product product,
IList<CustomAttribute> customAttributes, string langId,
IList<CustomAttribute> customAttributes, Customer customer, string langId,
string separator, bool htmlEncode, bool renderPrices,
bool allowHyperlinks, bool showInAdmin)
{
var priceCustomer = customer ?? _contextAccessor.WorkContext.CurrentCustomer;
var result = new StringBuilder();
var attributes = product.ParseProductAttributeMappings(customAttributes);
for (var i = 0; i < attributes.Count; i++)
Expand Down Expand Up @@ -233,9 +235,13 @@ private async Task<StringBuilder> PrepareFormattedAttribute(Product product,
if (renderPrices)
{
var attributeValuePriceAdjustment =
await _pricingService.GetProductAttributeValuePriceAdjustment(attributeValue);
await _pricingService.GetProductAttributeValuePriceAdjustment(attributeValue,
priceCustomer, _contextAccessor.StoreContext.CurrentStore,
_contextAccessor.WorkContext.WorkingCurrency);
var (priceAdjustmentBase, _) = await _taxService.GetProductPrice(product,
attributeValuePriceAdjustment, _contextAccessor.WorkContext.CurrentCustomer, _contextAccessor.StoreContext.CurrentStore);
attributeValuePriceAdjustment,
_contextAccessor.WorkContext.TaxDisplayType == TaxDisplayType.IncludingTax,
priceCustomer, _contextAccessor.StoreContext.CurrentStore);
switch (priceAdjustmentBase)
{
case > 0:
Expand Down
Loading