How does Litium merge a new cart to an existing cart on login?

Say I login and add items to cart. Then on another computer i add other (or the same) items to cart and then login, how does the logic to merge these two carts work?

When you logging in; Litium will add all the stored items into the current shopping cart. This process is using the Litium.Foundation.Modules.ECommerce.Plugins.Checkout.ICheckoutFlow.AddOrderRow method where you as partner can define if the row should be merged together with an existing one or add an additional row.

Is there any way I can override this logic? For example if my new cart is identical to the one previously stored I do not want to double amounts of everything on login?

Not on an easy way. you can always change your implementation of Litium.Foundation.Modules.ECommerce.Plugins.Checkout.ICheckoutFlow to not add multiple items but not sure if you can make the different behaviour for logging in and regular `add to cart´.

A solution (L7.7) to this is to create a boolean field on Customer entity with example name “JustLoggedIn”

Then you set this to true on login, LoginServiceImpl.cs
(Note: you can not set this after getting “var result” back because it would be too late since event AddOrderRow on Checkoutflow is already fired!)

public override bool Login(string loginName, string password, string newPassword, out SecurityToken token)
        {
            token = SecurityToken.AnonymousToken;
            SetCustomerJustLoggedIn(loginName, true);
            var result = _authenticationService.PasswordSignIn(loginName, password, newPassword);
            switch (result)
            {
                case AuthenticationResult.Success:
                    token = new SecurityToken((ClaimsIdentity)System.Threading.Thread.CurrentPrincipal.Identity);
                    _checkoutState.ClearState();
                    return true;
                case AuthenticationResult.RequiresChangedPassword:
                    SetCustomerJustLoggedIn(loginName, false);
                    throw new ChangePasswordException("You need to change password.");
                default:
                    SetCustomerJustLoggedIn(loginName, false);
                    return false;
            }
        }

        private void SetCustomerJustLoggedIn(string loginName, bool justLoggedIn)
        {
            var person = _personService.Get(_securityContextService.GetPersonSystemId(loginName) ?? Guid.Empty);

            if (person is not null && person.SystemId != Guid.Empty && !person.Fields.GetValue<bool>(CustomerFieldNameConstants.JustLoggedIn).Equals(justLoggedIn))
            {
                person = person.MakeWritableClone();
                person.Fields.AddOrUpdateValue(CustomerFieldNameConstants.JustLoggedIn, justLoggedIn);
                using (_securityContextService.ActAsSystem())
                {
                    _personService.Update(person);
                }
            }
        }

Create an overriding class on CheckoutFlow.cs

using System.Linq;
using Litium.Accelerator.Constants;
using Litium.Customers;
using Litium.Foundation.Modules.ECommerce.Carriers;
using Litium.Foundation.Modules.ECommerce.Plugins.Checkout;
using Litium.Foundation.Modules.ECommerce.Plugins.CustomerInfo;
using Litium.Foundation.Modules.ECommerce.Plugins.Deliveries;
using Litium.Foundation.Modules.ECommerce.Plugins.Orders;
using Litium.Foundation.Modules.ECommerce.Plugins.Payments;
using Litium.Foundation.Security;
using Litium.Products;
using Litium.Products.PriceCalculator;
using Litium.Web.Routing;

namespace Litium.Accelerator.Checkout
{
    public class CheckoutFlowCustom : CheckoutFlow
    {
        private readonly SecurityToken _securityToken;

        public CheckoutFlowCustom(IDeliveryFactory deliveryFactory,
                        ICustomerInfoFactory customerInfoFactory,
                        IPaymentInfoFactory paymentInfoFactory,
                        IOrderRowFactory orderRowFactory,
                        IOrderFactory orderFactory,
                        IPriceCalculator priceCalculator,
                        VariantService variantService,
                        RouteRequestLookupInfoAccessor routeRequestLookupInfoAccessor,
                        OrganizationService organizationService,
                        SecurityToken securityToken)
                        : base(deliveryFactory,
                                customerInfoFactory,
                                paymentInfoFactory,
                                orderRowFactory,
                                orderFactory,
                                priceCalculator,
                                variantService,
                                routeRequestLookupInfoAccessor,
                                organizationService)
        {
            _securityToken = securityToken;
        }

        public override OrderRowCarrier AddOrderRow(OrderCarrier orderCarrier, ShoppingCartItemCarrier shoppingCartRow, SecurityToken token)
        {
            // Logic to avoid multiple items in the shoppingcart on login
            var personJustLoggedIn = _securityToken.Person.Fields.GetValue<bool>(CustomerFieldNameConstants.JustLoggedIn);

            if (personJustLoggedIn && orderCarrier.OrderRows.Any(x => x.ArticleNumber.Equals(shoppingCartRow.ArticleNumber) && x.Quantity.Equals(shoppingCartRow.Quantity)))
            {
                return null;
            }
            return base.AddOrderRow(orderCarrier, shoppingCartRow, token);
        }
    }
}

Then last part add this extra code to OrderUtilities.cs AddArticleToCart method:
(Make sure all other parts of Accelerator code call this method when adding items to cart)

 public void AddArticleToCart(string articleNumber, decimal quantity, Guid languageId)
        {
            // Article can not be added to the shoping cart if the cart has no order.
            if (!HasOrder)
            {
                return;
            }

            // Cancelling the multiple adding to cart check on login
            if (_securityToken.Person.Fields.GetValue<bool>(CustomerFieldNameConstants.JustLoggedIn))
            {
                var person = _securityToken.Person.MakeWritableClone();
                person.Fields.AddOrUpdateValue(CustomerFieldNameConstants.JustLoggedIn, false);
                using (_securityContextService.ActAsSystem())
                {
                    _personService.Update(person);
                }
            }
            
            try
            {
                var article = _variantService.Get(articleNumber);

                // Add the article to the shoping cart if the article exists
                if (article != null)
                {
                    if (quantity > 0)
                    {
                        _cartAccessor.Cart.Add(article.Id, quantity, string.Empty, languageId);
                    }
                }
            }
            catch (Exception ex)
            {
                this.Log().Error($"AddArticleToCart: {ex.Message}", ex);
            }
        }
2 Likes