November Happy Hour will be moved to Thursday December 5th.

Deleting LineItems from Wishlists

Vote:
 

Hello,

We are moving away from the CartHelper and are starting to use the new cart APIs (CMS 11.9.1 and Commerce 11.8.5) and I've run into an issue with our Wishlists.  In the documentation for removing an item from the cart it shows to delete it from the shipment.

var lineItem = cart.GetAllLineItems().FirstOrDefault(x => x.Code == code && !x.IsGift);
if (lineItem != null)
{
    var shipment = cart.GetFirstShipment();
    shipment.LineItems.Remove(lineItem);
}

The wishlists that we currently have only have the LineItem, OrderForm, and OrderGroup tables populated, but no shipment is ever created.  I tried to find another way to remove the line item from the LineItem table, but haven't found any method that would do so.  Would we have to create a shipment for the Wishlists so we can delete the item using the new cart API or am I just missing a different way of doing this?

Thank you,

Kevin Larsen

#198751
Edited, Nov 05, 2018 23:03
Vote:
 

Good question. I don't have an answer out of my head but I will make sure to look into it

#198752
Nov 06, 2018 0:02
Vote:
 

How did you create the wishlists? I suppose you used the old, concrete APIs. So to options for you is to:

  • Call orderForm.GetAllLineItems().ToList() first, then add a shipment to the wishlist, then add the items to the shipment, then ... save it and ... delete again
  • cast the orderForm to OrderForm then delete a lineitem from its LineItems collection. Yes this means you have a tiny tiny old concrete API part in your code

So there is no perfect way to do it. The first option can be written as a migration step to migrate all of your wishlists to make sure they have a proper shipment attached.

#198761
Nov 06, 2018 6:29
Vote:
 

Hi Quan,

Yes, we used the old CartHelper and had an ExtCartHelper that used the CartHelper's AddEntry and also would set the OrderForm.Parent[metaFieldName] to the name the user wanted to save the list as. 

Creating a shipment for every wishlist would seem to be an unnecessary step for something like a wishlist, but I understand under the new API that it is needed.  It's a few extra steps for the current wishlists, but I'm sure the calls wouldn't need to be made for wishlists created under the new process and I can account for that by checking to make sure if a shipment exists for it and if it doesn't then create one.

Thank you,

Kevin Larsen

#198781
Nov 06, 2018 17:06
Vote:
 

Hi,

I made changes to the way that the page loads so that it checks the cart for a shipment and if it does not then it will create a shipment and then get the line items for the order with the code below.

if (cart.GetFirstShipment() != null)
{
	lineItems = cart.GetFirstShipment().LineItems.ToList();
}
else
{
	// If shipment does not exist it is old wishlist, create shipment and add wishlist lineItems to it for future loads
	lineItems = cart.GetAllLineItems().ToList();
	var shipment = orderGroupFactory.CreateShipment(cart);
	cart.AddShipment(shipment, orderGroupFactory);
	shipment.ShippingAddress = orderGroupFactory.CreateOrderAddress(cart);
	foreach (var item in lineItems)
	{
		var lItem = orderGroupFactory.CreateLineItem(item.Code, cart);
		cart.AddLineItem(lItem, orderGroupFactory);
	}
	orderRepository.Save(cart);
	// Get lineItems from created shipment
	lineItems = cart.GetFirstShipment().LineItems.ToList();
}

I did notice that when this ran, the LineItem table would have two lines per item, one looking to be the old one and the other being the new one.  When I ran the below code, which deletes a line item from the wishlist, it looks to remove the duplicate line for the deleted item and also any other old duplicate line item leaving any of the remaining line items that would be attached to the shipment.  Below is the code that I'm using to delete a line item.

// Get the wishlist that the product should be removed from
var orderRepository = ServiceLocator.Current.GetInstance<IOrderRepository>();
var cart = orderRepository.Load<ICart>(wishlistId);
// Remove product from wishlist
var shipment = cart.GetFirstShipment();
if (shipment != null)
{
    var lineItem = shipment.LineItems.FirstOrDefault(x => x.Code == productCode);
    shipment.LineItems.Remove(lineItem);
}

