Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VCST-2093: add section Id/changeCartConfiguredItem mutation/configurationItems query #22

Merged
merged 12 commits into from
Dec 6, 2024
74 changes: 72 additions & 2 deletions src/VirtoCommerce.XCart.Core/CartAggregate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -456,9 +456,16 @@ public virtual async Task<CartAggregate> ChangeItemQuantityAsync(ItemQtyAdjustme

if (lineItem != null)
{
SetLineItemTierPrice(qtyAdjustment.CartProduct.Price, qtyAdjustment.NewQuantity, lineItem);

lineItem.Quantity = qtyAdjustment.NewQuantity;

if (lineItem.IsConfigured)
{
await UpdateConfiguredLineItemPrice([lineItem]);
}
else
{
SetLineItemTierPrice(qtyAdjustment.CartProduct.Price, qtyAdjustment.NewQuantity, lineItem);
}
}

return this;
Expand Down Expand Up @@ -1101,6 +1108,69 @@ public virtual void SetLineItemTierPrice(ProductPrice productPrice, int quantity
}
}

public virtual Task<CartAggregate> UpdateConfiguredItemAsync(string lineItemId, LineItem configuredItem)
ksavosteev marked this conversation as resolved.
Show resolved Hide resolved
{
ArgumentNullException.ThrowIfNull(lineItemId);
ArgumentNullException.ThrowIfNull(configuredItem);

EnsureCartExists();

var lineItem = Cart.Items.FirstOrDefault(x => x.Id == lineItemId && x.IsConfigured);

if (lineItem != null)
{
lineItem.Quantity = configuredItem.Quantity;
lineItem.ListPrice = configuredItem.ListPrice;
lineItem.SalePrice = configuredItem.SalePrice;
lineItem.DiscountAmount = configuredItem.DiscountAmount;
lineItem.PlacedPrice = configuredItem.PlacedPrice;
lineItem.ExtendedPrice = configuredItem.ExtendedPrice;

lineItem.ConfigurationItems = new List<ConfigurationItem>(configuredItem.ConfigurationItems);
}

return Task.FromResult(this);
ksavosteev marked this conversation as resolved.
Show resolved Hide resolved
}

public virtual async Task<CartAggregate> UpdateConfiguredLineItemPrice(IList<LineItem> configuredItems)
{
var configProductsIds = configuredItems
.Where(x => !x.ConfigurationItems.IsNullOrEmpty())
.SelectMany(x => x.ConfigurationItems.Select(x => x.ProductId))
.Distinct()
.ToArray();

if (configProductsIds.Length == 0)
{
return this;
}

var configProducts = await _cartProductService.GetCartProductsByIdsAsync(this, configProductsIds.ToArray());
ksavosteev marked this conversation as resolved.
Show resolved Hide resolved

foreach (var configurationLineItem in configuredItems)
{
var contaner = AbstractTypeFactory<ConfiguredLineItemContainer>.TryCreateInstance();

if (CartProducts.TryGetValue(configurationLineItem.ProductId, out var configurableProduct))
{
contaner.ConfigurableProduct = configurableProduct;
}

foreach (var configurationItem in configurationLineItem.ConfigurationItems ?? [])
{
var product = configProducts.FirstOrDefault(x => x.Product.Id == configurationItem.ProductId);
if (product != null)
{
contaner.AddItem(product, configurationItem.Quantity, configurationItem.SectionId);
}
}

contaner.UpdatePrice(configurationLineItem);
}

return this;
}

#region ICloneable

public virtual object Clone()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Collections.Generic;
using VirtoCommerce.XCart.Core.Commands.BaseCommands;
using VirtoCommerce.XCart.Core.Models;

namespace VirtoCommerce.XCart.Core.Commands;

