If I remember correctly, there is an enum called OrderShipmentStatus (in Mediachase.Commerce.dll) used to set this property on the Shipment object. There is a class called OrderStatusManager with a method signature like below used to set a Shipment object’s status.
private static Shipment SetShipmentStatus(Shipment shipment, OrderShipmentStatus status) {}
It is a private method that sets the Status property on a shipment instance, runs some workflows, and does some other housekeeping. It is called in other parts of the class as shown in the following public static methods of OrderStatusManager that take a Shipment object as a parameter:
// in ReleaseOrderShipment()
SetShipmentStatus(shipment, OrderShipmentStatus.Released);
// in CancelOrderShipment()
SetShipmentStatus(shipment, OrderShipmentStatus.Cancelled);
// in CompleteOrderShipment()
SetShipmentStatus(shipment, OrderShipmentStatus.Shipped);
// in PickForPackingOrderShipment()
SetShipmentStatus(shipment, OrderShipmentStatus.Packing);
// in ReturnFromPackingOrderShipment()
SetShipmentStatus(shipment, OrderShipmentStatus.Released);
If you take a look at the underlying [Shipment] table there is a column for Status where the enum value is stored as a string. There is also a LineItemIds column that stores a string representation of the associated line items. Of course, it is NOT advised to modify the database table directly and use the API instead to make sure all the proper operations (workflows, etc) are run as well as protect against dependencies that could break code in future versions.
Oh cool.
So how do you handle shipments that are exactly the same but made by different customers.
Say for instance, there were two orders by two customers for the same items. How would I distinguish which shipment would need to be updated if I only know the line item ids for the orders?
If you use the API this should be a non-issue as a Shipment is linked to it's parent OrderForm type which is further linked to its parent OrderGroup type (which can be something like a PurchaseOrder or a PaymentPlan). So if you do something with a Shipment instance like:
var orderGroup = shipment.Parent.Parent // get a reference to parent OrderGroup instance
Following this, if you have two separate customers making two separate orders, their respective shipments will belong to two different OrderForm instances that belong to their own OrderGroup.
Oh I see it now but I'm a little lost on a couple things. Is there a repository or service class that allows you to search for a shipment by only line item ids?
I've created a Google spreadsheet of some sample data.
https://docs.google.com/spreadsheet/ccc?key=0Arks5K5urmOidHZhUkFmNHliMmo0WVpWMWI3aGxtZUE&usp=sharing
I forgot to mention that when I look up the shipment based on line item ids, the customer context is unknown as well. I'm grabbing a message from a topic in ActiveMQ that only contains line item ids and the shipment tracking number.
I took a look at your Google spreadsheet and it looks like you also have the OrderGroupId available.
You can use the OrderContext class (Mediachase.Commerce.dll) to start off with something like this.
// set your values
int orderGroupId;
int orderFormId;
int shipmentId;
int lineitemId;
var purchaseOrder = OrderContext.Current.GetPurchaseOrderById(orderGroupId);
foreach(OrderForm orderForm in purchaseOrder.OrderForms)
{
if(orderForm.OrderFormId == orderFormId) // find the right OrderForm
{
foreach(Shipment shipment in orderForm.Shipments) // find the right Shipment
{
if(shipment.ShipmentId == shipmentId)
{
// use Shipment class static methods
// get collection of line items
var lineItems = Shipment.GetShipmentLineItems(shipment);
// get the quantity of a line item in a shipment
foreach(LineItem lineItem in lineItems)
{
var lineItemQuantity = Shipment.GetLineItemQuantity(shipment, lineItem.LineItemId);
}
}
break;
}
}
break;
}
EDIT: Added some static method examples from Shipment class.
EDIT: Code syntax highlighting.
Oh got it. So I do need to at least have an orderGroupId in order for this to get the shipment ID?
Yes, that's right. You would need the orderGroupId in the OrderContext API to easily get to the OrderGroup root object. There are other methods in OrderContext that can help you find and retrieve an order, but using the orderGroupId would be the most direct way.
Cool. Is there any other way if you don't have the OrderContext or any other forms of information except the line item ids and quantity of each line item id?
I suppose you could then look at the database tables and infer backward. If you know the line item id, you can see the OrderFormId and OrderGroupId values from the [LineItem] table row. You can also look at the [Shipment] table to see which shipments belong to a particular OrderFormId. However, it isn't recommended to do this and you should probably alter this design before you are stuck with this as the only option.
Okay. I've made the request to alter the design of the system we're integrating with.
Hey Jeff,
I have one more question. How can I search for a lineitem in the LineItem table by lineitemid?
Here's a sample of table data.
https://docs.google.com/spreadsheet/ccc?key=0Arks5K5urmOidE5nYXAtRFozaURsVXBRMFlackY3QXc&usp=sharing
You can use something like the sample code from the 11/27/13 post above.
In the innermost foreach loop just do a check for equality on the lineItemId value you have, like below.
// get the quantity of a line item in a shipment
foreach(LineItem lineItem in lineItems)
{
if (lineItem.LineItemId == lineitemId)
{
// do your stuff here, like get quantity of the line item in a shipment
var lineItemQuantity = Shipment.GetLineItemQuantity(shipment, lineItem.LineItemId);
}
}
If you want to see the line items associated at a higher level with an OrderForm instead of with the OrderForm's shipments, do something like below.
// set your values
int orderGroupId;
int orderFormId;
int lineitemId;
var purchaseOrder = OrderContext.Current.GetPurchaseOrderById(orderGroupId);
foreach(OrderForm orderForm in purchaseOrder.OrderForms)
{
if(orderForm.OrderFormId == orderFormId) // find the right OrderForm
{
// get the quantity of a line item in a shipment
foreach(LineItem lineItem in orderForm.LineItems)
{
if (lineItem.LineItemId == lineitemId)
{
// do your stuff with lineItem here
}
}
}
break;
}
Then I'm afraid I'd have the same answer as my 12/2/13 post. Namely, inferring backward from the sql table. If you know the line item id, you can see the OrderFormId and OrderGroupId values from the [LineItem] table row.
select OrderGroupId, OrderFormId
from LineItem
where LineItemId = 2 -- your id here
Then plug those values back into the api calls. OrderGroupId is the only one really needed though since there is usually only one OrderForm in an OrderGroup and you can find it using the api. Of course, if you can get this data in your feed in the first place, you'll save that additional round trip. :)
Oh. So there's no repository or service class I can use to retrieve the line item?
BTW, before I forget, all Commerce forum posts should be entered EPiServer Commerce forum, link here:
http://world.episerver.com/Modules/Forum/Pages/Forum.aspx?id=40089&epslanguage=en
I'm not sure if this thread can be moved, but I can check on that.
Regarding your other question about search, my colleague pointed out that there might be another way to do what you're looking to do based on this blog. Give it a quick read and let me know if that helps you. Especially item #6.
It works but I keep getting this exception:
+ _innerException {"Validation Error: Number 205 - 'Defective authorization total' \n"} System.Exception {System.Workflow.ComponentModel.Compiler.WorkflowValidationFailedException}
I think we'll need a little more context to solve that error. You should submit a support ticket.
I'm trying to update the shipment status on an order but I'm only given the line item id. How can I search commerce manager to update a shipment status on an order?
I see that there is a status on the OrderForm and Shipment table. Which is the correct table to update shipment status?