Skip to content

Commit

Permalink
VCST-2303: add changeCartCurrency mutation (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
ksavosteev authored Dec 20, 2024
1 parent 692a6db commit 2a264d5
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 40 deletions.
94 changes: 54 additions & 40 deletions src/VirtoCommerce.XCart.Core/CartAggregate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,73 +180,84 @@ public virtual async Task<CartAggregate> AddConfiguredItemAsync(NewCartItem newC

EnsureCartExists();

if (newCartItem.CartProduct != null)
if (newCartItem.CartProduct == null)
{
CartProducts[newCartItem.CartProduct.Id] = newCartItem.CartProduct;
return this;
}

newConfiguredItem.Id = null;
newConfiguredItem.SelectedForCheckout = IsSelectedForCheckout;
newConfiguredItem.Quantity = newCartItem.Quantity;
newConfiguredItem.Note = newCartItem.Comment;
CartProducts[newCartItem.CartProduct.Id] = newCartItem.CartProduct;

Cart.Items.Add(newConfiguredItem);
newConfiguredItem.Id = null;
newConfiguredItem.SelectedForCheckout = IsSelectedForCheckout;
newConfiguredItem.Quantity = newCartItem.Quantity;
newConfiguredItem.Note = newCartItem.Comment;

if (newCartItem.DynamicProperties != null)
{
await UpdateCartItemDynamicProperties(newConfiguredItem, newCartItem.DynamicProperties);
}
Cart.Items.Add(newConfiguredItem);

await SetItemFulfillmentCenterAsync(newConfiguredItem, newCartItem.CartProduct);
await UpdateVendor(newConfiguredItem, newCartItem.CartProduct);
if (newCartItem.DynamicProperties != null)
{
await UpdateCartItemDynamicProperties(newConfiguredItem, newCartItem.DynamicProperties);
}

await SetItemFulfillmentCenterAsync(newConfiguredItem, newCartItem.CartProduct);
await UpdateVendor(newConfiguredItem, newCartItem.CartProduct);

return this;
}

public virtual async Task<CartAggregate> AddItemAsync(NewCartItem newCartItem)
{
EnsureCartExists();

ArgumentNullException.ThrowIfNull(newCartItem);

EnsureCartExists();

var validationResult = await AbstractTypeFactory<NewCartItemValidator>.TryCreateInstance().ValidateAsync(newCartItem, options => options.IncludeRuleSets(ValidationRuleSet));
if (!validationResult.IsValid)
{
OperationValidationErrors.AddRange(validationResult.Errors);
}
else if (newCartItem.CartProduct != null)
{
if (newCartItem.IsWishlist && newCartItem.CartProduct.Price == null)

if (!newCartItem.IgnoreValidationErrors)
{
newCartItem.CartProduct.Price = new ProductPrice(Currency);
return this;
}
}

if (newCartItem.CartProduct == null)
{
return this;
}

var lineItem = _mapper.Map<LineItem>(newCartItem.CartProduct);
if (newCartItem.IsWishlist && newCartItem.CartProduct.Price == null)
{
newCartItem.CartProduct.Price = new ProductPrice(Currency);
}

lineItem.SelectedForCheckout = IsSelectedForCheckout;
lineItem.Quantity = newCartItem.Quantity;
var lineItem = _mapper.Map<LineItem>(newCartItem.CartProduct);

if (newCartItem.Price != null)
{
lineItem.ListPrice = newCartItem.Price.Value;
lineItem.SalePrice = newCartItem.Price.Value;
}
else
{
SetLineItemTierPrice(newCartItem.CartProduct.Price, newCartItem.Quantity, lineItem);
}
lineItem.Currency ??= Currency.Code;
lineItem.SelectedForCheckout = newCartItem.IsSelectedForCheckout ?? IsSelectedForCheckout;
lineItem.Quantity = newCartItem.Quantity;

if (!string.IsNullOrEmpty(newCartItem.Comment))
{
lineItem.Note = newCartItem.Comment;
}
if (newCartItem.Price != null)
{
lineItem.ListPrice = newCartItem.Price.Value;
lineItem.SalePrice = newCartItem.Price.Value;
}
else
{
SetLineItemTierPrice(newCartItem.CartProduct.Price, newCartItem.Quantity, lineItem);
}

CartProducts[newCartItem.CartProduct.Id] = newCartItem.CartProduct;
await SetItemFulfillmentCenterAsync(lineItem, newCartItem.CartProduct);
await UpdateVendor(lineItem, newCartItem.CartProduct);
await InnerAddLineItemAsync(lineItem, newCartItem.CartProduct, newCartItem.DynamicProperties);
if (!string.IsNullOrEmpty(newCartItem.Comment))
{
lineItem.Note = newCartItem.Comment;
}

CartProducts[newCartItem.CartProduct.Id] = newCartItem.CartProduct;
await SetItemFulfillmentCenterAsync(lineItem, newCartItem.CartProduct);
await UpdateVendor(lineItem, newCartItem.CartProduct);
await InnerAddLineItemAsync(lineItem, newCartItem.CartProduct, newCartItem.DynamicProperties);

return this;
}

Expand All @@ -270,7 +281,9 @@ await AddItemAsync(new NewCartItem(item.ProductId, item.Quantity)
DynamicProperties = item.DynamicProperties,
Price = item.Price,
IsWishlist = item.IsWishlist,
IsSelectedForCheckout = item.IsSelectedForCheckout,
CartProduct = product,
IgnoreValidationErrors = item.IgnoreValidationErrors,
});
}
else
Expand Down Expand Up @@ -1150,6 +1163,7 @@ public virtual async Task<CartAggregate> UpdateConfiguredLineItemPrice(IList<Lin
foreach (var configurationLineItem in configuredItems)
{
var contaner = AbstractTypeFactory<ConfiguredLineItemContainer>.TryCreateInstance();
contaner.Currency = Currency;

if (CartProducts.TryGetValue(configurationLineItem.ProductId, out var configurableProduct))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using VirtoCommerce.XCart.Core.Commands.BaseCommands;

namespace VirtoCommerce.XCart.Core.Commands
{
public class ChangeCartCurrencyCommand : CartCommand
{
public string NewCurrencyCode { get; set; }
}
}
4 changes: 4 additions & 0 deletions src/VirtoCommerce.XCart.Core/Models/NewCartItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,9 @@ public NewCartItem(string productId, int quantity)
public IList<DynamicPropertyValue> DynamicProperties { get; set; }

public bool IsWishlist { get; set; }

public bool? IsSelectedForCheckout { get; set; }

public bool IgnoreValidationErrors { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using GraphQL.Types;

namespace VirtoCommerce.XCart.Core.Schemas
{
public class InputChangeCartCurrencyType : InputCartBaseType
{
public InputChangeCartCurrencyType()
{
Field<NonNullGraphType<StringGraphType>>("newCurrencyCode", "Second cart currency");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using VirtoCommerce.CartModule.Core.Model;
using VirtoCommerce.Platform.Core.Common;
using VirtoCommerce.Xapi.Core.Models;
using VirtoCommerce.XCart.Core;
using VirtoCommerce.XCart.Core.Commands;
using VirtoCommerce.XCart.Core.Commands.BaseCommands;
using VirtoCommerce.XCart.Core.Models;
using VirtoCommerce.XCart.Core.Services;

namespace VirtoCommerce.XCart.Data.Commands
{
public class ChangeCartCurrencyCommandHandler : CartCommandHandler<ChangeCartCurrencyCommand>
{
private readonly ICartProductService _cartProductService;

public ChangeCartCurrencyCommandHandler(
ICartAggregateRepository cartAggregateRepository,
ICartProductService cartProductService)
: base(cartAggregateRepository)
{
_cartProductService = cartProductService;
}

public override async Task<CartAggregate> Handle(ChangeCartCurrencyCommand request, CancellationToken cancellationToken)
{
// get (or create) both carts
var currentCurrencyCartAggregate = await GetOrCreateCartFromCommandAsync(request)
?? throw new OperationCanceledException("Cart not found");

var newCurrencyCartRequest = new ChangeCartCurrencyCommand
{
StoreId = request.StoreId ?? currentCurrencyCartAggregate.Cart.StoreId,
CartName = request.CartName ?? currentCurrencyCartAggregate.Cart.Name,
CartType = request.CartType ?? currentCurrencyCartAggregate.Cart.Type,
UserId = request.UserId ?? currentCurrencyCartAggregate.Cart.CustomerId,
OrganizationId = request.OrganizationId ?? currentCurrencyCartAggregate.Cart.OrganizationId,
CultureName = request.CultureName ?? currentCurrencyCartAggregate.Cart.LanguageCode,
CurrencyCode = request.NewCurrencyCode,
};

var newCurrencyCartAggregate = await GetOrCreateCartFromCommandAsync(newCurrencyCartRequest);

// clear (old) cart items and add items from the currency cart
newCurrencyCartAggregate.Cart.Items.Clear();

await CopyItems(currentCurrencyCartAggregate, newCurrencyCartAggregate);

await CartRepository.SaveAsync(newCurrencyCartAggregate);
return newCurrencyCartAggregate;
}

protected virtual async Task CopyItems(CartAggregate currentCurrencyCartAggregate, CartAggregate newCurrencyCartAggregate)
{
var ordinaryItems = currentCurrencyCartAggregate.LineItems
.Where(x => !x.IsConfigured)
.ToArray();

if (ordinaryItems.Length > 0)
{
var newCartItems = ordinaryItems
.Select(x => new NewCartItem(x.ProductId, x.Quantity)
{
IgnoreValidationErrors = true,
Comment = x.Note,
IsSelectedForCheckout = x.SelectedForCheckout,
DynamicProperties = x.DynamicProperties.SelectMany(x => x.Values.Select(y => new DynamicPropertyValue()
{
Name = x.Name,
Value = y.Value,
Locale = y.Locale,
})).ToArray(),
})
.ToArray();

await newCurrencyCartAggregate.AddItemsAsync(newCartItems);
}

// copy configured items
var configuredItems = currentCurrencyCartAggregate.LineItems
.Where(x => x.IsConfigured)
.ToArray();

await CopyConfiguredItems(newCurrencyCartAggregate, configuredItems);
}

protected virtual async Task CopyConfiguredItems(CartAggregate newCurrencyCartAggregate, IList<LineItem> configuredItems)
{
if (configuredItems.Count == 0)
{
return;
}

var configProductsIds = configuredItems
.Where(x => !x.ConfigurationItems.IsNullOrEmpty())
.SelectMany(x => x.ConfigurationItems.Select(x => x.ProductId))
.Distinct()
.ToList();

configProductsIds.AddRange(configuredItems.Select(x => x.ProductId));

var configProducts = await _cartProductService.GetCartProductsByIdsAsync(newCurrencyCartAggregate, configProductsIds);

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

contaner.ConfigurableProduct = configProducts.FirstOrDefault(x => x.Product.Id == configurationLineItem.ProductId);

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);
}
}

var expItem = contaner.CreateConfiguredLineItem(configurationLineItem.Quantity);

await newCurrencyCartAggregate.AddConfiguredItemAsync(new NewCartItem(configurationLineItem.ProductId, configurationLineItem.Quantity)
{
CartProduct = contaner.ConfigurableProduct,
IgnoreValidationErrors = true,
Comment = configurationLineItem.Note,
IsSelectedForCheckout = configurationLineItem.SelectedForCheckout,
DynamicProperties = configurationLineItem.DynamicProperties.SelectMany(x => x.Values.Select(y => new DynamicPropertyValue()
{
Name = x.Name,
Value = y.Value,
Locale = y.Locale,
})).ToArray(),
}, expItem.Item);
}
}
}
}
19 changes: 19 additions & 0 deletions src/VirtoCommerce.XCart.Data/Schemas/PurchaseSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,25 @@ public void Build(ISchema schema)

schema.Mutation.AddField(margeCartField);

var changeCartCurrency = FieldBuilder.Create<CartAggregate, CartAggregate>(GraphTypeExtenstionHelper.GetActualType<CartType>())
.Name("changeCartCurrency")
.Argument(GraphTypeExtenstionHelper.GetActualComplexType<NonNullGraphType<InputChangeCartCurrencyType>>(), SchemaConstants.CommandName)
.ResolveSynchronizedAsync(CartPrefix, "userId", _distributedLockService, async context =>
{
var cartCommand = context.GetCartCommand<ChangeCartCurrencyCommand>();

await CheckAuthByCartCommandAsync(context, cartCommand);

//We need to add cartAggregate to the context to be able use it on nested cart types resolvers (e.g for currency)
var cartAggregate = await _mediator.Send(cartCommand);

//store cart aggregate in the user context for future usage in the graph types resolvers
context.SetExpandedObjectGraph(cartAggregate);
return cartAggregate;
}).FieldType;

schema.Mutation.AddField(changeCartCurrency);

/// <example>
/// This is an example JSON request for a mutation
/// {
Expand Down

0 comments on commit 2a264d5

Please sign in to comment.