orderRepository.Save(cart);

Would there be something in there that would "validate" the line items and remove any duplicates?  I know there is the ValidateOrRemoveLineItems validation, but I don't know if that would be used to remove the old line items from the LineItem table, or if it would remove the new line items because I'm not adding any additional information like quantity or placed price.

Thank you,

Kevin Larsen

#198785
Nov 06, 2018 21:13
Vote:
 

Hello Kevin.

The reason for that behaviour is that the old lineitems still exist on the OrderForm since they haven't been removed by any part of the API or explicitly by you.

The APIs for removing work on the LineItem.Code level. If you remove a lineitem from a shipment it triggers code that checks if it should also remove it from the OrderForm level. It removes any LineItems with the same Code that aren't a part of a shipment. (So that includes the duplicate)

It's probably best to remove duplicates when running your legacy handling code. Something like this will probably (untested by me :D) do the trick:

        private void MyWishListLogic(ICart cart)
        {
            if (IsLegacyWishList(cart))
                UpdateLegacyWishList(cart);

            var lineItems = cart.GetAllLineItems().ToList();

            // Show wishlist
        }

        private void UpdateLegacyWishList(ICart cart)
        {
            var codes = cart.GetAllLineItems().Select(x => x.Code); // Keep a reference of all current codes
            (cart.GetFirstForm() as OrderForm)?.LineItems?.Clear(); // Clear all lineitems from the orderform.
            
            var shipment = orderGroupFactory.CreateShipment(cart);
            cart.AddShipment(shipment, orderGroupFactory);
            shipment.ShippingAddress = orderGroupFactory.CreateOrderAddress(cart);
            foreach (var code in codes)
            {
                var lineItem = orderGroupFactory.CreateLineItem(code, cart);
                cart.AddLineItem(lineItem, orderGroupFactory);
            }
            orderRepository.Save(cart);
        }

        private bool IsLegacyWishList(ICart cart)
        {
            return cart.Name == Mediachase.Commerce.Orders.Cart.WishListName // or whatever the wishlist names are.
                   && cart.GetFirstShipment() == null;
        }

The main takeaway being the clearing of the old orderform as a new step in the process of porting the legacy wishlist.

You can ignore my refactor if you want. :P

#198812
Nov 07, 2018 8:59
Vote:
 

@Jafet: it's even better if " The first option can be written as a migration step to migrate all of your wishlists to make sure they have a proper shipment attached."

So you don't have to care about IsLegacyWishList ever again

#198817
Nov 07, 2018 9:14
Vote:
 
Yeah, that's even better. I only reacted to the latest question from Kevin. :P
Those example methods can just be moved to the migration in that case. :)
#198818
Edited, Nov 07, 2018 9:28
Vote:
 

@Kevin

Do you have an estimate on how many wishlists would need to be updated this way?

#198823
Nov 07, 2018 9:40
Vote:
 

The number of wishlists is probably not that big.  I don't see it being any more than 250, and even being over 100 would be a bit of a surprise.

#198848
Nov 07, 2018 15:29
Vote:
 

I was able to take the code that you wrote, Jafet, and incorporate it in what I had.  Had to add a call .ToList() when gathering the codes, but other than that it did solve the issue. 

There are few additional things that I still want to make sure are moved over in this check, such as setting the quantity and placed price which are easy, but also columns like MaxQty that are in the LineItem table.  I've struggled trying to figure out how to use the lineItem.Properties[] hashtable to get the metafields to set from the LineItem, but I also may not fully grasp what is all needed to be done to use it.

Would making it a migration step be part of the migration that is ran after the first time the site loads after the upgrade, or would I just have to create a job that could be ran after the upgarde?

Thank you,

Kevin Larsen

#198861
Edited, Nov 07, 2018 16:57
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.