public class ChangeCartConfiguredItemCommand : CartCommand
ksavosteev marked this conversation as resolved.
Show resolved Hide resolved
{
public string LineItemId { get; set; }

public int? Quantity { get; set; }

public IList<ProductConfigurationSection> ConfigurationSections { get; set; } = new List<ProductConfigurationSection>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@ public class CreateConfiguredLineItemCommand : ICommand<ExpConfigurationLineItem

public string ConfigurableProductId { get; set; }

public int Quantity { get; set; } = 1;

public IList<ProductConfigurationSection> ConfigurationSections { get; set; } = [];
}
42 changes: 27 additions & 15 deletions src/VirtoCommerce.XCart.Core/ConfiguredLineItemContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ public class ConfiguredLineItemContainer : ICloneable
public IList<string> ProductsIncludeFields { get; set; }

public CartProduct ConfigurableProduct { get; set; }
public IList<LineItem> Items { get; set; } = new List<LineItem>();

public LineItem AddItem(CartProduct cartProduct, int quantity)
private readonly List<SectionLineItem> _items = [];

public LineItem AddItem(CartProduct cartProduct, int quantity, string sectionId)
{
var lineItem = AbstractTypeFactory<LineItem>.TryCreateInstance();
lineItem.ProductId = cartProduct.Id;
Expand Down Expand Up @@ -51,17 +52,21 @@ public LineItem AddItem(CartProduct cartProduct, int quantity)
lineItem.ExtendedPrice = lineItem.PlacedPrice * lineItem.Quantity;
}

Items.Add(lineItem);
_items.Add(new SectionLineItem
{
Item = lineItem,
SectionId = sectionId,
});

return lineItem;
}

public ExpConfigurationLineItem CreateConfiguredLineItem()
public ExpConfigurationLineItem CreateConfiguredLineItem(int quantity)
{
var lineItem = AbstractTypeFactory<LineItem>.TryCreateInstance();

lineItem.IsConfigured = true;
lineItem.Quantity = 1;
lineItem.Quantity = quantity;

lineItem.Discounts = [];
lineItem.TaxDetails = [];
Expand All @@ -83,18 +88,19 @@ public ExpConfigurationLineItem CreateConfiguredLineItem()
lineItem.VendorId = ConfigurableProduct.Product.Vendor;

// create sub items
lineItem.ConfigurationItems = Items
lineItem.ConfigurationItems = _items
.Select(x =>
{
var subItem = AbstractTypeFactory<ConfigurationItem>.TryCreateInstance();

subItem.ProductId = x.ProductId;
subItem.Name = x.Name;
subItem.Sku = x.Sku;
subItem.ImageUrl = x.ImageUrl;
subItem.Quantity = x.Quantity;
subItem.CatalogId = x.CatalogId;
subItem.CategoryId = x.CategoryId;
subItem.SectionId = x.SectionId;
subItem.ProductId = x.Item.ProductId;
subItem.Name = x.Item.Name;
subItem.Sku = x.Item.Sku;
subItem.ImageUrl = x.Item.ImageUrl;
subItem.Quantity = x.Item.Quantity;
subItem.CatalogId = x.Item.CatalogId;
subItem.CategoryId = x.Item.CategoryId;

return subItem;
})
Expand All @@ -119,8 +125,8 @@ public void UpdatePrice(LineItem lineItem)
{
var configurableProductPrice = ConfigurableProduct.Price ?? new Xapi.Core.Models.ProductPrice(Currency);

lineItem.ListPrice = Items.Sum(x => x.ListPrice * x.Quantity) + configurableProductPrice.ListPrice.Amount;
lineItem.SalePrice = Items.Sum(x => x.SalePrice * x.Quantity) + configurableProductPrice.SalePrice.Amount;
lineItem.ListPrice = _items.Select(x => x.Item).Sum(x => x.ListPrice * x.Quantity) + configurableProductPrice.ListPrice.Amount;
lineItem.SalePrice = _items.Select(x => x.Item).Sum(x => x.SalePrice * x.Quantity) + configurableProductPrice.SalePrice.Amount;

lineItem.DiscountAmount = Math.Max(0, lineItem.ListPrice - lineItem.SalePrice);
lineItem.PlacedPrice = lineItem.ListPrice - lineItem.DiscountAmount;
Expand All @@ -144,5 +150,11 @@ public object Clone()
{
return MemberwiseClone();
}

private sealed class SectionLineItem
{
public string SectionId { get; set; }
public LineItem Item { get; set; }
}
}
}
11 changes: 11 additions & 0 deletions src/VirtoCommerce.XCart.Core/Models/ConfigurationItemsResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Generic;
using VirtoCommerce.CartModule.Core.Model;

