Transitioning from Sitecore Experience Commerce to OrderCloud: Orders

Reading Time: 10 minutes

In this article, we will review and compare orders and related features between Sitecore Experience Commerce and OrderCloud to facilitate developers looking to transition from XC to OrderCloud as well as identify a path for migration of existing XC solutions to OrderCloud.

We will look at a high-level comparison of architecture, functionality, and data models, with greater focus on transitioning from Sitecore Experience Commerce, meaning identifying a close path to parity in OrderCloud, using the Habitat catalog and SXA Storefront.

Conceptual Architecture and Features

Journalling (Auditing)

The journalling functionality in XC takes a snapshot of an entity when it is persisted as a means of effectively producing a change log against the entity for auditing and historical purposes. The entities are registered to the commerce role configuration under the EntityJournalingPolicy, and only the order entity is registered to the policy as part of the habitat role configurations by default.

While OrderCloud does not currently expose its logs via the API, two approaches for implementing audit logs are webhooks and middleware proxy requests, both of which require a custom database to store the audit snapshots.

The Webhook Approach

While webhooks may turn out to be sufficient for your auditing requirements, there are considerations to be aware of to ensure that you don’t get caught out with these gotchas.

Regardless if you choose to use pre-hooks or post-hooks, OrderCloud may touch multiple entities within a single request that could impact the completeness of the implementation, leading to the inability for complete reconciliation and may be mistaken as data corruption.

The post-hook webhooks will forward on OrderCloud’s response and user token to the webhooks url (typically the middleware), The user token can be parsed to retrieve information about the user making the request.

Note: Assignment requests return a response code of 204 and an empty body. The assignment model will not be available in these requests.

The pre-hook webhooks on the other hand, capture the request body, however creating a log at this point would be premature as the OrderCloud API will typically rollback or not commit changes until the request has been fully validated and processed. Pre-hooks are also a blocking process, which will lead to performance impacts on your solution.

The Middleware Proxy Approach

Using middleware as a proxy for all OrderCloud requests that will have auditing attached may provide the most complete approach but may also come with the most complexity and performance implications.

The proxy request in middleware doesn’t mean you are limited to only calling the OrderCloud API in question, but can provide the opportunity to retrieve the entity prior to making changes, and then retreiving the entity after changes have been made for endpoints such as any PATCH endpoint, which doesn’t return the entity in the response. While this may seem great, you will be facing performance implications, which can only be managed through asynchronous calls to an extent. It is also possible to cater for multiple entities being touched in OrderCloud with this approach, but again will have performance implications.

Entitlements

In XC, entitlements can be considered as a product classification that typically represents a sellable item as a trackable unit of ownership or license, which may change from customer or ambient activity, such as redemptions or expiry. Upon purchase of a sellable item with an entitlement classification, the Commerce Engine will create an specialised entitlement commerce entity, which can then be further customised for extended functionality. for the spei include gift cards, installations, warrranties, services and subscriptions.

Figure 1: Entitlement creation from order.

The entitlements are created with entity references to the order and the customer entities, while the order and customer entities are also updated with entity references back to the entitlements to form two-way relationships.

Figure 2: Entitlement architecture in XC.

As OrderCloud does not have any native support for entitlements, we are still able to support this feature via customisation with considerations in mind. Most importantly, xp for any given entity is limited to 8000 bytes, therefore storing all entitlement data directly on the buyer user’s xp could see the character limit be exceeded over time as more entitlements are created for the buyer user. Instead, a third-party database, such as Cosmos DB, can be utilised to store the entitlement data, while only storing the reference id of the entitlement record against the buyer user’s xp, minimising the required xp storage.

Figure 3: Entitlement architecture in an OrderCloud solution.

Shipments

In Fulfillment to Shipping, we covered the fulfillment components and shipping estimates that are applied to orders to gather fulfillment information during the storefront checkout delivery step. Post order-placement, the actual fulfillment of the order may or may not be in alignment with representation of fulfillment in the storefront and can be controlled via customisation. For example, the shipping details may have been collected for the entire order and a flat fee charged, however business processes may see the order be fulfilled across multiple shipments. Any variance to actual shipping costs from the estimates would typically not be reconciled with the customer.

XC provides the shipment commerce entity and, as part of the Released Orders Minion, will generate a shipment for the order or for each line item, respective to whether cart-level or cart line-level fulfillments were applied to the cart during the checkout steps.

Figure 4: XC shipments for cart-level and cart line-level fulfillments.

