With Episerver Commerce, you can create an order several ways through the order system API. This topic describes the main components of the underlying mechanisms for order management.
Key classes
CartHelper
Note: This section explains how to work with shopping carts using the older APIs. Episerver recommends using the abstraction APIs to manage shopping carts, as described in Shopping carts.
The CartHelper class acts as a common helper class for managing carts and orders. The CartHelper class is usually sufficient for straightforward order placement scenarios. One advantage of the CartHelper class is that all required objects are automatically created in the background.
Example: adding an entry to a default cart via CartHelper
Entry MyEntry = CatalogContext.Current.GetCatalogEntry(
entryCode,
new CatalogEntryResponseGroup(CatalogEntryResponseGroup.ResponseGroup.CatalogEntryFull));
CartHelper MyHelper = new CartHelper(Cart.DefaultName);MyHelper.AddEntry(MyEntry);
The only additional required step is the transformation of a cart to a PurchaseOrder.
Cart and OrderForm
Working with the Cart class and OrderForm class directly provides increased options and added flexibility, as compared with CartHelper classes. The following example uses the OrderForm, Cart, PurchaseOrder, LineItem and related classes.
Creating an order
Note: This section explains how to work with orders using the older APIs. Episerver recommends using the abstraction APIs to manage orders, as described in Order processing.
The following example shows how to create an empty PurchaseOrder and store it in the database. The Cart class is used, and the SaveAsPurchaseOrder() method transforms the cart to a PurchaseOrder.
Example: creating an empty order
Cart TheCart = OrderContext.Current.GetCart("MinimalOrder",CustomerContext.Current.CurrentContactId);
PurchaseOrder purchaseOrder = TheCart.SaveAsPurchaseOrder();
purchaseOrder.AcceptChanges();
Entities like OrderForm, LineItem shipments and payments are still not in the order. The order is incomplete and cannot be properly loaded. While the CartHelper class takes care of this automatically, there are other ways of working with the order system.
Episerver Commerce can use several OrderForm instances in a single order. Most often, a single OrderForm is sufficient, but the platform supports a one-to-many relationship for most order system entities. The order API is therefore prepared for encountering several OrderForm instances in an order.
The code above creates a more or less empty OrderGroup database entry, with a name and a customer reference. The status is set to InProgress, which is correct. However, more steps must be taken for the order to be properly processed.
Adding OrderForm and shipment
The next example adds an OrderForm and the first Shipping so that you can add one or more LineItems.
Adding an order form
The following example opens up the order again and adds an OrderForm. The integer 114 is the OrderGroup primary key. An OrderGroup is like a bucket for a cart or an order.
Example: adding an order form
PurchaseOrder purchaseOrder = OrderContext.Current.GetPurchaseOrderById(114);
OrderForm orderForm = new OrderForm();
purchaseOrder.OrderForms.Add(orderForm);
purchaseOrder.AcceptChanges();
As previously stated, there can be several OrderForms in an OrderGroup, and most often a single OrderForm is not sufficient. In this case, customization is required.
The following image shows that you can access an order by the UI after adding an OrderForm. Under the Details tab, there is no way of adding LineItems, since the order process is closely tied to shipments. However, from an API and database point of view, there are no restrictions of any kind.
Adding a shipment
Example: adding a shipment to the order
Shipment TheShipment = new Shipment();
OrderForm orderForm = purchaseOrder.OrderForms[0];
ShippingMethodDto.ShippingMethodRow shippingMethodRow = null;
ShippingMethodDto allUsShipping = ShippingManager.GetShippingMethods("en-us");
foreach (ShippingMethodDto.ShippingMethodRow shipRow in allUsShipping.ShippingMethod)
{
if (shipRow.DisplayName == "Ground Shipping")
{
shippingMethodRow = shipRow;
}
}
TheShipment.ShippingMethodId = shippingMethodRow.ShippingMethodId;
TheShipment.ShippingMethodName = shippingMethodRow.DisplayName;
TheShipment.ShippingAddressId=
CustomerContext.Current.CurrentContact.PreferredShippingAddress.Name
TheShipment.ShipmentTotal = shippingMethodRow.BasePrice;
TheShipment.SetParent(orderForm);TheShipment.Status = OrderShipmentStatus.AwaitingInventory.ToString();
orderForm.Shipments.Add(TheShipment);
purchaseOrder.AcceptChanges();
You can add LineItems to the order from the user interface.
Under the Details tab, you can add LineItems to the order. However if the line is opened for editing, an exception such as this is thrown: Exception Details: System.NullReferenceException (there is nothing to edit). You can add LineItems via the UI and, when this is done, the line gets populated with values.
Note: Adding another shipment to a second OrderForm in the order does not show up, although it is added to the database and can be managed by the API. Also, the cost for additional shipping on additional OrderForms is not aggregated in the UI view by default.
Note: The Shipment Collection on the OrderForm class exposes methods like Add() and AddNew(), which are useful when adding shipments.
Adding line items to the order
You can add LineItems by the CartHelper (cart property) as shown above using the AddEntry(<Entry>) method. When you add the entry by CartHelper, everything else that is required is done in the background. But because you use a PurchaseOrder and not a Cart, this option is not available.
Note: The simplest way to add the entry is to use the Cart/CartHelper classes as far as possible. Then, at a certain point, convert the Cart to a PurchaseOrder for further coding.
Extending the order schema
To the LineItem class (LineItemEx) in MetaDataPlus, a custom field is added (LineItemExtraInfo) as a plain string field. This is done to differentiate properties between lines in an order, although it is the same SKU (not very commonly used). This option is not available if you use the CartHelper option.
Example: adding line items to an order
// Load what is needed
PurchaseOrder purchaseOrder = OrderContext.Current.GetPurchaseOrderById(114);
OrderForm orderForm = purchaseOrder.OrderForms[0];
Shipment orderFormShipment = orderForm.Shipments[0];
// Get the Entry
Entry myEntry = CatalogContext.Current.GetCatalogEntry(
"049383208344",
new CatalogEntryResponseGroup(CatalogEntryResponseGroup.ResponseGroup.CatalogEntryFull));
Price price = StoreHelper.GetDiscountPrice(myEntry);
// First LineItem
LineItem MyLineItem = new LineItem();
MyLineItem.DisplayName = myEntry.Name;
MyLineItem.CatalogEntryId = myEntry.ID;
MyLineItem.Quantity = 1;
MyLineItem.PlacedPrice = price.Amount;
// Second LineItem
LineItem MyOtherLineItem = new LineItem();
MyOtherLineItem.CatalogEntryId = myEntry.ID;
MyOtherLineItem.DisplayName = myEntry.Name;
MyOtherLineItem.Quantity = 1;
MyOtherLineItem.PlacedPrice = price.Amount;
orderForm.LineItems.Add(MyLineItem);
// To have two lineItems for the same SKU, set the bool LineItemRollup (second arg.) to false
orderForm.LineItems.Add(MyOtherLineItem, false);
// Set it true and you will have the count of 2, for one single LineItem
// The added custom MetaField (class LineItemEx) filled up
orderForm.LineItems[0]["LineItemExtraInfo"] = "Item 1, nothing special";
orderForm.LineItems[1]["LineItemExtraInfo"] = "Item 2, very special";
// The following lines are needed to make the LineItems display
// The shipping is closely tied to UI, but not related in such a way in the API
PurchaseOrderManager.AddLineItemToShipment(
purchaseOrder,
orderForm.LineItems[0].LineItemId,
orderFormShipment,
1);
PurchaseOrderManager.AddLineItemToShipment(
purchaseOrder,
orderForm.LineItems[1].LineItemId,
orderFormShipment,
1);
purchaseOrder.OrderForms[0].Shipments.AcceptChanges();
orderForm.AcceptChanges();
You can add LineItems to an order without adding them to a shipment because orders are subject to change often and not immediately executed.
Adding payments
After you create a basic purchase order, add payment and shipment information. Exactly how this is done varies greatly depending on the site and integrations with other systems.
Payments
You can execute payment in many ways. The following example shows generic code used to associate a payment with the previously-created order.
Example: adding a payment
PurchaseOrder purchaseOrder = OrderContext.Current.GetPurchaseOrderById(114);
OrderForm orderForm = purchaseOrder.OrderForms[0];
// using only one here
Payment ThePayment = null; // This is one is abstract, not an instantiable "PaymentMethod"
PaymentMethodDto paymentMethodDto = PaymentManager.GetPaymentMethodBySystemName
("Generic", "en-us");
Decimal amount = 0M;
foreach (LineItem l in orderForm.LineItems)
{
amount += l.PlacedPrice;
}
OtherPayment otherPayment = new OtherPayment(); // the "real" payment-Type in MetaDataPlus
ThePayment = (Payment)otherPayment;
ThePayment.BillingAddressId = CustomerContext.Current.CurrentContact.PreferredBillingAddressId.ToString();
ThePayment.PaymentMethodName = paymentMethodDto.PaymentMethod[0].Name;
ThePayment.PaymentMethodId = paymentMethodDto.PaymentMethod[0].PaymentMethodId;
ThePayment.Amount = amount;
ThePayment.TransactionType = TransactionType.Sale.ToString();
ThePayment.Status = PaymentStatus.Pending.ToString();
ThePayment.SetParent(orderForm);
ThePayment.AcceptChanges();
orderForm.Payments.Add(ThePayment);
orderForm.AcceptChanges();
Split shipments
As Episerver Commerce has a one-to-many relation between most objects in the Order subsystem, LineItems of an order shipment can be split to a new shipment introduced in the order. This displays a message informing that overall totals do not match if a payment already has been created and managed in the UI.
If an additional shipment is added, an extra fee has to be paid. The order processing workflows detect that the payments do not match the total amount.
Example: splitting shipments
Shipment existingShipment = purchaseOrder.OrderForms[0].Shipments[0];
//single OrderFormShipment newShipment = purchaseOrder.OrderForms[0].Shipments.AddNew();
PurchaseOrderManager.MoveLineItemToShipment(purchaseOrder,
purchaseOrder.OrderForms[0].LineItems[1].LineItemId,
existingShipment,
newShipment,
1);
existingShipment.AcceptChanges();
newShipment.AcceptChanges();
Split payments
Split payments are supported. Add the payments in the standard way and keep track of totals if payments are added by the API, as these properties are read.
Address information
The Order Management subsystem relies on MetaDataPlus as the metadata engine and for database storage. On the other hand, the Customer Management subsystem relies on Business Foundation (BF) for metadata, storage and definitions. This might lead to differences between the OrderAddress class in MetaDataPlus and the Address class in BF. The latter has a many-to-one relation in the CustomerContact class (a customer) but no direct relation or link to MetaDataPlus and OrderAddress. Depending on how the platform is used, order addresses can be useful.
A ConvertToOrderAddress method exists at the StoreHelper class to help convert a customer address to an OrderAddress.
Key classes
Note: This section explains how to work with shopping carts using the older APIs. Episerver recommends using the abstraction APIs to manage shopping carts, as described in Shopping carts.
- OrderAddresses in a PurchaseOrder belong to the OrderGroup and are contained in the OrderAddressCollection.
- The public OrderAddress class inherits OrderStorageBase that, in turn, inherits the public abstract OrderStorageBase class, which in turn inherits the public abstract MetaStorageBase class. The MetaStorageBase class inherits the public MetaObject class in the Mediachase.MetaDataPlus.dll library.
- The definition of an OrderAddress is managed in MetaDataPlus (Administration subsystem) at the OrderGroupAddressEx class definition of structure.
- OrderAddresses are separated from the address for a customer, where BF is used to define address structure. The public CustomerAddress class inherits AddressEntity and is the set of properties of a customer address.
- The public AddressEntity class inherits EntityObject in the Mediachase.BusinessFoundation.Data.Business namespace.
- There is also a public Address class, but this is related to payments in the Mediachase.Commerce.Plugins.Payment.Authorize namespace.
- The StoreHelper.ConvertToOrderAddress method is used for the transformation.
Example: order addresses and display
OrderAddress orderShipping = StoreHelper.ConvertToOrderAddress(
CustomerContext.Current.CurrentContact.PreferredShippingAddress);
purchaseOrder.OrderAddresses.Add(orderShipping);
OrderAddress orderBilling = StoreHelper.ConvertToOrderAddress(
CustomerContext.Current.CurrentContact.PreferredBillingAddress);
purchaseOrder.OrderAddresses.Add(orderBilling);
purchaseOrder.OrderAddresses.AcceptChanges();
// This is an "extra field" in the OrderGroup table (UI related)
purchaseOrder.CustomerName = CustomerContext.Current.CurrentContactName;
// Name of the orderform
purchaseOrder.OrderForms[0].Name = "FirstOrderForm";
// Displaying the right billing address
purchaseOrder.OrderForms[0].BillingAddressId = orderBilling.Name;
purchaseOrder.OrderForms.AcceptChanges();
purchaseOrder.AcceptChanges();
Last updated: Apr 01, 2021