Skip to content

Commit

Permalink
#5699 Fixed problem with accounting product qty in bundles on stock
Browse files Browse the repository at this point in the history
  • Loading branch information
sergey.s authored and seraishi committed Jun 7, 2021
1 parent 614450a commit f96dd32
Showing 1 changed file with 104 additions and 55 deletions.
159 changes: 104 additions & 55 deletions src/Libraries/Nop.Services/Orders/ShoppingCartService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -410,19 +410,8 @@ protected virtual bool IsCustomerShoppingCartEmpty(Customer customer)
if (product.BackorderMode == BackorderMode.NoBackorders)
{
var maximumQuantityCanBeAdded = await _productService.GetTotalStockQuantityAsync(product);
if (maximumQuantityCanBeAdded < quantity)
{
if (maximumQuantityCanBeAdded <= 0)
{
var productAvailabilityRange = await _dateRangeService.GetProductAvailabilityRangeByIdAsync(product.ProductAvailabilityRangeId);
var warning = productAvailabilityRange == null ? await _localizationService.GetResourceAsync("ShoppingCart.OutOfStock")
: string.Format(await _localizationService.GetResourceAsync("ShoppingCart.AvailabilityRange"),
await _localizationService.GetLocalizedAsync(productAvailabilityRange, range => range.Name));
warnings.Add(warning);
}
else
warnings.Add(string.Format(await _localizationService.GetResourceAsync("ShoppingCart.QuantityExceedsStock"), maximumQuantityCanBeAdded));
}

warnings.AddRange(await GetQuantityProductWarningsAsync(product, quantity, maximumQuantityCanBeAdded));

if (warnings.Any())
return warnings;
Expand All @@ -436,37 +425,50 @@ protected virtual bool IsCustomerShoppingCartEmpty(Customer customer)
{
var cart = await GetShoppingCartAsync(customer, shoppingCartType, storeId);
var totalAddedQuantity = cart
.Where(item => item.ProductId == product.Id)
.Where(item => item.ProductId == product.Id && item.Id != shoppingCartItemId)
.Sum(product => product.Quantity);

var alreadyExistedItem = cart.FirstOrDefault(item => item.Id == shoppingCartItemId);
if (alreadyExistedItem == null)
{
//it's new item
totalAddedQuantity += quantity;
}
else
{
//it's existing item, then add to total the added quantity only
if (quantity > alreadyExistedItem.Quantity)
totalAddedQuantity += quantity - alreadyExistedItem.Quantity;
}
totalAddedQuantity += quantity;

if (maximumQuantityCanBeAdded < totalAddedQuantity)
//counting a product into bundles
foreach (var bundle in cart.Where(x => x.Id != shoppingCartItemId && !string.IsNullOrEmpty(x.AttributesXml)))
{
if (maximumQuantityCanBeAdded <= 0)
var attributeValues = await _productAttributeParser.ParseProductAttributeValuesAsync(bundle.AttributesXml);
foreach (var attributeValue in attributeValues)
{
var productAvailabilityRange = await _dateRangeService.GetProductAvailabilityRangeByIdAsync(product.ProductAvailabilityRangeId);
var warning = productAvailabilityRange == null ? await _localizationService.GetResourceAsync("ShoppingCart.OutOfStock")
: string.Format(await _localizationService.GetResourceAsync("ShoppingCart.AvailabilityRange"),
await _localizationService.GetLocalizedAsync(productAvailabilityRange, range => range.Name));
warnings.Add(warning);
if (attributeValue.AttributeValueType == AttributeValueType.AssociatedToProduct && attributeValue.AssociatedProductId == product.Id)
totalAddedQuantity += bundle.Quantity * attributeValue.Quantity;
}
else
warnings.Add(string.Format(await _localizationService.GetResourceAsync("ShoppingCart.QuantityExceedsStock"), maximumQuantityCanBeAdded));
}

warnings.AddRange(await GetQuantityProductWarningsAsync(product, totalAddedQuantity, maximumQuantityCanBeAdded));
}
}