In OrderCloud, shipment flexibility is increased as shipments are not bound to the ship estimates, like XC’s shipments to physical fulfillments. Shipments can be created against the entire order or a subset of line items with support for partial quantity fulfillment.

Figure 5: OrderCloud shipments for order-level and line-level fulfillments do not have to align to ship estimates.

Return Merchandise Authorisation (RMA)

RMAs in the Commerce Engine are another partial implementation to get faciliate development by providing the foundation. This is commonly an area of customisation to allow RMAs to fit with the business processes, e.g. restricting returns on certain items, calculating refunds, determining whether the inventory needs to be incremented on a return, etc. The RMA functionality allows customer service representatives to raise a return request, with a line item, quantity, and reason for return, and the RMA will be created with an approval process. The remaining functionality is introduced via customisation.

In OrderCloud, order returns are a recently released feature that has more complete functionality than XC’s RMA offering. A return can be created against multiple line items, which also consists of an approval workflow and custom refund calculations can be achieved via the OrderReturn integration event.

Functionality

The Storefront Checkout Order Submission Step

In XC, submitting an order is not just about converting a cart to an order, but performing necessary processes such as payment authorisation and validating the order is in a valid state prior to finalising the order. The following sections cover these processes that we would look to transfer into an OrderCloud implementation.

Turning our attention to the storefront functionality of the order confirmation checkout step from Carts to Unsubmitted Orders: Storefront User Journey, we will look at adding the email address and payment details to the cart/unsubmitted order, focusing on varying APIs of the two platforms.

Figure 6: The order submission aspect of the storefront user journey.

When submitting an order in XC, the Commerce Engine will perform validations and processes that accompany order creation, which is depicted at a high level in figure 7. The darker nodes represent functionality that is built into OrderCloud’s order submit endpoint, while the nodes in italics represent functionality we wouldn’t need to bring over to OrderCloud. This leaves the Authorise Payment and Assign Confirmation ID as the custom functionality we would need to incorporate into an OrderCloud solution.

Figure 7: A comparison of XC’s create order process and an equivalent approach in OrderCloud.
Order Validation

When submitting orders in XC, the Commerce Engine ensures the cart is in a valid state prior to converting it to an order. The validation can be tightened through customisation for business specific validation requirements, but for the default validation rules, the Commerce Engine will evaluate the following:

  • The payments total matches the cart’s grand total.
  • An email is assigned to the cart.
  • The cart has line items.
  • Applied coupons for promotions are eligible.
  • Applied promotions are eligible.
  • Inventory availability is not exceeded.

Similarly, OrderCloud’s submit order endpoint validates an order with the following:

  • The order has line items.
  • The order wasn’t already submitted.
  • The order has been calculated via integration event, if applicable.
  • All payments have been accepted.
  • Applied promotions are eligible.
  • Inventory availability is not exceeded.

The difference validate gaps between the two platforms that we will need to implement for parity:

  • The payments total matches the order’s grand total.
  • An email is assigned to the order.

For more details on the internal logic of OrderCloud’s order submit process, including validation, see Order Submit Logic.

Payment Capture
Credit Cards

The SXA storefront provides an auth-capture implementation for braintree payments where upon order submission the payment is authorised and during the released order minion the payment is captured.

Figure 8: High level credit card payment flow in XC.

In OrderCloud, the payments resource represents credit card payments and validation is independent of the order creation and validation processes. In figure 10, we see that the authorise payment is incorporated as part of the middleware’s order creation process.

Gift Cards

For the gift card implementation, the gift card amount is captured post order placement, during the pending orders minion.

XC’s gift card implementation is only intended as a sample, which would typically be replaced with an integration with a gift card provider, so we typically wouldn’t need to focus so much on the default XC implementation and instead focus on porting the custom integration.

Figure 9: High level gift card payment flow in XC.

For gift cards, in the previous article, Taxes and Payments, we identified that the gift cards were represented as spending accounts in OrderCloud.

Order Confirmation Id

The order’s OrderConfirmationId is generated as part of order creation, however this is commonly overwritten with an order number incrementor implementation for a user-friendly confirmation id or an external system (e.g. OMS) identifier for reconciliation purposes.

In OrderCloud, generating unique order IDs is a native feature. While entity IDs are generated with a unique alphanumeric characters, OrderCloud’s incrementors resource can be configured and referenced when explicitly assigning an entity ID. This is achieved by wrapping the incrementor ID in curly braces, which also supports prefixed and suffixed values for even further versatility.

// Request Body
{
  "ID": "MyPrefix-{MyOrderIncrementorID}"
}

