Save of updated Cart crashes (upgraded to 10.2.3)

Vote:
 

Hi

After upgrading to 10.2.3 we are experiencing _orderRepository.Save(cart); crashing after cart is updated and saved again. The exception comes from SQL: Cannot insert the value NULL into column 'InStockQuantity', table 'NNNNNNNNNN_10.dbo.LineItem'; column does not allow nulls. UPDATE fails.

For first there is no setting of InStockQuantity by us, but it is passed as not null and Save goes OK. Next time same code is run, it fails. For sake of testing there is one simple entry type:

[CatalogContentType(GUID = "F2474AB5-CCED-48D9-BECA-5522F3AA55B3",
MetaClassName = "TestVariant",
DisplayName = "Test Variant")]
public class TestVariant : VariationContent
{
}
}

One entry of that type is created in catalog UI and then this shippet of code is executed:

            var cart = GetCart();

           string code = "New-test-variant_1";

            var lineItem = cart.CreateLineItem(code);
            lineItem.Quantity = 1;
            lineItem.PlacedPrice = 100;

            cart.AddLineItem(lineItem);
            var cartLink = _orderRepository.Save(cart);

where GetCart returns always the same named cart

        private ICart GetCart()
        {
            var customer = Guid.Parse("CD8A9CCB-8550-4E4B-AC5F-5262E7B89491");

            return _orderRepository.LoadOrCreateCart(customer, "DefaultName");
        }

First time cart is saved with no problems, then second call causes exception:

System.Data.SqlClient.SqlException was unhandled by user code
  Class=16
  ErrorCode=-2146232060
  HResult=-2146232060
  LineNumber=41
  Message=Cannot insert the value NULL into column 'InStockQuantity', table 'Sport1Commerce_10.dbo.LineItem'; column does not allow nulls. UPDATE fails.