if (warnings.Any())
return warnings;

//validate product quantity and product quantity into bundles
if (string.IsNullOrEmpty(attributesXml))
{
var cart = await GetShoppingCartAsync(customer, shoppingCartType, storeId);
var totalQuantityInCart = cart.Where(item => item.ProductId == product.Id && item.Id != shoppingCartItemId && string.IsNullOrEmpty(item.AttributesXml))
.Sum(product => product.Quantity);

totalQuantityInCart += quantity;

foreach (var bundle in cart.Where(x => x.Id != shoppingCartItemId && !string.IsNullOrEmpty(x.AttributesXml)))
{
var attributeValues = await _productAttributeParser.ParseProductAttributeValuesAsync(bundle.AttributesXml);
foreach (var attributeValue in attributeValues)
{
if (attributeValue.AttributeValueType == AttributeValueType.AssociatedToProduct && attributeValue.AssociatedProductId == product.Id)
totalQuantityInCart += bundle.Quantity * attributeValue.Quantity;
}
}

warnings.AddRange(await GetQuantityProductWarningsAsync(product, totalQuantityInCart, maximumQuantityCanBeAdded));
}
}

break;
Expand All @@ -476,22 +478,8 @@ protected virtual bool IsCustomerShoppingCartEmpty(Customer customer)
{
//combination exists
//let's check stock level
if (!combination.AllowOutOfStockOrders && combination.StockQuantity < quantity)
{
var maximumQuantityCanBeAdded = combination.StockQuantity;
if (maximumQuantityCanBeAdded <= 0)
{
var productAvailabilityRange = await _dateRangeService.GetProductAvailabilityRangeByIdAsync(product.ProductAvailabilityRangeId);
var warning = productAvailabilityRange == null ? await _localizationService.GetResourceAsync("ShoppingCart.OutOfStock")
: string.Format(await _localizationService.GetResourceAsync("ShoppingCart.AvailabilityRange"),
await _localizationService.GetLocalizedAsync(productAvailabilityRange, range => range.Name));
warnings.Add(warning);
}
else
{
warnings.Add(string.Format(await _localizationService.GetResourceAsync("ShoppingCart.QuantityExceedsStock"), maximumQuantityCanBeAdded));
}
}
if (!combination.AllowOutOfStockOrders)
warnings.AddRange(await GetQuantityProductWarningsAsync(product, quantity, combination.StockQuantity));
}
else
{
Expand Down Expand Up @@ -537,6 +525,40 @@ protected virtual bool IsCustomerShoppingCartEmpty(Customer customer)
return warnings;
}

/// <summary>
/// Validates the maximum quantity a product can be added
/// </summary>
/// <param name="product">Product</param>
/// <param name="quantity">Quantity</param>
/// <param name="maximumQuantityCanBeAdded">The maximum quantity a product can be added</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the warnings
/// </returns>
protected virtual async Task<IList<string>> GetQuantityProductWarningsAsync(Product product, int quantity, int maximumQuantityCanBeAdded)
{
if (product == null)
throw new ArgumentNullException(nameof(product));

var warnings = new List<string>();

if (maximumQuantityCanBeAdded < quantity)
{
if (maximumQuantityCanBeAdded <= 0)
{
var productAvailabilityRange = await _dateRangeService.GetProductAvailabilityRangeByIdAsync(product.ProductAvailabilityRangeId);
var warning = productAvailabilityRange == null ? await _localizationService.GetResourceAsync("ShoppingCart.OutOfStock")
: string.Format(await _localizationService.GetResourceAsync("ShoppingCart.AvailabilityRange"),
await _localizationService.GetLocalizedAsync(productAvailabilityRange, range => range.Name));
warnings.Add(warning);
}
else
warnings.Add(string.Format(await _localizationService.GetResourceAsync("ShoppingCart.QuantityExceedsStock"), maximumQuantityCanBeAdded));
}

return warnings;
}

#endregion