// Response Body
{
  "ID": "MyPrefix-010010",
  ...
  "IsSubmitted": false,
  "xp": null
}

A few consideration when it comes to the order confirmation IDs:

  • In a multi-tenant solution, you can create a unique incrementor, which may create the same incrementor value, e.g. 010010, however when assigning an order ID, a unique storefront prefix can prevent duplicate IDs from being created, e.g. <Storefront 1>-{Storefront1OrderIncrementorID}.
  • If your solution cannot guarantee unique IDs for orders across the Marketplace, the ID can be stored on the order’s xp instead.
Order Submission by Middleware Proxy

In reviewing the order submission process between XC and OrderCloud, it’s important to understand that XC development methodology differs from OrderCloud in that XC allows the implementor to extend and modify the Commerce Engine directly, while OrderCloud opts to leave customisation to the implementor by way of middleware to supplement and enhance the platforms native behaviour. We will generally be able to achieve close parity when it comes to the happy paths, while unhappy paths will rely on the middleware to perform the order submit request along with surrounding custom logic.

In OrderCloud, we have the ability to extend platform functionality prior to execution by using middleware as a proxy, wrapping the OrderCloud API endpoints, or creating pre-hook webhooks to intercept an API request. Both approaches have merit and will typically be a design and implementation decision.

For more information on extending OrderCloud, see Flexible Fullfilment – Extending OrderCloud.

In our happy path only example, figure 10 provides an OrderCloud solution equivalent of XC’s create order process, by wrapping OrderCloud’s order submit and several other related endpoints, encompassing all aspects of the order submission process.

  1. By calling the order validate endpoint, we ensure that no stale data, in particular order calculations, are being retrieved from the cache of other API calls we will make, such as getting the order worksheet.
  2. The order worksheet is retrieved to perform custom validation.
  3. Payments are not stored on the order worksheet at this time, so it is necessary to retrieve payments separate to the order.
  4. For our custom validation:
    1. We identified in Tax and Payments that XC has an order-specific email, which may not be the user’s address, so we would need to confirm that the order-specific email has been provided on the order.
    2. We also acknowledged earlier that XC validates the cart’s payments total against the grand total, so this is another custom validation we can implement in our OrderCloud solution.
  5. While XC doesn’t create a sales activity until the post-order processing stage, OrderCloud transactions are required to keep track of payment activity.
  6. Patching the order request covers:
    1. Updating the order confirmation id with our incrementor id. It is possible that the order’s ID is generated upon initial creation instead of during the order creation process, however this will see submitted orders not necessarily coming in in a linear fashion and potentially many generated IDs being discarded due to order abandonment.
    2. Applying the initial custom order status, which we will visit in a later article.
Figure 10: An example approach for XC create order equivalent in an OrderCloud solution.

References

Continue the Series

  1. Transitioning from Sitecore Experience Commerce to OrderCloud: Customers to Buyer Users
  2. Transitioning from Sitecore Experience Commerce to OrderCloud: Customers and Buyers – API Access
  3. Transitioning from Sitecore Experience Commerce to OrderCloud: Catalogs and Categories
  4. Transitioning from Sitecore Experience Commerce to OrderCloud: Sellable Items To Products
  5. Transitioning from Sitecore Experience Commerce to OrderCloud: Inventory and Pricing
  6. Transitioning from Sitecore Experience Commerce to OrderCloud: Carts to Unsubmitted Orders and Carts
  7. Transitioning from Sitecore Experience Commerce to OrderCloud: Fulfillments to Shipping
  8. Transitioning from Sitecore Experience Commerce to OrderCloud: Tax and Payments
  9. Transitioning from Sitecore Experience Commerce to OrderCloud: Orders
  10. Transitioning from Sitecore Experience Commerce to OrderCloud: Order Workflow and Minions
  11. Transitioning from Sitecore Experience Commerce to OrderCloud: Promotions

OrderCloud: Order Submit Logic

Reading Time: 4 minutes

In this article, we will review the business logic that Sitecore OrderCloud utilises when the order submit endpoint (POST /orders/{direction}/{orderID}/submit) is executed for a better checkout design, implementation and error handling.

Introduction

When it comes to submitting an order that is either incomplete or has become invalid, the OrderCloud API provides fairly easy to understand error codes to facilitate troubleshooting. What is not provided however, is a complete list of error codes that one may expect from an endpoint in order to be proactive in catering for such situations.