Concurrency Error
The statement has been terminated.
  Number=515
  Procedure=ecf_LineItem_Update
  Server=.
  Source=.Net SqlClient Data Provider
  State=2
  StackTrace:
       at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
       at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
       at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
       at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption)
       at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
       at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
       at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
       at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
       at EPiServer.Data.Providers.SqlTransientErrorsRetryPolicy.Execute[TResult](Func`1 method)
       at Mediachase.Data.Provider.SqlDataProvider.ExecuteNonExec(DataCommand command)
       at Mediachase.Commerce.Storage.MetaStorageBase.AcceptChanges(MetaDataContext context, Boolean saveSystem)
       at Mediachase.Commerce.Orders.LineItem.AcceptChanges()
       at Mediachase.Commerce.Storage.MetaStorageCollectionBase`1.AcceptChanges()
       at Mediachase.Commerce.Orders.OrderForm.AcceptChanges()
       at Mediachase.Commerce.Storage.MetaStorageCollectionBase`1.AcceptChanges()
       at Mediachase.Commerce.Orders.OrderGroup.AcceptChanges()
       at Mediachase.Commerce.Orders.Cart.AcceptChanges()
       at EPiServer.Commerce.Order.Internal.CartProvider.Save(ICart cart)
       at EPiServer.Commerce.Order.DefaultOrderRepository.Save(IOrderGroup order)
       at Sport1.Web.Controllers.API.CartController.Put(CartRequest request) in C:\Work\Projects\Sport1\src\Sport1.Web\Controllers\API\CartController.cs:line 59
       at lambda_method(Closure , Object , Object[] )
       at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.b__9(Object instance, Object[] methodParameters)
       at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)


If somebody has an idea what to look for as a reason, it will be much appreciated

Thanks

#174538
Jan 29, 2017 17:46
Vote:
 

Hi,

I created this test with Mspec, and it ran successfully:

    public class Test : IntegrationTest
    {
        protected static ICart _cart;

        Establish context = () =>
        {
            SaveCart();
            _cart = SaveCart();
        };

        private static ICart GetCart()
        {
            var customer = Guid.Parse("CD8A9CCB-8550-4E4B-AC5F-5262E7B89491");

            return OrderRepository.LoadOrCreateCart<ICart>(customer, "DefaultName");
        }

        private static ICart SaveCart()
        {
            var cart = GetCart();

            string code = "New-test-variant_1";

            var lineItem = cart.CreateLineItem(code);
            lineItem.Quantity = 1;
            lineItem.PlacedPrice = 100;

            cart.AddLineItem(lineItem);
            var cartLink = OrderRepository.Save(cart);
            return OrderRepository.Load<ICart>(cartLink.OrderGroupId);
        }

        It Should_save_the_cart_successfully = () => _cart.ShouldNotBeNull();
    }

Is this the same code which throws exception for you?

#174540
Jan 29, 2017 20:31
Vote:
 

Yes, (depending on what is OrderRepository in your code). In mine it is injected IOrderRepository, suppose it's the same.

The code goes through ok first time, and cart is saved and that LineItem is in it when I look for it in Manager. Then on next call it raises exception about InStockQuantity being null.

Only additional thing I managed to notice is that on the first run of the snippet, the line item is of a type Mediachase.Commerce.Orders.LineItem (when Cart is inspected just before the save). After first Save is executed, that line item becomes EPiServer.Commerce.Order.Internal.InMemoryLineItem.

Just before second Save this is returned by GetAllLineItems()

+ [0] {EPiServer.Commerce.Order.Internal.InMemoryLineItem} EPiServer.Commerce.Order.ILineItem {EPiServer.Commerce.Order.Internal.InMemoryLineItem}
+ [1] {Mediachase.Commerce.Orders.LineItem} EPiServer.Commerce.Order.ILineItem {Mediachase.Commerce.Orders.LineItem}

The InMemoryLine item doesn not carry InStockQuantity with it, so it breaks with constrained not-NULL value when cart is Saved second time.

-		[0]	{EPiServer.Commerce.Order.Internal.InMemoryLineItem}	EPiServer.Commerce.Order.ILineItem {EPiServer.Commerce.Order.Internal.InMemoryLineItem}
		Code	"New-test-variant_1"	string
		DisplayName	"New test variant"	string
		InventoryTrackingStatus	Disabled	Mediachase.Commerce.Inventory.InventoryTrackingStatus
		IsGift	false	bool
		IsInventoryAllocated	false	bool
		LineItemDiscountAmount	0.00	decimal
		LineItemId	11071	int
		OrderLevelDiscountAmount	0.000000000	decimal
		PlacedPrice	100.000000000	decimal
+		Properties	Count = 7	System.Collections.Hashtable
		Quantity	1	decimal
		ReturnQuantity	0	decimal
+		Non-Public members		

Just for sake of reference, the one that was added in second run is like this

-		[1]	{Mediachase.Commerce.Orders.LineItem}	EPiServer.Commerce.Order.ILineItem {Mediachase.Commerce.Orders.LineItem}
		AllowBackordersAndPreorders	false	bool
		BackorderQuantity	0	decimal
		Catalog	""	string
		CatalogEntryId	"New-test-variant_1"	string
		CatalogNode	""	string
		Code	"New-test-variant_1"	string
		ConfigurationId	""	string
+		ConnectionHandler	{EPiServer.ServiceLocation.Injected<Mediachase.Data.Provider.IConnectionStringHandler>}	EPiServer.ServiceLocation.Injected<Mediachase.Data.Provider.IConnectionStringHandler>
+		Created	{29.01.2017 21.30.48}	System.DateTime
		CreatorId	null	string
		Description	""	string
		DisableSync	false	bool
+		Discounts	{Mediachase.Commerce.Orders.LineItemDiscountCollection}	Mediachase.Commerce.Orders.LineItemDiscountCollection
		DisplayName	""	string
		ExtendedPrice	0	decimal
+		FieldStorage	Count = 7	System.Collections.Hashtable {Mediachase.MetaDataPlus.ObservableHashtable}
		Id	-7	int
		InStockQuantity	0	decimal
		InventoryStatus	0	int
		IsGift	false	bool
		IsInventoryAllocated	false	bool
+		ItemArray	{object[7]}	object[]
+		LineItemCalculator	{EPiServer.ServiceLocation.Injected<EPiServer.Commerce.Order.ILineItemCalculator>}	EPiServer.ServiceLocation.Injected<EPiServer.Commerce.Order.ILineItemCalculator>
		LineItemDiscountAmount	0	decimal
		LineItemId	-7	int
+		LineItemOrdering	{29.01.2017 21.30.48}	System.DateTime
		ListPrice	0	decimal
		MaxQuantity	100	decimal
+		MetaClass	{Mediachase.MetaDataPlus.Configurator.MetaClass}	Mediachase.MetaDataPlus.Configurator.MetaClass
		MinQuantity	1	decimal
+		Modified	{29.01.2017 21.30.48}	System.DateTime
		ModifierId	null	string
		ObjectState	Added	Mediachase.MetaDataPlus.MetaObjectState
		OldQuantity	0	decimal
		OrderFormId	0	int
		OrderGroupId	0	int
		OrderLevelDiscountAmount	0	decimal
		OrigLineItemId	null	int?
+		Parent	{Mediachase.Commerce.Orders.OrderForm}	Mediachase.Commerce.Orders.OrderForm
		ParentCatalogEntryId	""	string
		PlacedPrice	100	decimal
		PreorderQuantity	0	decimal
		ProviderId	""	string
		Quantity	1	decimal
		ReturnQuantity	0	decimal
		ReturnReason	""	string
		ShippingAddressId	""	string
+		ShippingMethodId	{00000000-0000-0000-0000-000000000000}	System.Guid
		ShippingMethodName	""	string
		Status	""	string
+		SystemFieldStorage	Count = 34	System.Collections.Hashtable
		WarehouseCode	""	string
+		Static members		
+		Non-Public members		


Obviously InStockQuantity is 0 (which is correct for test SKU) which is not NULL. 

I can confirm that in QuickSilver 10.2.3. this saving and then saving updated cart works. I have some problem obviously, but am wondering where to start looking for solution to, bcs upgrade process didn't raise any errors. Hm.

Comparing to QuickSilver shows that it works with Seriazable cart, adding SeriazableLineItem, that contains full set of properties including InStockQuantity. So this InMemoryLineItem is pretty suspicious, but I don't know where did that come from.

#174541
Edited, Jan 29, 2017 22:36
Vote:
 
<p>@Quan: I've&nbsp;been involved in&nbsp;trying to resolve this issue.</p> <p>I just did a test where I replaced the databases&nbsp;with a fresh episerver db install. I created a simple test sku (with no meta properties) and tried to run the sample code, but the same error occurs.<br /><br /></p> <p></p>
#174549
Jan 30, 2017 10:52
Vote:
 

@Mari: Can you show the test code here?

#174550
Jan 30, 2017 10:58
Vote:
 

To isolate the issue I have created a TestController that I call manually:

public ActionResult Index()
        {
            var cart = LoadOrCreateCart(Mediachase.Commerce.Orders.Cart.DefaultName);
            
            var lineItem1 = cart.CreateLineItem("test-sku123");
            lineItem1.Quantity = 1;
            lineItem1.PlacedPrice = 100;
            cart.AddLineItem(lineItem1);
            _orderRepository.Save(cart);

            var content = new ContentResult
            {
                Content = string.Format("<h1>Nr of items in cart: {0}</h1>", cart.GetAllLineItems().Count())
            };

            return content;

        }

        private ICart LoadOrCreateCart(string name)
        {

            var customer = Guid.Parse("BD7F334C-2D8A-4836-BB7F-2D3302216D20");
            var cart = _orderRepository.LoadOrCreateCart<ICart>(customer, name, _currentMarket);

            return cart;
        }
#174551
Jan 30, 2017 11:03
Vote:
 

I was unable to reproduce this problem on 10.2.3 (QS), with old or new cart system.

Nr of items in cart: 6

Is there anything special about test-sku123?

#174555
Jan 30, 2017 13:34
Vote:
 

No, it's a clean Variation without any properties. 

If I turn on SQL Profiler I can see that on 2nd add it calls stored procedure "ecf_LineItem_Update" with @InStockQuantity=NULL. Do you have any idea what could be causing this?

I originally thought it was a database issue, but since I can reproduce it in a fresh db it has to be somthing in the solution.

#174558
Jan 30, 2017 13:44
Vote:
 

And: If I run the same code using the old api, it works with no errors. Here is the code:

var custmerGuid = Guid.Parse("9A9510FB-8E32-43BB-BC14-6C13B6A47BDC");
            Cart cart = OrderContext.Current.GetCart(Cart.DefaultName, custmerGuid);

            //Get first orderform or create on if no exists
            var orderForm = GetOrderForm(cart);

            var lineItem = new LineItem
            {
                Code = "test-sku123",
                Quantity = 1
            };

            orderForm.LineItems.Add(lineItem);

            //now save the changes to the database
            cart.AcceptChanges();
#174560
Jan 30, 2017 13:58
Vote:
 

SKU type doesn't play a role in reproducing the bug. I have just put "ninja" (that doesn't even exist as object) for the Code and saved the cart (first time). Cart is visible in Manager.

When cart is updated and Save called again, same SQL exception breaks with InStockQuantity NULL message.

So the very sku type is not causing it. It breaks the same for variants that work CartHelper (before and after upgrade), test sku that is an empty type (like on beginning of this thread), or just putting any string for code (bcs validation is not called).

#174561
Jan 30, 2017 14:00
Vote:
 

Can you zip the project (I assume you can produce it on Quicksilver) and send it to me directly via email.

I suspect this is an urgent matter and we are willing to help - but we need to reproduct the problem first.

Regards,

/Q

#174562
Jan 30, 2017 14:02
Vote:
 

What do you mean by "produce it on QuickSilver"?

#174563
Jan 30, 2017 14:20
Vote:
 

I meant "reproduce" - can you replace one of the controller in Quicksilver with your code to see if it happens?

#174564
Jan 30, 2017 14:21
Vote:
 

We cannot reproduce this on QuickSilver (as stated in the original post).

#174565
Jan 30, 2017 14:23
Vote:
 

OK I missed that. As I can't reproduce this issue, the usual response - contact our developer support service. When we have a bug report we will act on it ASAP.

#174569
Jan 30, 2017 15:09
Vote:
 

It is already there #47225

#174570
Edited, Jan 30, 2017 15:13
Vote:
 
<p>@Quan: We went back&nbsp;start - did the upgrade again, and now we can see that the test code works. We will add code changes (impl using new order api) step by step to try to see if there are any changes there that might have caused this. Thanks for your help!</p>
#174578
Jan 30, 2017 20:22
Vote:
 

Hi @Mari,

I just guess your issue when you post "it's a clean Variation without any properties" and code, the reason causes this should related to inventory service.

Not sure but when creating a variation - for testing - we need to add an inventory record for that variation. If not, it might be removed when adding to cart.

Hope this help.

/Son Do

#174606
Jan 31, 2017 15:37
Vote:
 

For sake of the test of saving, validation was not called so line item wasn't removed. The problem is solved by upgrading from start.

#174608
Jan 31, 2017 15:57