Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Ryan Dansie 17 posts 98 karma points
    Mar 18, 2016 @ 16:04
    Ryan Dansie
    0

    manually create shipment for an order/invoice

    What I'm trying to do is export some specific products from an order (to a CSV file) once payment has been taken, and then to mark those products as processed so that anybody viewing the back end would be able to see that those products don't need to be included in any further shipment (they are offloaded to a third party).

    The process is triggered once the Paypal IPN is received and we know the payment has been taken successfully and the code which exports the relevant products to a CSV file is working fine. The bit i'm having trouble with is marking the relevant products as shipped/processed.

    From looking in the back end, I can see that you can use the action to "Fulfill" on an order, which allows you to create a shipment and select specific products from the order to be included in that shipment. So it seems the best way to achieve what I want would be to create a shipment programmatically after performing the CSV export and adding the relevant items to it.

    I can see in the back end that once you create a shipment on an order, it then appears in the "shipments" tab when viewing that order and if the "Fulfill" action is used again, the system already knows which products are already included in a shipment. From this, its apparent that the shipment is linked to the invoice somehow but for the life of me I cant see where that link is created/recorded either in the database or in code. I see no reference to the invoice or order id from the shipment record.

    Guid invoiceKey = Guid.Parse(successURL.ParseQueryString()["InvoiceKey"]);
    var invoice = _merchelloContext.Services.InvoiceService.GetByKey(invoiceKey);
    var shipmentStatus = _merchelloContext.Services.ShipmentService.GetAllShipmentStatuses().First(s => s.Alias == "packaging");
    var defaultWarehouseAddress = _merchelloContext.Services.WarehouseService.GetDefaultWarehouse().AsAddress();
    var shipment = _merchelloContext.Services.ShipmentService.CreateShipment(shipmentStatus, defaultWarehouseAddress, address, invoice.Items);
    
    //I have created the shipment. Now how do I link it to the invoice?
    
    _merchelloContext.Services.ShipmentService.Save(shipment);
    

    From the code I have written, how do I link the newly created shipment to the pre existing invoice?

    Am I approaching this in the right way?

    My merchello version is 1.11.

  • Ryan Dansie 17 posts 98 karma points
    Mar 21, 2016 @ 10:14
    Ryan Dansie
    0

    Note when I run the code above, no shipment is added to the database.

  • Ryan Dansie 17 posts 98 karma points
    Mar 21, 2016 @ 12:05
    Ryan Dansie
    0

    I think I have found the database link between the shipment and the invoice. There is an orderKey and a shipmentKey field on the merchOrderItem table.

    Regarding the above code not creating a shipment, I have found that there is an exception thrown on the last line:

    Message: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_merchOrderItem_merchOrder". The conflict occurred in database "GUWShop", table "dbo.merchOrder", column 'pk'.
    The statement has been terminated., Stack Trace: 
    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)
    at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
    at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
    at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
    at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
    at StackExchange.Profiling.Data.ProfiledDbCommand.ExecuteNonQuery()
    at Umbraco.Core.Persistence.PetaPocoCommandExtensions.<>c__DisplayClass1.<ExecuteNonQueryWithRetry>b__0()
    at Umbraco.Core.Persistence.FaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)
    at Umbraco.Core.Persistence.Database.Insert(String tableName, String primaryKeyName, Boolean autoIncrement, Object poco)
    at Umbraco.Core.Persistence.Database.Insert(Object poco)
    at Merchello.Core.Persistence.Repositories.OrderLineItemRepository.PersistNewItem(IOrderLineItem entity)
    at Merchello.Core.Persistence.Repositories.LineItemRepositoryBase`1.SaveLineItem(T item)
    at Merchello.Core.Persistence.Repositories.ShipmentRepository.PersistNewItem(IShipment entity)
    at Merchello.Core.Persistence.Repositories.MerchelloRepositoryBase`1.PersistNewItem(IEntity entity)
    at Merchello.Core.Persistence.UnitOfWork.PetaPocoUnitOfWork.Commit(Action`1 transactionCompleting)
    at Merchello.Core.Services.ShipmentService.Save(IShipment shipment, Boolean raiseEvents)
    at Merchello.Plugin.Payments.PayPal.Controllers.PayPalApiController.IpnNotification() in {my path}\Merchello.Plugin.Payments.PayPal\Controllers\PayPalApiController.cs:line 290
    

    Note: the original idea to add the line items this way comes from here: https://gist.github.com/rustyswayne/598049064a02fce1597c#file-gistfile1-cs-L83

  • Ryan Dansie 17 posts 98 karma points
    Mar 21, 2016 @ 15:40
    Ryan Dansie
    0

    Made a bit of progress. Instead of this:

    var shipment = _merchelloContext.Services.ShipmentService.CreateShipment(shipmentStatus, defaultWarehouseAddress, address, invoice.Items);
    _merchelloContext.Services.ShipmentService.Save(shipment);
    

    it now looks like this:

    var shipment = _merchelloContext.Services.ShipmentService.CreateShipment(shipmentStatus, defaultWarehouseAddress, address);
    shipment.Items.Add(invoice.Orders.First().Items);
    _merchelloContext.Services.ShipmentService.Save(shipment);
    

    I found that the overload of CreateShipment I was using previously tries to filter out non shippable line items but in the process makes copies of the line items which are missing important information like the primary key and orderKey of that line item, so when it comes to actually saving it, it tries to save with an empty guid for the orderKey hence the previous foreign key error.

    With the new code it is now successfully creating the shipment which I can see in the database. The only problem is when i log in to the back end, view the relevant invoice and then click on the "Shipments" tab, the newly created shipment doesn't appear.

    So it seems i'm still missing something.

  • Ryan Dansie 17 posts 98 karma points
    Mar 21, 2016 @ 17:07
    Ryan Dansie
    0

    I have tracked it down to the InvoiceApicontroller.GetInvoice method in the Merchello.Web project getting a cached version of the invoice. So it seems I have to update the cache after adding the shipment. So far have tried adding the below after saving the shipment:

    Examine.ExamineManager.Instance.IndexProviderCollection["MerchelloInvoiceIndexer"].RebuildIndex();
    

    Which hasn't solved it.

  • Ryan Dansie 17 posts 98 karma points
    Mar 24, 2016 @ 14:47
    Ryan Dansie
    0

    I have confirmed that it's a cache issue but its not automatically updating, even after a long period of time. After three days of no activity and then going back on to the system again it was still not showing the shipment against the relevant invoice.

    However after manually deleting the contents of the App_Data/TEMP directory its now displaying correctly.

    In addition to the above attempt to clear the cache, I have also tried this:

    var IndexProvider = Examine.ExamineManager.Instance.IndexProviderCollection["MerchelloInvoiceIndexer"];
    
    IndexProvider.DeleteFromIndex(invoice.Key.ToString());
    

    But this doesn't work either. Any suggestions would be appreciated.

  • Ryan Dansie 17 posts 98 karma points
    Apr 05, 2016 @ 13:54
    Ryan Dansie
    100

    I ended up taking a different approach with it. My initial thoughts were to try to replicate what the backend was doing to create a shipment however the classes/methods used are marked as internal so are unavailable from outside the merchello assembly.

    So I ended up calling the web api controller directly as below:

                    var shipmentApicontroller = new Web.Editors.ShipmentApiController();
    
                    var shipmentModel = new Web.Models.Shipping.ShipmentRequestDisplay();
    
                    shipmentModel.ShipmentStatusKey = shipmentStatus.Key;
                    shipmentModel.ShipMethodKey = new Guid("5f850937-de92-43bf-9808-792686022f10");
                    shipmentModel.TrackingNumber = "";
    
                    var shipmentOrderDetails = new Web.Models.ContentEditing.OrderDisplay();
                    shipmentModel.Order = shipmentOrderDetails;
                    shipmentOrderDetails.Key = order.Key;
    
                    var shipmentItems = new List<Web.Models.ContentEditing.OrderLineItemDisplay>();
    
                    foreach(var lineItem in shipmentLineItems)
                    {
                        var shipmentItem = new Web.Models.ContentEditing.OrderLineItemDisplay();
                        shipmentItems.Add(shipmentItem);
                        shipmentItem.Key = lineItem.Key;
                    }
    
                    shipmentOrderDetails.Items = shipmentItems;
    
                    shipmentApicontroller.NewShipment(shipmentModel);
    

    Probably not a recommended way to use web api controllers but it's the only thing that has worked for me. Now the shipment is immediately visible in the back end.

  • Jesus 7 posts 75 karma points
    Sep 15, 2017 @ 23:20
    Jesus
    0

    Hi, i just what i need.. thanks a lot

  • M N 125 posts 212 karma points
    Aug 21, 2018 @ 02:17
    M N
    0

    Hey Ryan,

    I'm looking at all sorts of hacky ways at this point as well. Is there a better way to manually create a shipment outside of using any shipment providers at all? Any better approach a year later?

    I started creating manual line items with a sku of something like "myShipSku" but then there is no way to set the "Ship To" under "Billing Address".

  • Willem Dewilde 1 post 71 karma points
    Aug 30, 2018 @ 14:19
    Willem Dewilde
    0

    Hello

    This way you can create a shipment and update the order status. Only thing not included is check if items are in stock and the part of the task chain that decreases the stock but that shouldn't be hard to implement.

    // invoice 
    var invoice = _invoiceService.GetByKey(order.InvoiceKey);
    var currentOrder = _orderService.GetByKey(order.Key);
    
    // shipment line item
    var shipmentLineItem = invoice.Items.FirstOrDefault(x => x.LineItemType == LineItemType.Shipping);
    
    // the shipment
    var quoted = shipmentLineItem.ExtendedData.GetShipment<InvoiceLineItem>();
    
    var status = _shipmentService.GetShipmentStatusByKey(Merchello.Core.Constants.ShipmentStatus.Quoted);
    
    var shipment = _shipmentService.CreateShipment(status, quoted.GetOriginAddress(), quoted.GetDestinationAddress());
    shipment.ShipMethodKey = new Guid("96BBF5DD-D0AB-4CF5-ABE7-67F5312A9C91");
    shipment.VersionKey = quoted.VersionKey;
    shipment.Carrier = "";
    shipment.TrackingCode = "";
    shipment.TrackingUrl = "";
    shipment.Items.Add(currentOrder.Items);
    
    _shipmentService.Save(shipment);
    
    var orderStatus = _orderService.GetOrderStatusByKey(Merchello.Core.Constants.OrderStatus.Open);          
    currentOrder.OrderStatus = orderStatus;
    _orderService.Save(currentOrder);
    
Please Sign in or register to post replies

Write your reply to:

Draft