While a global catch all may be the quickest and easiest way to approach order submission error handling, the user experience of being informed that “A technical difficulty has occurred. Please contact support” in your storefront’s checkout will likely have a negative impact on the conversion rate. In addition, the admin application tends to be neglected with the love required to support business users in identifying and rectifying the issues themselves, so more often than not it is the developer to the rescue, but there are ways we can prevent these scenarios from occurring.

As OrderCloud is always utilising the latest code base, it is possible that the documented processes can change over time without warning.

Order Submission Logic

The following sections covering order submission logic, will highlight most of the platform logic, but will omit more trivial details.

When an exception is thrown by the API, denoted by the red End terminators with given error code, any pending changes to the underlying database entities are rolled back.

Order Submit

The order submit logic is essentially the entry point for validating and processing a submitted order.

Figure 1: Order submit logic.

Validate Order

The validate order process not only used in the order submit process, but is the entire validation taking place for the order validate endpoints.

In figure 2, soft errors, represented by the red boxes, allows the API to validate other areas of the order and return errors in list form, which can see your overall solution being more performant with less back and forth to resolve all order validation errors.

The Deduct inventory sub-process will validate inventory is available for the order, but does not persist inventory changes as this can be found in figure 1, under Commit inventory.

Figure 2: Validate order logic.

Validate Buyer-Seller Relationship

The buyer-seller relationship effectively validates that the user is authorised to purchase an order directly from a supplier. The relationship can be implicit, i.e. the supplier can be configured with AllBuyerCanOrder set as true or the user is an admin user, or an explicit relationship between the buyer and supplier has been defined.

Figure 3: Validate buyer-seller relationship logic.

Validate Promotions

The validate promotions logic in figure 4 shows the relevant flow for the order submit process.

Figure 4: Validate promotions logic.

Validate Promotion

For each promotion applied to the order, it is validated during the order submit to ensure it is still eligible. Any and all errors found are added to the error list, which will be returned by the API as part of the validate order logic in figure 2.

Figure 5: Validate promotion logic.

Deduct Line Item Inventory

Order inventory will be validated to ensure that there is sufficient inventory for all order lines. Any and all errors found are added to the error list, which will be returned by the API as part of the validate order logic in figure 2.

Figure 6: Deduct line item inventory logic.

Validate and Adjust Spending Account

For the sub-process Validate and adjust spending accounts in figure 1, the following flowchart represents how each spending account is evaluated as they are iterated over for all applied payments for the order.

Figure 7: Validate and adjust spending account logic.

Summary

With this insight into the order submit processes, you should now be well equipped to identify how extending OrderCloud order submit functionality through webhooks, wrapped API calls, and integration events fit in with your solutions, as well as anticipate the many errors that can occur and be more proactive with more specific error handling over a global catch all implementation.

References

Transitioning from Sitecore Experience Commerce to OrderCloud: Carts to Unsubmitted Orders and Carts

Reading Time: 11 minutes

In this article, we will review and compare Sitecore Experience Commerce carts and OrderCloud unsubmitted orders to facilitate developers looking to transition from XC to OrderCloud as well as identify a path for migration of existing XC solutions to OrderCloud.

We will look at a high-level comparison of architecture, functionality, and data models, with greater focus on transitioning from Sitecore Experience Commerce, meaning identifying a close path to parity in OrderCloud, using the Habitat catalog and SXA Storefront.

Conceptual Architecture and Features

Carts vs Unsubmitted Orders and Carts

Architecture Overview

In XC, the cart entity stores information about the order to be placed. When an order is placed, the contents of a cart is copied over into a new order entity with an initial order status of Pending.

OrderCloud has two approaches in handling the cart/order lifecycle. The solution can leverage unsubmitted orders or the OrderCloud’s cart resource.

Unsubmitted Orders

In OrderCloud, an order represents both the cart and order within the cart/order lifecycle, using the Status property to identify its current state. The initial status of the order is Unsubmitted, which is effectively the XC cart equivalent. When the order is submitted, the order will then transition its status to Open, matching the XC order with status of Pending.

It is also possible that the OrderCloud order submitted status will be set to AwaitingApproval, however this status is not relevant for the SXA storefront comparison.

Figure 1: The XC and OrderCloud representations of a shopping cart being constructed and their initial state once the order has been submitted.

Referencing figure 2, there are a few important details regarding the XC and SXA cart to order architecture in relation to OrderCloud’s architecture:

  1. SXA sees that a customer only interacts with a single cart per storefront, by using the naming convention, “Default<customer id><storefront name>“, to identify the cart id and is the only means of a relationship that the customer has with the cart in the XC database.
  2. The Commerce Engine’s /carts endpoint, which retrieves the cart, requires the cartId parameter and does not have consideration for the user context.
  3. While order ids must be unique, orders are created with randomly generated ids and are not copied from the cart’s fixed id. Orders are persisted, accompanied with a list entry, which represent the relationship between the customer and the orders they have placed.
  4. During order creation, after the cart contents has been copied to the order, the cart is emptied to allow it to be used for subsequent order placement.