#region Methods
Expand Down Expand Up @@ -720,6 +742,7 @@ public virtual async Task<IList<Product>> GetProductsRequiringProductAsync(IList
}

/// <summary>
/// Temporary solution
/// Validates shopping cart item attributes
/// </summary>
/// <param name="customer">Customer</param>
Expand All @@ -729,17 +752,18 @@ public virtual async Task<IList<Product>> GetProductsRequiringProductAsync(IList
/// <param name="attributesXml">Attributes in XML format</param>
/// <param name="ignoreNonCombinableAttributes">A value indicating whether we should ignore non-combinable attributes</param>
/// <param name="ignoreConditionMet">A value indicating whether we should ignore filtering by "is condition met" property</param>
/// <param name="shoppingCartItemId">Shopping cart identifier; pass 0 if it's a new item</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the warnings
/// </returns>
public virtual async Task<IList<string>> GetShoppingCartItemAttributeWarningsAsync(Customer customer,
protected virtual async Task<IList<string>> GetShoppingCartItemAttributeWarningsAsync(Customer customer,
ShoppingCartType shoppingCartType,
Product product,
int quantity = 1,
string attributesXml = "",
bool ignoreNonCombinableAttributes = false,
bool ignoreConditionMet = false)
bool ignoreConditionMet = false, int shoppingCartItemId = 0)
{
if (product == null)
throw new ArgumentNullException(nameof(product));
Expand Down Expand Up @@ -906,7 +930,7 @@ public virtual async Task<IList<Product>> GetProductsRequiringProductAsync(IList
var totalQty = quantity * attributeValue.Quantity;
var associatedProductWarnings = await GetShoppingCartItemWarningsAsync(customer,
shoppingCartType, associatedProduct, (await _storeContext.GetCurrentStoreAsync()).Id,
string.Empty, decimal.Zero, null, null, totalQty, false);
string.Empty, decimal.Zero, null, null, totalQty, false, shoppingCartItemId);

var productAttribute = await _productAttributeService.GetProductAttributeByIdAsync(productAttributeMapping.ProductAttributeId);

Expand All @@ -928,6 +952,31 @@ public virtual async Task<IList<Product>> GetProductsRequiringProductAsync(IList
return warnings;
}

/// <summary>
/// Validates shopping cart item attributes
/// </summary>
/// <param name="customer">Customer</param>
/// <param name="shoppingCartType">Shopping cart type</param>
/// <param name="product">Product</param>
/// <param name="quantity">Quantity</param>
/// <param name="attributesXml">Attributes in XML format</param>
/// <param name="ignoreNonCombinableAttributes">A value indicating whether we should ignore non-combinable attributes</param>
/// <param name="ignoreConditionMet">A value indicating whether we should ignore filtering by "is condition met" property</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the warnings
/// </returns>
public virtual async Task<IList<string>> GetShoppingCartItemAttributeWarningsAsync(Customer customer,
ShoppingCartType shoppingCartType,
Product product,
int quantity = 1,
string attributesXml = "",
bool ignoreNonCombinableAttributes = false,
bool ignoreConditionMet = false)
{
return await GetShoppingCartItemAttributeWarningsAsync(customer, shoppingCartType, product, quantity, attributesXml, ignoreNonCombinableAttributes, ignoreConditionMet, 0);
}

/// <summary>
/// Validates shopping cart item (gift card)
/// </summary>
Expand Down Expand Up @@ -1076,7 +1125,7 @@ public virtual async Task<IList<Product>> GetProductsRequiringProductAsync(IList

//selected attributes
if (getAttributesWarnings)
warnings.AddRange(await GetShoppingCartItemAttributeWarningsAsync(customer, shoppingCartType, product, quantity, attributesXml));
warnings.AddRange(await GetShoppingCartItemAttributeWarningsAsync(customer, shoppingCartType, product, quantity, attributesXml, false, false, shoppingCartItemId));

//gift cards
if (getGiftCardWarnings)
Expand Down

0 comments on commit f96dd32

Please sign in to comment.