Skip to content
This repository has been archived by the owner on Jun 13, 2023. It is now read-only.

Commit

Permalink
VP-5616: Extend Shopping cart with Configured Groups
Browse files Browse the repository at this point in the history
Implement the logic of group`s adding , changing, removing. 
Include into the group data the info about the product and configured product parts.
  • Loading branch information
trueboroda committed Oct 27, 2020
1 parent 7dd65ae commit 6b4af0b
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 69 deletions.
5 changes: 5 additions & 0 deletions VirtoCommerce.Storefront.Model/Cart/Demo/AddCartItem.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
using Newtonsoft.Json;

namespace VirtoCommerce.Storefront.Model.Cart
{
partial class AddCartItem
{
public string ConfiguredProductId { get; set; }

[JsonIgnore]
public string ConfiguredGroupId { get; set; }
}
}
10 changes: 7 additions & 3 deletions VirtoCommerce.Storefront.Model/Cart/Demo/ConfiguredGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ public class ConfiguredGroup : Entity
public ConfiguredGroup(int quantity, Currency currency, Money extendedPrice,
Money extendedPriceWithTax, Money taxTotal)
{
Id = Guid.NewGuid().ToString("N");
Quantity = quantity;
Currency = currency;
ExtendedPrice = extendedPrice;
ExtendedPriceWithTax = extendedPriceWithTax;
TaxTotal = taxTotal;
TaxTotal = taxTotal;
}

public string ProductId { get; set; }

public Product Product { get; set; }

public DateTime CreatedDate { get; set; }

Expand Down Expand Up @@ -56,14 +60,14 @@ public ConfiguredGroup(int quantity, Currency currency, Money extendedPrice,
[JsonRequired]
public Money ExtendedPriceWithTax { get; set; }

#endregion
#endregion Pricing

#region Taxation

[JsonRequired]
public Money TaxTotal { get; set; }

#endregion
#endregion Taxation

public ICollection<ProductPart> Parts { get; set; } = new List<ProductPart>();
}
Expand Down
13 changes: 13 additions & 0 deletions VirtoCommerce.Storefront.Model/Cart/Demo/ShoppingCart.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using VirtoCommerce.Storefront.Model.Cart.Demo;

namespace VirtoCommerce.Storefront.Model.Cart
{
public partial class ShoppingCart
{
[JsonRequired]
public LineItem[] UsualItems
{
get
{
var result = Items.Where(x => !ConfiguredGroups.Any(y => y.Items.Contains(x))).ToArray();

return result;
}
}

public ICollection<ConfiguredGroup> ConfiguredGroups { get; set; } = new List<ConfiguredGroup>();
}
}
35 changes: 30 additions & 5 deletions VirtoCommerce.Storefront/Controllers/Api/ApiCartDemoController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using VirtoCommerce.Storefront.Infrastructure;
using VirtoCommerce.Storefront.Model;
using VirtoCommerce.Storefront.Model.Cart;
Expand All @@ -11,15 +12,15 @@
using VirtoCommerce.Storefront.Model.Common.Exceptions;
using VirtoCommerce.Storefront.Model.Services;


namespace VirtoCommerce.Storefront.Controllers.Api
{
[StorefrontApiRoute("cartdemo")]
[ResponseCache(CacheProfileName = "None")]
public class ApiCartDemoController : StorefrontControllerBase
{
private readonly ICartBuilder _cartBuilder;
private readonly ICatalogService _catalogService;
private readonly ICatalogService _catalogService;

public ApiCartDemoController(IWorkContextAccessor workContextAccessor, ICatalogService catalogService, ICartBuilder cartBuilder,
IStorefrontUrlBuilder urlBuilder)
: base(workContextAccessor, urlBuilder)
Expand All @@ -28,24 +29,48 @@ public ApiCartDemoController(IWorkContextAccessor workContextAccessor, ICatalogS
_catalogService = catalogService;
}


// POST: storefrontapi/cart/items/bulk
[HttpPost("items/bulk")]
[ValidateAntiForgeryToken]
public async Task<ActionResult<AddItemsToCartResult>> AddItemsToCart([FromBody] AddCartItem[] items)
{
if (items.IsNullOrEmpty())
{
throw new ArgumentException("Items should not be null or empty");
}

EnsureCartExists();

//Need lock to prevent concurrent access to same cart
using (await AsyncLock.GetLockByKey(WorkContext.CurrentCart.Value.GetCacheKey()).LockAsync())
{
var productIds = items.Select(x => x.Id).ToArray();
var products = await _catalogService.GetProductsAsync(productIds, Model.Catalog.ItemResponseGroup.ItemSmall | Model.Catalog.ItemResponseGroup.ItemWithPrices | Model.Catalog.ItemResponseGroup.Inventory);
var products = await _catalogService.GetProductsAsync(productIds, Model.Catalog.ItemResponseGroup.ItemSmall | Model.Catalog.ItemResponseGroup.ItemWithPrices | Model.Catalog.ItemResponseGroup.Inventory);
var cartBuilder = await LoadOrCreateCartAsync();
var cart = _cartBuilder.Cart;
var currency = WorkContext.CurrentCurrency;

var firstItem = items.First();

var configuredProductId = firstItem.ConfiguredProductId;

var configuredGroup = cart.ConfiguredGroups?.FirstOrDefault(x => (x.ProductId == configuredProductId)
&& x.Items.OrderBy(x => x.ProductId).Select(x => x.ProductId).SequenceEqual(items.OrderBy(i => i.ProductId).Select(i => i.ProductId).ToArray())
);

if (configuredGroup == null)
{
configuredGroup = new Model.Cart.Demo.ConfiguredGroup(firstItem.Quantity, WorkContext.CurrentCurrency, new Money(0m, currency), new Money(0m, currency), new Money(0m, currency));
cart.ConfiguredGroups.Add(configuredGroup);
}
else
{
configuredGroup.Quantity = configuredGroup.Quantity + Math.Max(1, firstItem.Quantity);
}

foreach (var item in items)
{
item.ConfiguredGroupId = configuredGroup?.Id;
item.Product = products.First(x => x.Id == item.ProductId);
await cartBuilder.AddItemAsync(item);
}
Expand Down
2 changes: 1 addition & 1 deletion VirtoCommerce.Storefront/Controllers/CartController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using VirtoCommerce.Storefront.AutoRestClients.OrdersModuleApi;
using VirtoCommerce.Storefront.Infrastructure;
using VirtoCommerce.Storefront.Model;
Expand Down
2 changes: 0 additions & 2 deletions VirtoCommerce.Storefront/Domain/Cart/CartConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -692,8 +692,6 @@ public static LineItem ToLineItem(this cartDto.CartLineItem lineItemDto, Currenc
ConfiguredGropupId = lineItemDto.ConfiguredGroupId,
};



result.ImageUrl = lineItemDto.ImageUrl.RemoveLeadingUriScheme();

if (lineItemDto.TaxDetails != null)
Expand Down
17 changes: 4 additions & 13 deletions VirtoCommerce.Storefront/Domain/Cart/Demo/CartConverter.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using VirtoCommerce.Storefront.Common;
using VirtoCommerce.Storefront.Model;
using VirtoCommerce.Storefront.Model.Cart;
using VirtoCommerce.Storefront.Model.Cart.Demo;
using VirtoCommerce.Storefront.Model.Catalog;
using VirtoCommerce.Storefront.Model.Common;
using VirtoCommerce.Storefront.Model.Marketing;
using VirtoCommerce.Storefront.Model.Security;
using VirtoCommerce.Storefront.Model.Stores;
using VirtoCommerce.Storefront.Model.Tax;
using cartDto = VirtoCommerce.Storefront.AutoRestClients.CartModuleApi.Models;


namespace VirtoCommerce.Storefront.Domain
{

public static partial class CartConverter
{
public static cartDto.DemoCartConfiguredGroup ToConfiguredGroup(this ConfiguredGroup group)
Expand All @@ -29,7 +19,8 @@ public static cartDto.DemoCartConfiguredGroup ToConfiguredGroup(this ConfiguredG
return new cartDto.DemoCartConfiguredGroup
{
Id = group.Id ?? Guid.NewGuid().ToString("N"),
ItemIds = group.Items.Select(x => x.Id).ToList(),
ProductId = group.ProductId,
ItemIds = group.Items.Select(x => x.Id).ToList(),
CreatedBy = group.CreatedBy,
CreatedDate = group.CreatedDate,
ModifiedBy = group.ModifiedBy,
Expand All @@ -43,7 +34,7 @@ public static cartDto.DemoCartConfiguredGroup ToConfiguredGroup(this ConfiguredG
PlacedPriceWithTax = (double)group.PlacedPriceWithTax.InternalAmount,
SalePrice = (double)group.SalePrice.InternalAmount,
SalePriceWithTax = (double)group.SalePriceWithTax.InternalAmount,
TaxTotal = (double)group.TaxTotal.InternalAmount,
TaxTotal = (double)group.TaxTotal.InternalAmount,
Quantity = group.Quantity
};
}
Expand All @@ -56,6 +47,7 @@ public static ConfiguredGroup ToConfiguredGroup(this cartDto.DemoCartConfiguredG
new Money(group.TaxTotal ?? 0, cart.Currency))
{
Id = group.Id,
ProductId = group.ProductId,
CreatedBy = group.CreatedBy,
CreatedDate = group.CreatedDate ?? DateTime.UtcNow,
ModifiedBy = group.ModifiedBy,
Expand All @@ -67,7 +59,6 @@ public static ConfiguredGroup ToConfiguredGroup(this cartDto.DemoCartConfiguredG
PlacedPrice = new Money(group.PlacedPrice ?? 0, cart.Currency),
PlacedPriceWithTax = new Money(group.PlacedPriceWithTax ?? 0, cart.Currency),
Currency = cart.Currency,

};

foreach (var item in group.ItemIds.Select(id => cart.Items.First(x => x.Id == id)))
Expand Down
44 changes: 22 additions & 22 deletions VirtoCommerce.Storefront/Domain/Cart/Demo/DemoCartBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,19 @@ public override Task RemoveItemAsync(string lineItemId)
{
EnsureCartExists();

var configureLineItem = Cart.ConfiguredGroups.FirstOrDefault(x => x.ConfiguredLineItem?.ProductId.Equals(lineItemId, StringComparison.InvariantCulture) ?? false);
var configuredGroup = Cart.ConfiguredGroups?.FirstOrDefault(x => x.Id.Equals(lineItemId, StringComparison.InvariantCulture));

if (configureLineItem != null)
if (configuredGroup != null)
{
var configurablePieces = Cart.Items.Where(x => x.ConfiguredGropupId?.Equals(lineItemId, StringComparison.InvariantCulture) ?? false).ToArray();
var groupItems = Cart.Items.Where(x => !string.IsNullOrEmpty(x.ConfiguredGropupId) &&
x.ConfiguredGropupId.Equals(configuredGroup.Id)).ToArray();

foreach (var configuirablePieceLineItem in configurablePieces)
foreach (var lineItem in groupItems)
{
Cart.Items.Remove(configuirablePieceLineItem);
Cart.Items.Remove(lineItem);
}

Cart.ConfiguredGroups.Remove(configureLineItem);
Cart.ConfiguredGroups.Remove(configuredGroup);
}

return base.RemoveItemAsync(lineItemId);
Expand All @@ -55,18 +56,13 @@ public override Task ChangeItemQuantityAsync(ChangeCartItemQty changeItemQty)
{
EnsureCartExists();

var configuredProduct = Cart.ConfiguredGroups?.FirstOrDefault(x =>
x.ConfiguredLineItem?.ProductId.Equals(changeItemQty.LineItemId, StringComparison.InvariantCulture) ?? false);
var configuredGroup = Cart.ConfiguredGroups?.FirstOrDefault(x => x.Id.Equals(changeItemQty.LineItemId, StringComparison.InvariantCulture));

if (configuredProduct != null)
if (configuredGroup != null)
{
foreach (var lineItem in Cart
.Items
.Where(x =>
!string.IsNullOrEmpty(x.ConfiguredGropupId) &&
x.ConfiguredGropupId.Equals(configuredProduct.ConfiguredLineItem.ProductId)
)
)
var groupItems = Cart.Items.Where(x => !string.IsNullOrEmpty(x.ConfiguredGropupId) &&
x.ConfiguredGropupId.Equals(configuredGroup.Id)).ToArray();
foreach (var lineItem in groupItems)
{
lineItem.Quantity = changeItemQty.Quantity;
}
Expand All @@ -83,9 +79,9 @@ public override async Task<bool> AddItemAsync(AddCartItem addCartItem)

if (result.IsValid)
{
var lineItem = addCartItem.Product.ToLineItem(Cart.Language, addCartItem.Quantity);
var lineItem = addCartItem.Product.ToLineItem(Cart.Language, addCartItem.Quantity);
lineItem.Product = addCartItem.Product;
lineItem.ConfiguredGropupId = addCartItem.ConfiguredProductId;
lineItem.ConfiguredGropupId = addCartItem.ConfiguredGroupId;

if (addCartItem.Price != null)
{
Expand Down Expand Up @@ -116,14 +112,18 @@ public override async Task<bool> AddItemAsync(AddCartItem addCartItem)

protected override async Task AddLineItemAsync(LineItem lineItem)
{
if (!string.IsNullOrEmpty(lineItem.ConfiguredGropupId))
var existingLineItem = Cart.Items.FirstOrDefault(li => li.ProductId == lineItem.ProductId && li.ConfiguredGropupId == lineItem.ConfiguredGropupId);
if (existingLineItem != null)
{
lineItem.Id = null;
Cart.Items.Add(lineItem);
await ChangeItemQuantityAsync(existingLineItem, existingLineItem.Quantity + Math.Max(1, lineItem.Quantity));
await ChangeItemPriceAsync(existingLineItem, new ChangeCartItemPrice() { LineItemId = existingLineItem.Id, NewPrice = lineItem.ListPrice.Amount });
existingLineItem.Comment = lineItem.Comment;
existingLineItem.DynamicProperties = lineItem.DynamicProperties;
}
else
{
await base.AddLineItemAsync(lineItem);
lineItem.Id = null;
Cart.Items.Add(lineItem);
}
}
}
Expand Down
37 changes: 14 additions & 23 deletions VirtoCommerce.Storefront/Domain/Cart/Demo/DemoCartService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,38 +66,30 @@ public override async Task<IPagedList<ShoppingCart>> SearchCartsAsync(CartSearch

var cart = cartDto.ToShoppingCart(currency, language, user);

await AddConfiguredItemsToCartAsync(cart, language, currency);
await FillProductPartsOfGroups(cart, language, currency);

result.Add(cart);
}
return new StaticPagedList<ShoppingCart>(result, criteria.PageNumber, criteria.PageSize, resultDto.TotalCount.Value);
});
}


protected virtual async Task AddConfiguredItemsToCartAsync(ShoppingCart cart, Language language, Currency currency)
protected virtual async Task FillProductPartsOfGroups(ShoppingCart cart, Language language, Currency currency)
{
foreach (var grouping in cart.Items.Where(x => !x.ConfiguredGropupId.IsNullOrEmpty()).GroupBy(x => x.ConfiguredGropupId))
if(cart.ConfiguredGroups.IsNullOrEmpty())
{
var configuredProductId = grouping.Key;
var configuredProductItems = grouping.AsEnumerable().ToArray();
var configuredProductQuantity = configuredProductItems.FirstOrDefault()?.Quantity ?? 1;

var configuredItem = new ConfiguredGroup();
var product = (await _catalogService.GetProductsAsync(new[] {configuredProductId}, ItemResponseGroup.None)).FirstOrDefault();
return;
}

configuredItem.ConfiguredLineItem = product?.ToLineItem(language, configuredProductQuantity);
var groupProductsIds = cart.ConfiguredGroups.Select(x => x.ProductId).ToArray();
var groupProducts = await _catalogService.GetProductsAsync(groupProductsIds, ItemResponseGroup.None);

if (configuredItem.ConfiguredLineItem != null)
{
configuredItem.ConfiguredLineItem.PlacedPrice = new Money(configuredProductItems.Sum(x => x.PlacedPrice.Amount), currency);
configuredItem.ConfiguredLineItem.ExtendedPrice = new Money(configuredProductItems.Sum(x => x.ExtendedPrice.Amount), currency);
}
foreach (var group in cart.ConfiguredGroups)
{
var product = groupProducts.FirstOrDefault(x=>x.Id == group.ProductId);
group.Product = product;

configuredItem
.Parts
.AddRange(
configuredProductItems
var productParts = group.Items
.Select(x =>
{
var result = _demoCatalogService.TryGetProductPartByCategoryId(x.CategoryId);
Expand All @@ -106,10 +98,9 @@ protected virtual async Task AddConfiguredItemsToCartAsync(ShoppingCart cart, La

return result;
})
.OrderBy(x => x.Name)
);
.OrderBy(x => x.Name).ToArray();

cart.ConfiguredGroups.Add(configuredItem);
group.Parts.AddRange(productParts);
}
}
}
Expand Down

0 comments on commit 6b4af0b

Please sign in to comment.