In comparison to OrderCloud’s architecture, orders are tightly coupled to their owners via the FromUserID property, and as both order ids in XC and OrderCloud are generated, there is no chance for Id conflicts.

  1. In managing a single order, the most recent unsubmitted order is considered the active ‘cart’ and as such does not need to rely on an order id to identify the cart as its relationship to the user as it’s tightly coupled to the owner via the FromUserID property.
  2. The /me/orders endpoint retrieves the order based on user context, which is extracted from the auth token, rather than the order id.
  3. The order id is also generated by default, so there’s no chance of an ID conflict when allowing the platform to generate it.
  4. There is no implementation concerns around migrating a cart to an order.
Figure 2: The SXA storefront will use a single cart per customer, which is converted into each order, whereas OrderCloud creates the orders directly allowing multiple unsubmitted orders being active simultaneously.
OrderCloud Carts

As an alternate approach to tracking and utilising the unsubmitted orders in an implementation, OrderCloud also provides a cart resource, which can simplify the implementation as it behaves much the same as the orders resource without the need for identifying, persisting, and passing the order ID for requests. Instead, the cart resource will point to the latest unsubmitted orders or create a new order if no unsubmitted orders exist for the user. When the cart is submitted, the next unsubmitted order, if available, will become the active cart.

More information can be found in Introducing the Cart API.

Choosing Between Orders and Cart Implementations

When deciding whether to use the orders or cart resource in an implementation, the cart resource will typically be the preferred implementation practice for the majority of storefronts as it can handle a single cart or multiple carts for profiled users, allowing the implementation to switch the order context for the user.

For marketplaces requiring multiple storefronts, e.g. a storefront per brand or storefront per country, where user accounts are shared across storefronts, the orders resource allows the storefront to control the order context for each user as seen in figure 4.

Figure 3: In a multi-storefront, shared user scenario, the cart resource’s order context is dependent on the user.
Figure 4: In a multi-storefront, shared user scenario, the order resource allows the implementation to control the order context.

Storefront User Journey

In figure 5, we see a high-level user journey for the SXA storefront alongside the OrderCloud equivalent. While OrderCloud’s user journey appears to be longer, this is only due to order requiring explicit creation and XC consolidating shipping address and shipping method into fulfillment details, and billing address and payment into payment details. Ultimately, the user journey is matched and there are no sacrifices necessary.

Figure 5: Cart to order vs unsubmitted order to submitted order user journey comparison.

Viewing the Cart/Order

In XC, the GET /Carts({cartId}) endpoint is typically called with the $expand query parameter to retrieve all cart components, cart lines and their components for the full view of the cart.

In OrderCloud, the RESTful API provides individual endpoints for extracting order data at more granular levels. For example the order details can be retrieved via the GET /orders/{direction}/{orderID} or GET /cart endpoint, but it won’t provide the line item details, which can be retrieved in bulk using GET /orders/{direction}/{orderID}/lineitems or GET /cart/lineitems, or individually via GET /orders/{direction}/{orderID}/lineitems/{lineitemID} or GET /cart/lineitems/{lineitemID}.

Alternately, OrderCloud also provides GET /orders/{direction}/{orderID}/worksheet and GET /cart/worksheet endpoints, which return the order object along with all line items and all responses from integration events. providing a more complete view, however some objects such as price schedule details will still require additional requests from the appropriate resources, which are typically only retrieved on demand as required.

Cart vs Order Totals

In the table below, we see a comparison between the cart line totals side by side with the equivalent line item totals. The notable differences are as follows:

  • XC Adjustments may include taxes, fulfillment fees, and promotion discounts, whereas OrderCloud breaks out the each adjustment classification at the order level.
  • As calculations with adjustments are calculated differently at the line-level and cart/order level, calculating the GrandTotal/Total may result in differently between the platforms.
  • The PaymentsTotal is not available on the order, however the payments total can be retrieved and calculated by the middleware, using OrderCloud’s GET /orders/{direction}/{orderID}/payments or GET /cart/payments endpoint.