namespace VirtoCommerce.XCart.Core.Models;

public class ConfigurationItemsResponse
{
public CartAggregate CartAggregate { get; set; }

public IList<ConfigurationItem> ConfigurationItems { get; set; } = [];
}
51 changes: 51 additions & 0 deletions src/VirtoCommerce.XCart.Core/Queries/GetConfigurationItemsQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.Linq;
using GraphQL;
using GraphQL.Types;
using VirtoCommerce.Xapi.Core.BaseQueries;
using VirtoCommerce.Xapi.Core.Extensions;
using VirtoCommerce.XCart.Core.Models;

namespace VirtoCommerce.XCart.Core.Queries;

public class GetConfigurationItemsQuery : Query<ConfigurationItemsResponse>, ICartQuery
{
public IList<string> IncludeFields { get; set; } = new List<string>();
public string StoreId { get; set; }
public string CartType { get; set; }
public string CartName { get; set; }
public string UserId { get; set; }
public string OrganizationId { get; set; }
public string CurrencyCode { get; set; }
public string CultureName { get; set; }

public string CartId { get; set; }
public string LineItemId { get; set; }

public override IEnumerable<QueryArgument> GetArguments()
{
yield return Argument<StringGraphType>(nameof(CartId));
yield return Argument<NonNullGraphType<StringGraphType>>(nameof(LineItemId));
yield return Argument<NonNullGraphType<StringGraphType>>(nameof(StoreId), description: "Store Id");
yield return Argument<NonNullGraphType<StringGraphType>>(nameof(CurrencyCode), description: "Currency code (\"USD\")");
yield return Argument<StringGraphType>(nameof(CartType), description: "Cart type");
yield return Argument<StringGraphType>(nameof(CartName), description: "Cart name");
yield return Argument<StringGraphType>(nameof(UserId), description: "User Id");
yield return Argument<StringGraphType>(nameof(CultureName), description: "Culture name (\"en-Us\")");
}
ksavosteev marked this conversation as resolved.
Show resolved Hide resolved

public override void Map(IResolveFieldContext context)
{
CartId = context.GetArgument<string>(nameof(CartId));
LineItemId = context.GetArgument<string>(nameof(LineItemId));
StoreId = context.GetArgument<string>(nameof(StoreId));
CartType = context.GetArgument<string>(nameof(CartType));
CartName = context.GetArgument<string>(nameof(CartName));
UserId = context.GetArgument<string>(nameof(UserId));
OrganizationId = context.GetCurrentOrganizationId();
CurrencyCode = context.GetArgument<string>(nameof(CurrencyCode));
CultureName = context.GetArgument<string>(nameof(CultureName));

IncludeFields = context.SubFields.Values.GetAllNodesPaths(context).ToArray();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using VirtoCommerce.CartModule.Core.Model;
using VirtoCommerce.CartModule.Core.Model;
using VirtoCommerce.Xapi.Core.Schemas;

namespace VirtoCommerce.XCart.Core.Schemas
Expand All @@ -9,6 +9,9 @@ public CartConfigurationItemType()
{
Field(x => x.Id, nullable: false).Description("Configuration item ID");
Field(x => x.Name, nullable: true).Description("Configuration item name");
Field(x => x.SectionId, nullable: true).Description("Configuration item section ID");
Field(x => x.ProductId, nullable: true).Description("Configuration item product ID");
Field(x => x.Quantity, nullable: true).Description("Configuration item quantity");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using GraphQL.Types;
using VirtoCommerce.Xapi.Core.Schemas;
using VirtoCommerce.XCart.Core.Models;

namespace VirtoCommerce.XCart.Core.Schemas;

public class ConfigurationItemsResponseType : ExtendableGraphType<ConfigurationItemsResponse>
{
public ConfigurationItemsResponseType()
{
ExtendableField<ListGraphType<CartConfigurationItemType>>(
"configurationItems",
"Configuration items for configurable product",
resolve: context => context.Source.ConfigurationItems ?? []);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using GraphQL.Types;

namespace VirtoCommerce.XCart.Core.Schemas
{
public class InputChangeCartConfiguredItemType : InputCartBaseType
{
public InputChangeCartConfiguredItemType()
{
Field<NonNullGraphType<StringGraphType>>("lineItemId", "Line item Id");
Field<IntGraphType>("quantity", "Quantity");
Field<ListGraphType<ConfigurationSectionInput>>("configurationSections");
ksavosteev marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
<PackageReference Include="VirtoCommerce.Xapi.Core" Version="3.809.0" />
<PackageReference Include="VirtoCommerce.XCatalog.Core" Version="3.818.0" />
<PackageReference Include="VirtoCommerce.CartModule.Core" Version="3.820.0" />
<PackageReference Include="VirtoCommerce.CartModule.Core" Version="3.822.0" />
<PackageReference Include="VirtoCommerce.PaymentModule.Core" Version="3.804.0" />
<PackageReference Include="VirtoCommerce.ShippingModule.Core" Version="3.802.0" />
<PackageReference Include="VirtoCommerce.InventoryModule.Core" Version="3.805.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using VirtoCommerce.CartModule.Core.Model;
using VirtoCommerce.XCart.Core;
using VirtoCommerce.XCart.Core.Commands;
using VirtoCommerce.XCart.Core.Commands.BaseCommands;
using VirtoCommerce.XCart.Core.Services;

namespace VirtoCommerce.XCart.Data.Commands;

public class ChangeCartConfiguredItemCommandHandler : CartCommandHandler<ChangeCartConfiguredItemCommand>
{
private readonly IMediator _mediator;

public ChangeCartConfiguredItemCommandHandler(ICartAggregateRepository cartAggregateRepository, IMediator mediator)
: base(cartAggregateRepository)
{
_mediator = mediator;
}

public override async Task<CartAggregate> Handle(ChangeCartConfiguredItemCommand request, CancellationToken cancellationToken)
{
var cartAggregate = await GetOrCreateCartFromCommandAsync(request);

var lineItem = GetConfiguredLineItem(request, cartAggregate);
if (lineItem != null)
{
var createConfigurableProductCommand = new CreateConfiguredLineItemCommand
ksavosteev marked this conversation as resolved.
Show resolved Hide resolved
{
StoreId = request.StoreId,
UserId = request.UserId,
OrganizationId = request.OrganizationId,
CultureName = request.CultureName,
CurrencyCode = request.CurrencyCode,
ConfigurableProductId = lineItem.ProductId,
ConfigurationSections = request.ConfigurationSections,
Quantity = request.Quantity ?? lineItem.Quantity,
};

var mediatorResult = await _mediator.Send(createConfigurableProductCommand, cancellationToken);
await cartAggregate.UpdateConfiguredItemAsync(lineItem.Id, mediatorResult.Item);

return await SaveCartAsync(cartAggregate);
}

return cartAggregate;
}

private static LineItem GetConfiguredLineItem(ChangeCartConfiguredItemCommand request, CartAggregate cartAggregate)
{
return cartAggregate.Cart.Items.FirstOrDefault(x => x.Id == request.LineItemId && x.IsConfigured);
}
}
Loading
Loading