Cart (XC)CalculationOrder (OC)CalculationMatch
SubTotalSum of all line-level SubTotals.SubtotalSum of all LineItem.LineSubtotals.Yes
AdjustmentsTotalSum of all cart-level Adjustments.ShippingCostHandled by middleware via OrderCalculate integration event.No
TaxCostHandled by middleware via OrderCalculate integration event.No
PromotionDiscountSum of all order-level and line item-level promotion discount amounts applied.No
GrandTotalSubTotal + sum of all cart-level adjustments flagged with IncludeInGrandTotal being true + sum of all line-level AdjustmentsTotalsTotalSubtotal + TaxCost + ShippingCostPromotionDiscount.No
PaymentsTotalSum of all PaymentComponent Amounts.N/A

Cart Lines vs Line Items

Adding a Cart Line vs Line Item

From an API perspective, to add a cart lines in XC, the request requires the cartId, quantity, and itemId, consisting of catalogId, sellableItemId, and variationId.

The catalogId is intended to resolve pricing and promotions for sellable items and their variants, based on the catalog associations to price books and promotion books as per the XC architecture.

POST https://authoring.engine.dev/api/AddCartLine HTTP/1.1

{
  "cartId": "{orderId}",
  "itemId": "{catalogId}|{sellableItemId}|{variantId}",
  "quantity": 1
}

Adding a line item in OrderCloud is much the same as seen in the example request below. orderId parameter replaces the cartId, while the ProductId equates to the sellableItemId. The catalogId however, is not required as pricing and promotions are not dependent on catalog assignments, and rather than passing the variantId, the individual product specs with OptionIDs are specified, ultimately resolving to variant equivalent, but also caters for specialised open text specs that OrderCloud supports.

POST https://sandboxapi.ordercloud.io/v1/orders/outgoing/{orderId}/lineitems HTTP/1.1

{
  "ProductID": "{productId}",
  "Specs": [
    {
      "SpecID": "{specId}",
      "OptionID": "{specOptionId}"
    },
    {
      "SpecID": "{specId2}",
      "OptionID": "{specOptionId2}"
    }
  ]
}

Cart Line Rollup

In the SXA storefront, when adding a sellable item/variation to the cart that has already been added, the quantity will be updated to reflect the initial cart’s quantity plus the quantity added. This is known as cart line rollup and is controlled by the RollupCartLinesPolicy.Rollup property in the Commerce Engine’s environment role configuration.

Figure 6: Add cart line functionality.

As line rollup is not a native feature of OrderCloud’s RESTful API, this functionality could be implemented in the middleware. Instead adding line items will create a new line item in the order, in the same manner that XC will with rollup disabled.

Figure 7: Create line item functionality.

Line-Level Pricing

XC’s cart lines contain the UnitListPrice and the SellPrice on the PurchaseOptionMoneyPolicy, which can be thought of as “was” and “now” pricing respectively.

The SellPrice can evaluate to the same value as the UnitListPrice, to which the SXA storefront will treat this as a standard price.

"Policies": [
    {
        "@odata.type": "#Sitecore.Commerce.Plugin.Pricing.PurchaseOptionMoneyPolicy",
        "PolicyId": "5e6c1f42c7c94bc4b79696e716b6cda5",
        "Models": [],
        "Expires": "2019-04-22T13:13:18.8117816Z",
        "SellPrice": {
            "CurrencyCode": "USD",
            "Amount": 6
        },
        "FixedSellPrice": false
    }
],
"UnitListPrice": {
    "CurrencyCode": "USD",
    "Amount": 2429.99
}

The MessagesComponent, also shows the audit trail detailing how the cart line’s unit list price and sell price were resolved, which are evaluated from ListPrices and Price Card Snapshots at the sellable item and item variation levels.

Cart Line MessageComponent Snippet

{
	"@odata.type": "#Sitecore.Commerce.Core.MessagesComponent",
	"Id": "7e6bc27965ef4f3a954e6fcdadb96fa5",
	"Name": "",
	"Comments": "",
	"Policies": [],
	"Messages": [
		{
			"Code": "Pricing",
			"Text": "SellPrice<=PriceCard.Snapshot: Price=$10.00|Qty=1.0|PriceCard=Habitat_PriceCard"
		},
		{
			"Code": "Pricing",
			"Text": "ListPrice<=PricingPolicy: Price=$1,919.69"
		},
		{
			"Code": "Pricing",
			"Text": "Variation.SellPrice<=Variation.PriceCard.Snapshot: Price=$9.00|Qty=1.0|Variation=56042567|PriceCard=Habitat_VariantsPriceCard"
		},
		{
			"Code": "Pricing",
			"Text": "Variation.ListPrice<=Variation.PricePolicy: Variation=56042567|Price=$2,429.99"
		},
		{
			"Code": "Pricing",
			"Text": "CartItem.SellPrice<=PriceCard.ActiveSnapshot: Price=$6.00|Qty=5.0"
		},
		{
			"Code": "Pricing",
			"Text": "CartItem.ListPrice<=SellableItem.Variation.ListPrice: Price=$2,429.99"
		}
	],
	"ChildComponents": []
}

OrderCloud stores the resolved UnitPrice on the line item from the respective PriceBreak, prioritising the SalePrice over Price.

For orders in an unsubmitted state, the /products/ resource may be used to retrieve the product information will return the the current active snapshot, however once the order has been placed the product and price schedules resources are no longer considered the source of truth. If price schedule information, e.g. price breaks, sale start/end dates, etc. is required for historical and reconciliation purposes, a webhook can be created on the order submit endpoint to persist the information on the line items’ xp.

The other aspect of note is that the Currency is set at the order level, which applies to all monetary values on the order and line items.

"Order": {
  "ID": "MyOrder"
  ...
  "Currency": "USD"
  ...
},
"LineItems": [
  {
    "ID": "MyLineItem",
    ...
    "UnitPrice": 2429.99,
    ...
  }
]

Cart Line vs Line Item Totals

In the table below, we see a comparison between the cart line totals side by side with the equivalent line item totals. The notable differences are as follows:

  • XC Adjustments may include taxes, fulfillment fees, and promotion discounts, whereas OrderCloud only includes PromotionDiscount. Extended properties (xp) can be leveraged to persist taxes and fulfillments.
  • XC inverts the promotion discount adjustments so a $20 discount will be represented as -$20 in XC, so calculation of the GrandTotal is valid.
  • XC’s GrandTotal effectively matches the OrderCloud’s LineTotal as taxes and fulfillment fees aren’t flagged with IncludeInGrandTotal.
CartLine (XC)CalculationLineItem (OC)CalculationMatch
SubTotalSellPrice * QuantityLineSubtotalUnitPrice * QuantityYes
AdjustmentsTotalSum of all line-level Adjustments.PromotionDiscountSum of all line item-level promotion discount amounts applied for the current line item.No
GrandTotalSubTotal + sum of line-level adjustments, where adjustments are flagged with IncludeInGrandTotal being true.LineTotalLineSubtotalPromotionDiscountYes
PaymentsTotalPayments not considered at line-level without customisation.N/A

Extending the Cart and Cart Lines vs Order and Line Items

Those familiar with the SXA storefront and the Commerce Engine will be pleased with the significantly reduced development efforts involved in extending the order and order line items. Gone are the days of the plumbing extended strongly-typed model classes through several application layers in strongly-typed classes and dependency injection. And that’s just the storefront. The Commerce Engine would typically require creating new endpoints and replacing several more classes.

OrderCloud’s extended properties (xp) is not only a dynamically built object in OrderCloud, but the OrderCloud .NET and JavaScript SDKs also represent xp as a dynamic type, meaning you can write to a new property within the xp in one class and read from it in another class without any plumbing.

var order = new Order();
order.xp.MyNewProperty = "My New Property Value";

var lineitem = new LineItem();
line.xp.MyLineItemProperty = true;

Merge Carts vs Transfer Order

A typical ecommerce feature is retaining a cart/unsubmitted order of a guest/anonymous user as they log in or register to the storefront. In the SXA storefront, the register component will essentially transfer ownership to the newly registered user, while the login component will merge the anonymous cart with registered user’s cart, if it exists, by copying across all cart lines. If Cart Line Rollup is enabled, quantities will be updated where appropriate instead of copying over the cart line.

Figure 8: XC merges the anonymous cart with the existing customer cart on log in (rollup enabled).

OrderCloud contains similar functionality in allowing ownership an order from an anonymous user to be transferred to a profiled (registered) user. For registration, the order will need to be explicitly transferred.

Where a profiled user logs in, the difference between transferring an order and XC’s merging of carts, is that the buyer user may now have two active unsubmitted orders. The implementor would need to manage merging the orders together and either explictly deleting the second order or let OrderCloud’s unsubmitted order cleanup process delete it.

Figure 9: OrderCloud transfers an anonymous order to an authenticated registered user.

Abandoned Cart/Order Cleanup

The Commerce Engine’s minions role is responsible for cleaning up abandoned and empty carts. As seen below the PurgeCartsMinion runs daily, removing empty carts that are more than 2 days old and carts that have not been updated for more than 14 days. Both the minion and purge carts policies are configurable.

{
  "$type": "Sitecore.Commerce.Core.MinionPolicy, Sitecore.Commerce.Core",
  "WakeupInterval": "01.00:00:00",
  "ListsToWatch": [
    "Carts"
  ],
  "FullyQualifiedName": "Sitecore.Commerce.Plugin.Carts.PurgeCartsMinion, Sitecore.Commerce.Plugin.Carts",
  "ItemsPerBatch": 10,
  "SleepBetweenBatches": 500
}
{
  "$type": "Sitecore.Commerce.Plugin.Carts.PurgeCartsPolicy, Sitecore.Commerce.Plugin.Carts",
  "AbandonedCartsThreshold": 14,
  "EmptyCartsThreshold": 2
}

In OrderCloud, unsubmitted order cleanup is an internal automated process with the rules outlined below, which are not configurable.

User TypeHas Line Items?Retention Policy
AllNo24 hours
AnonymousYes7 days
ProfiledYes90 days from LastUpdated date

Data Mapping

With the conceptual analysis above, we will now review what data mapping would look like for migration and from a comparison standpoint.

In the XC Entity/Component column, components are assumed to live on the primary XC entity being mapped.

OrderCloud IDs do not allow spaces. It is important that the IDs are parsed to remove/replace invalid characters consistently.

Orders

POST /orders/{direction}

In the Architecture Overview, we note that the cart equivalent in OrderCloud is an unsubmitted order. While no direct data mapping required, the following table details the alignment with XC.

OC PropertyData TypeRequiredNotes
directionstringYesIs a resource parameter, not body property.
Set to Outgoing for buyer users.
IDstringNoAllowing the ID to be generated by the platform effectively matches XC order behaviour.
FromCompanyIDstringNoThis value will default to the buyer, which effectively matches XC.
ToCompanyIDstringNoThis value will default to the marketplace owner, which effectively matches XC.
FromUserIDstringNoDefaults to the current user context.
BillingAddressIDstringNoNot required at the time of creation.
ShippingAddressIDstringNoNot required at the time of creation.
CommentsstringNo
ShippingCostnumberNoNot required at the time of creation.
TaxCostnumberNoNot required at the time of creation.
xpobjectNoAny custom components required for the initial state can be added to xp.

Line Items

POST /orders/{direction}/{orderID}/lineitems

Adding a Cart Line vs Line Item has covered how to align the create line ltem request to align with XC. The following table covers the full view of the request.

OC PropertyData TypeRequiredXC PropertyNotes
directionstringYesIs a resource parameter, not body property.
Set to Outgoing for buyer users.
orderIDstringYesIs a resource parameter, not body property.
OrderCloud’s order.ID
IDstringNoBoth XC and OrderCloud generate this ID by default.
ProductIDstringYesItemIdOnly the SellableItemId portion of the ItemId is required.
QuantitystringYesQuantityThis value will default to the marketplace owner, which is effectively matches XC.
UnitPricestringNoWill be resolved from the PriceSchedule assignment of the current user context.
CostCenterstringNo
DateNeededstringNo
ShippingAccountstringNo
ShippingAddressIDstringNo
ShipFromAddressIDstringNo
InventoryRecordIDstringNo
SpecsobjectNoItemIdUsed over variant ID. All spec IDs and specOptionIDs that represent the variant product will need to be specified.
xpobjectNoCustom components can be added to xp as needed.

References

Continue the Series

  1. Transitioning from Sitecore Experience Commerce to OrderCloud: Customers to Buyer Users
  2. Transitioning from Sitecore Experience Commerce to OrderCloud: Customers and Buyers – API Access
  3. Transitioning from Sitecore Experience Commerce to OrderCloud: Catalogs and Categories
  4. Transitioning from Sitecore Experience Commerce to OrderCloud: Sellable Items To Products
  5. Transitioning from Sitecore Experience Commerce to OrderCloud: Inventory and Pricing
  6. Transitioning from Sitecore Experience Commerce to OrderCloud: Carts to Unsubmitted Orders and Carts
  7. Transitioning from Sitecore Experience Commerce to OrderCloud: Fulfillments to Shipping
  8. Transitioning from Sitecore Experience Commerce to OrderCloud: Tax and Payments
  9. Transitioning from Sitecore Experience Commerce to OrderCloud: Orders
  10. Transitioning from Sitecore Experience Commerce to OrderCloud: Order Workflow and Minions
  11. Transitioning from Sitecore Experience Commerce to OrderCloud: Promotions