Transitioning from Sitecore Experience Commerce to OrderCloud: Promotions

Reading Time: 10 minutes

In this article, we will review and compare promotions 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.

Note: This article is a little outdated with the release of Promotion Enhancements. Please see the official documentation for latest updates.

Conceptual Architecture and Features

Qualifications/Conditions vs Eligible Expressions

The Business Tools application in XC provides a basic wizard dialog for building up qualifications for a promotion. XC has over thirty promotion qualifications (or conditions) to construct common promotion scenarios without customisation.

For a complete list of available qualifications, see Promotions qualifications.

Figure 1: Add quailification wizard in XC.
Figure 2: Promotion qualifications view in XC.

OrderCloud promotions provide an EligibleExpression property, which accepts a free-text logic expression to qualify/validate a promotion as eligible against an order, using custom syntax outlined in Rule-Based Promotion Expressions.

Figure 3: Eligible expression in OrderCloud.

Looking at table 1, we see the native support for utilising entities and objects in qualifications/eligible expressions for each platform. For qualification types that are not natively supported by the OrderCloud platform, it is possible that support can be achieved through customisation. For more details, see Extending Promotion Functionality > Custom Promotion Logic.

Qualification Type \ PlatformXCOrderCloud
Product InventoryYesNo
Cart Line/Line ItemYesYes
Cart/OrderYesYes
CustomerYesYes
Customer Order HistoryYesNo
Date and TimeYesPartial
Table 1: Native qualification type support between XC and OrderCloud.

Benefits/Actions vs Value Expressions

Similar to qualifications, the benefits (or actions) in XC’s Business Tools also uses a wizard dialog to facilitate building out the promotion’s benefits, with over 10 pre-defined benefits available.

For a complete list of available benefits, see Promotion benefits.

Figure 4: Add benefit wizard in XC.
Figure 5: Promotion benefits view in XC.

OrderCloud promotions have a ValueExpression property, which use the same Rule-Based Promotion Expressions as the EligibleExpression property.

Figure 6: Value expression in OrderCloud.

Is Exclusive vs Can Combine

Promotions in XC have an Is Exclusive flag, which is used in the Promotion Evaluation and Application Logic to determine eligible promotions and prioritisation over non-exclusive promotions.

In OrderCloud, promotions use a can combine flag with similar behaviour, however the single biggest difference is that exclusive promotions do not take priority over non-exclusive (can combine) promotions.

Order Level vs Line Item Level Promotions

Both XC and OrderCloud promotions allow promotions to apply discounts at the order level or line item level, however, in calculating the promotion’s qualifications against the order or line item total properties, XC’s calculations are dynamically updated with each promotion discount calculation, therefore leveraging the Engine’s Promotion Priority Rules.

OrderCloud calculates each promotion discount against static totals, therefore regardless of the order in which promotions are calculated, it will not impact the final discount calculations, as per Promotion Calculation Logic.

Promotion Prioritisation

As touched upon above in Order Level vs Line Item Level Promotions, XC has a set of Promotion Priority Rules that determine the order promotions are applied.

OrderCloud’s Promotion Prioritisation Logic does not prioritise the order of promotions applied to an order, which is effectively not required as is utilises static order and line item totals meaning that promotion discount values will always be calculated the same, regardless of the order they are processed.

Promotion Assignments

For promotions in XC to be available to customers in a storefront, the Commerce Engine groups promotions into promotion books, which are associated to catalogs. The catalog is then associated to a storefront, which will be associated to a security domain that contains customers, to complete the customer to promotion connection.

Promotion books can be associated to multiple catalogs to be utilised in other storefronts.

In OrderCloud, promotions have more explicit assignments, allowing direct assignments to the buyer.

OrderCloud promotions can also be configured to target buyer user groups for more refined promotion targeting. More information can be found in Personalized Shopping > Targeted Promotions.

Figure 6: XC vs OrderCloud promotion assignment architecture.

Automatic vs Coupon-Triggered Promotions

The Commerce Engine’s promotions can be classified as automatic or coupon-triggered. Coupon-triggered promotions are evaluated as a preliminary qualifier during promotion qualification, which requires the coupon code to be applied to the cart, while the automatic promotions pass this qualifier gate and continue the promotion evaluation process, effectively allowing a promotion to be able to be applied to a cart based on the cart state and promotion qualifications alone and not via some explicit request to apply the promotion. For futher details, see Promotion Evaluation and Application Logic.

An XC promotion can be added to a cart even if it has yet to qualify for its benefit.

Currently, only coupon-triggered promotions are native to the OrderCloud platform. In a slightly different manner to XC, applying a promotion via a coupon code will be rejected by the platform if its currently not eligible, e.g. does not meet its eligible expression (qualification rule), falls outside the start or expiration date, does not adhere to the rules for the CanCombine flag, etc.

Private vs Public Coupons

The Commerce Engine breaks down coupons into private and public coupons, where public coupons are those that anyone may use, while private coupons are generated as single use coupons that the business can distribute to customers as they see fit, yet has no restrictions in the Commerce Engine as to who can use the coupons, although some smarts can be configured in the promotion’s qualifications and customisations will allow specific business requirements to be met.

Figure 7 shows the general setup of promotions with public vs private coupons, where customers with the relevant promotion assignments can apply the promotions to their cart.

Figure 7: Overview of private vs public coupons on XC promotions.

In OrderCloud, although promotions don’t have an explicit private/public flag against them, they can still be configured to behave in a similar fashion.

For “public” coupon promotions, this would simply depend on the promotion assignment created. For example, at a global level the AllowAllBuyers flag will allow all users to apply the promotion to their order, or an explicit promotion assignment to one or more buyers and/or buyer user groups may fit the “public” coupon business requirement.

For “private” coupon promotion, defining the promotion assignments to target the subset of buyer users and leveraging the RedemptionLimitPerUser property (covered in more detail in Redemption Limits), we can restrict the amount of redemptions the users have for the promotion.

Figure 8: An approach to public vs private promotions in OrderCloud.

Redemption Limits

As mentioned in Private vs Public Coupons, in XC private coupons have a single usage limit. Public coupons, on the other hand, have no redemption limit, although the UsageCount is tracked against the coupon entity, which can be used in a customisation to limit promotion redemptions at a global level.

In OrderCloud, promotions have a RedemptionLimit property to configure a global limit across all users and a RedemptionLimitPerUser property to set a redemption limit per user (both with the relevant promotion assignments).

Approval Workflow

XC has approval workflow logic built into promotions, which requires a business user with the PromotionerManager role to approve a promotion prior to it being made available.

Figure 7: Promotion approval workflow.

OrderCloud has no native approval workflow for promotions, however we could use the following approach to replicate the XC functionality in OrderCloud (with the additional retract action for business users who have submitted a promotion for approval prematurely and want to amend the promotion without intervention from the promotion manager).

Figure 8: An XC approval workflow equivalent in OrderCloud.
  • xp.Status will track the current promotion’s status. The Disabled state has been promoted to an explicit status.
  • Pre-hook webhooks would be utilised for all relevant promotion endpoints, for the middleware to validate the user’s custom PromotionManager role where applicable and also ensure that the xp.Status transition is valid.

Disabling a Promotion in Non-Approved States

For non-Approved statuses, these are effectively inactive promotion states that we will need to safeguard against, i.e. inactive promotions cannot be applied to orders and should be invalidated for orders not in an Unsubmitted or AwaitingApproval state. The approach we will look to take is to override the promotion’s ExpirationDate property.

For the Draft and ReadyForApproval states, the ExpirationDate can be set with the StartDate and the intended expiration date can be set on the xp as ActualExpirationDate. When the promotion is promoted to an approved state, the ExpirationDate can be replaced with the ActualExpirationDate.

With the StartDate being the equivalent of the ExpirationDate, the promotion can never be evaluated to being active.

For the Disabled state, we can set the ExpirationDate to the current date/time, while the xp.ActualExpirationDate still maintains the original expiration date for historical and reconciliation purposes.

For this approach, “inactive” promotions will throw a Promotion.Expired error code, which in conjunction with the xp.Status could be translated into a more user-friendly and promotion status-specific error message for front end applications, and/or allow for more appropriate error handling.

Triggering Promotion Evaluation

In XC, promotions were evaluated whenever the CalculateCartPipeline was called, which typically occurs when retrieving the cart or modifying the cart.

In OrderCloud, promotions are validated, calculated, and invalidated by the platform when certain endpoints are called. For detailed information, see Promotion Validation, Calculation, and Invalidation.

Extending Promotions

It is possible to fully customise Commerce Engine by adding, modifying and removing pipelines and pipelines and pipeline blocks to meet bespoke business requirements.

Extending promotions in OrderCloud requires a building the solution around the API rather than being able to customise the platform directly as per the Commerce Engine . Depending on business requirements a few approaches have been documented in Custom Promotion Logic.

XC to OrderCloud Promotion Cheatsheet

As a quick reference for re-writing XC promotion qualifications and benefits as eligible expressions and value expressions in OrderCloud, the following tables provide either the available OrderCloud expressions, or are flagged that customisation are required to achieve, with some instances providing sample approaches to how the customisation could be achieved.

Qualifications to Eligible Expressions

Product Inventory Qualifications

Line Level: true

Qualification/ConditionEligible Expression
Inventory Item Stock Count In [specific] Location [compares] to [specific value]?Inventory evaluation requires extending the API functionality via middleware.
Is Item In Stock?Inventory evaluation requires extending the API functionality via middleware.
Is Item in Stock in [specific] Location?Inventory evaluation requires extending the API functionality via middleware.
Is Item Out of Stock?Inventory evaluation requires extending the API functionality via middleware.
Is Item Out of Stock in [specific] Location?Inventory evaluation requires extending the API functionality via middleware.
Is Cart Item Available?Inventory evaluation requires extending the API functionality via middleware.
Is Cart Item [specific] Quantity Available?Inventory evaluation requires extending the API functionality via middleware.
Is Item Pre-orderable?item.Product.xp.PreOrderable = true
Although pre-order functionality is not native to the platform, a pre-order flag may be set on the product’s xp to achieve this promotion.
Is Item Pre-orderable in [specific] Location?Inventory evaluation requires extending the API functionality via middleware.
Is Item Back-orderable?item.Product.xp.Backorderable = true
Although back-order functionality is not native to the platform, a back-order flag may be set on the product’s xp to achieve this promotion.
Is Item Back-orderable in [specific] Location?Inventory evaluation requires extending the API functionality via middleware.

Cart Line Qualifications

Line Level: true

Qualification/ConditionEligible Expression
Cart Item Quantity [compares] to [specific value]?item.ProductID = <value> and item.Quantity <comparison operator> <value>
Cart Item Quantity is in [min] [max] Range?item.ProductID = <value> and item.Quantity >= <min value> and item.Quantity <= <max value>
Cart Item Subtotal [compares] to [specific value]?item.ProductID = <value> and item.LineSubtotal <comparison operator> <value>
Cart Item is in [specific] category?item.product.incategory(<category ID>)
OR
item.incategory(<category ID>)

Cart Qualifications

Qualification/ConditionEligible Expression
Cart Has included/excluded [specific] item(s)? (Items Collection)items.Any(<value>) and not item.Any(<value>)
Cart Has Items?Order.LineItemCount > 0
Cart Has [count] Items?Order.LineItemCount = <value>
Cart Any Item Has [Tag]?Arrays are not currently supported in OrderCloud, however this could be achieved via converting tags to flags or strings for direct evaluation.
Cart Any Item Subtotal [compares] to [specific value]?items.any(LineSubtotal <comparison operator> <value>)
Cart Subtotal [compares] to [specific value]?Order.Subtotal <comparison operator> <value>
Cart Has Fulfillment?Order.xp.SelectedShipMethodID <> null
Requires maintaining a copy of the SelectedShipMethodID from the ShipEstimateResponse on the order’s xp.
Is Context Currency [specific value]?Order.Currency = <value>

Customer Qualifications

Qualification/ConditionEligible Expression
Is Cart Contact Currency [specific value]?Order.FromUser.Locale.Currency = <value>
Is Cart Contact Customer ID [specific value]?Order.FromUser.ID = <value>
Is Cart Contact Language [specific value]?Order.FromUser.Locale.Language = <value>
Is Cart Contact Registered?Order.FromUser.ID = <Default Context User>

Order History Qualifications

While the orders count and orders total may be able to be updated on the customers xp, keeping track of individual items purchased in the xp would be considered abuse as this can scale out of control and breach the maximum character length.

Qualification/ConditionEligible Expression
Current Customer Has Purchased [specific] Item?Evaluation requires extending the API functionality via middleware.
Current Customer Has Purchased Item with [specific] Tag?Evaluation requires extending the API functionality via middleware.
Current Customer Orders Count [compares] to [specific value]?Order.FromUser.xp.OrdersCount <comparison operator> <value>
Requires maintaining the count of orders on the buyer user’s xp.
Current Customer Orders Total [compares] to [specific value]?Order.FromUser.xp.OrdersTotal <comparison operator> <value>
Requires maintaining the sum of order totals on the buyer user’s xp.

Shop Qualifications

Qualification/ConditionEligible Expression
Is Shop Currency [specific value]?Order.Currency = <value>
Is Shop Language [specific value]?Order.FromUser.Locale.Language = <value>
Is Shop Name [specific value]?Order.xp.Storefront = 'Storefront'
Requires customising the storefront to apply the storefront name to the order’s xp.

Date Qualifications

Qualification/ConditionEligible Expression
Current Date Has Passed?<value> > now(0)
It is likely that the promotion’s StartDate should be sufficient.
Is Current Day?Evaluation requires extending the API functionality via middleware.
Is Current Month?Evaluation requires extending the API functionality via middleware.

Benefits to Value Expressions

Cart Benefits

Benefits/ActionsValue Expression
Get Free ShippingOrder.ShippingCost
Get Free GiftRequires extending the API functionality via middleware.
Get Cart Subtotal [specific] Amount Off<value>
Get Cart Subtotal [specific] Percent OffOrder.Subtotal * <percentage value>

Cart Line Benefits

As can be seen below, a number of benefits can be converted into the same, more basic value expression in OrderCloud as it retains context of the line item for evaluation, whereas XC typically requires the condition to be re-evaluated in the benefit to be applied correctly.

Benefits/ActionsValue Expression
Get Cart Any Item Sell Priceitem.UnitPrice - <value>
Get [specific] Amount Off Item Subtotal<value>
Get Cart Any Item Subtotal [specific] Amount OffSee above.
Get Cart Item in Category [specific] Amount OffSee above.
Get Cart Item Subtotal [specific] Amount OffSee above.
Get [specific] Percentage off Item Subtotalitem.LineSubtotal * <percentage value>
Get Cart Any Item Subtotal [specific] Percent OffSee above.
Get Cart Item in Category [specific] Percent OffSee above.
Get Cart Item Subtotal [specific] Percent OffSee above.

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

Transitioning from Sitecore Experience Commerce to OrderCloud: Order Workflow and Minions

Reading Time: 8 minutes

In this article, we will review and compare the order workflows 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 also review minions from XC and how this these processes can be brought over into OrderCloud to facilitate order workflow automation.

We will look at a high-level comparison of architecture, functionality, and data models, where applicable, 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.

Order Workflow

In XC, the default order workflow demonstrates the capabilities of post-order placement processing where order status transitions are achieved through manual intervention or automated processes (minions). It is intended to be extended and customised to cater for the unique business requirements of each project.

While figure 1 represents the default order workflow, XC can be customised to create or remove order statuses, and create or remove the triggers that transition an order between statuses, e.g. manual intervention via business users (customer service representatives) or automated processes such as minions.

Figure 1: XC’s order status workflow.

To provide clarity regarding each order status, XC’s definitions are:

  • Pending: Initial order state – order paid, no fulfillment.
  • OnHold: Prevents order from being progressed by minions. Customer service representatives may modify, line items, payments, shipments, etc.
  • WaitingForAvailability: One or more items in the order are not available. This status is expected for items on backorder or preorder.
  • Released: Intended status for indicating the order passed validation and is ready for shipping. It is also typically used in customisations to the workflow to indicate the order has been exported to an external order management system (OMS).
  • Problem: An order that has an unexpected status when processed by minions. Manual intervention by customer service representatives is available to transition the order back into a state to be picked up my the minions again, however this typically indicates a bug within custom implementations, which requires developer intervention.
  • Completed: Order has been shipped, has had entitlements generated, and has nothing left to process. Complete orders are eligible for the return mechandise authorisation (RMA) processes.
  • Cancelled: Order has been cancelled and can no longer be processed by minions or customer service representatives.

In OrderCloud, the order status workflow differs to XC’s in that order submission can include an approval process, which is typically a B2B order scenario, while the post-order placement workflow for order status transitions is minimal, as per figure 2.

To implement a custom order workflow, we will cover an approach below in Custom Order Status in OrderCloud.

Figure 2: OrderCloud’s order status workflow.

OrderCloud’s order status definitions are:

  • Unsubmitted: Created but has not yet been submitted.
  • Open: Submitted but not yet fulfilled.
  • AwaitingApproval: On hold awaiting approval.
  • Declined: Submitted but has been declined.
  • Completed: Submitted and all the line items on the order have been shipped.
  • Canceled: Order can no longer be submitted.

Removing the order statuses for approval and the cart equivalent “Unsubmitted” status, we see that OrderCloud simply transitions between Open (XC’s Pending) and Completed or Canceled statuses.

Figure 3: OrderCloud’s order status workflow (XC equivalent).

Custom Order Status in OrderCloud

To achieve an XC equivalent order workflow in OrderCloud, the missing order statuses may be introduced under OrderCloud’s Open status, using xp to represent the custom status. Typically, this would be achieved by PATCHing the order’s xp, using either middleware as a proxy to support wrap OrderCloud API endpoints with custom logic, or have middleware perform validation via pre-hook webhooks to prevent invalid status transitions.

There is no restriction in modifying the workflow or introducing new statuses via xp, only considerations for ensuring that the workflow transitions from end-to-end as intended.

Figure 4: The XC workflow equivalent in OrderCloud.

Transaction and Payment Status

We previously have covered the payment concepts between XC and OrderCloud in Tax and Payments, but what we didn’t cover was the statuses that belong to these entities and how they can be utilised in the SXA storefront.

For Sitecore Experience Commerce, the federated payment components have a TransactionStatus property, which is intended on tracking the raw statuses specific to the payment provider. They are typically used only for transparency and reconciliation purposes, rather than driving business logic in XC.

Figure 5: The default workflow for transaction status of federated payments.

Sales activities have a PaymentStatus property, which also is intended on tracking the transaction status in a more agnostic fashion by reducing payment provider statuses to more generic status buckets, which can then be used to influence business logic. In Order Workflow, figure 1 shows that the payment status affecting the order status, namely by placing an order into a problem state where the payment status is set as Problem.

Figure 6 and 7 shows the workflows for payment sales activities in XC, which modifies existing sales activity for subsequent transactions, hence representing as transitions, however this is commonly customised to create a separate sales activity per transition for a complete historical view of events and reconciliation reporting.

Figure: 6: The default workflow for payment status of federated payments.
Figure 7: The default workflow for payment status of gift cards.

Translating XC’s payments over to OrderCloud payments resources, we see that transactions have the ResultCode property, which could be considered the equivalent to XC’s payment status, while XC’s transaction status could be stored on the payment’s xp. Looking at the payments resource in OrderCloud however we see that the intended architecture for transactions is to track each transaction for a payment rather than updating the its status, as the transactions can be created or deleted, but not updated.

Rather than porting XC’s architecture over to OrderCloud directly, there are some improvements we could introduce to reduce the complexity of the overall solution, which includes the business logic used in the order workflow and not just recreating the payment entities like for like. For example, if we treat transactions as a snapshot in time for a given payment, rather than a dynamic object representing only the most recent transaction, the ResultCode would probably be better can be used for the raw transaction status given by the payment provider.

Figure 8: Transaction ResultCode (status) workflow. Each status will be represented by its own transaction.

For the overall payment status, we can use the payment’s xp to maintain the most recent transaction status in a normalised form, agnostic of payment provider, which would simply be translated as part of the payment provider integration.

Figure 9: Payment status workflow example in OrderCloud.

Order Minions

Order minions are background jobs that process all orders in a given status to either perform some function on and/with the order, before transitioning it to its next status.

Pending Orders Minion

The pending orders minion is a shell for processing orders fresh from being placed. It is expected to be customised to fit each project’s requirements, and can be thought of as an extension of the create order process, delegated with performing asynchronous activities in a separate Commerce Engine running the minions role, which typically would run on dedicated resources. This is to mitigate any performance impacts on the Commerce Engine instance(s) running the shops role, which serves the storefront’s live traffic.

The pending order minion will usually be extended with processes such as exporting the order to an order management system (OMS), sending an order invoices via an email service provider, generating gift cards, activating subscriptions, and more.

In OrderCloud, we will review the order submit integration event and batch order processing approaches to process pending orders.

Order Submit Integration Event

The order submit integration event is a non-blocking asynchronous call from OrderCloud, calling a job runner application, allowing an order to be processed immediately without blocking the storefront/buyer application. The job runner application, in this case, will update the order’s custom status in the xp, so that other automated jobs can process the order appropriately within the order workflow.

Third party integrations, such as Azure Service Bus, may further faciliate mitigating transient issues, increase resilience, and offload processing power from the job runner application.

Figure 10: Order submit integration event.

Batch Order Processing

An alternate approach to integration events for processing orders is batch jobs, which is more closely aligned to pending orders minion behaviour, by leveraging the custom order status in the order xp and the ElasticSearch backed premium search for orders.

Figure 11: Batch processing pending orders.

Released Orders Minion

The released orders minion processes orders in the released state by creating shipments against the order, generating entitlements, capture payments, then transition the order into a completed state as depicted in figure 12. We don’t need to bring over fault pipeline blocks, and as discussed in Orders – Shipments, we will be able to fold the shipment generation together.

Figure 12: A comparison of XC’s released order process and an equivalent approach in OrderCloud.

To replicate the released orders minion in OrderCloud, we would need to follow the batch processing orders approach as described for the pending order minion equivalent behaviour.

While it might seem that the pending orders minion and released orders minion should be folded into a single process, the intention behind segregating these order states into separate processing jobs is due to the expected customisations, which may see manual triggering/transitioning or delayed processing between order states that will be considered against the project’s business requirements.

Figure 13: Batch processing released orders.

Waiting For Availability Orders Minion

The waiting for availability order minion simply iterates over orders in registered to the WaitingForOrders list and determines if all products on the order have availability, in which they are returned to the Pending status/list. It is intended to support back-order and preorder scenarios, however as these features aren’t completely functional, customisation is required to complete the implementation and cater for any unique business requirements.

Pre-order and backorder inventory features are not natively supported in OrderCloud and as XC’s implementation is also incomplete, we won’t cover how to port this across in detail, only to highlight that we can leverage the same batch processing approach we have already outlined as a framework for implementing this as an automated job.

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

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

Sitecore Experience Commerce: Adding Custom Properties to Search in BizFx

Reading Time: 8 minutes

In this article, we will review how we can extend the search components in Business Tools (BizFx) to support search functionality using custom properties for its results.

This article will not consider locale-support.

Introduction

As the search functionality in BizFx is fairly primitive, from a business user perspective, and a common request is to provide customisation guidance for the search functionality in the Business Tools, we will use the Search component from the Merchandising dashboard for our example in extending the search functionality.

Figure 1: Search component in the Merchandising Dashboard.

This implementation will assume the sellable items that have custom properties added via composer templates, however you will be able to adapt the solution where properties have been added programatically with components or policies.

Figure 2: A sellable item extended with a composer template.

Implementation and Configuration

Updating the Solr Schema

We first need to add our custom property to the managed schema of our Solr cores related to our search scopes. These schemas are found in the file system where Solr is deployed, under server/solr/<search scope>/conf/managed-schema.

The commerce engine uses switch on rebuild so we need to update the managed schema for both CatalogItemsScope and CatalogItemsScope-Rebuild for this instance.

    <!-- CommerceEngine Catalog -->
    <field name="displayname" type="string" indexed="true" stored="true"/>
    <field name="datecreated" type="pdate" indexed="true" stored="true"/>
    <field name="dateupdated" type="pdate" indexed="true" stored="true"/>
    <field name="artifactstoreid" type="string" indexed="true" stored="true"/>
    <field name="parentcataloglist" type="string" indexed="true" stored="true" multivalued="true"/>
    <field name="variantid" type="string" indexed="true" stored="true"/>
    <field name="variantdisplayname" type="string" indexed="true" stored="true"/>
    <field name="inventorysetids" type="string" indexed="true" stored="true"/>
    <field name="productid" type="string" indexed="true" stored="true"/>
    <field name="name" type="string" indexed="true" stored="true"/>
    <field name="yearmanufactured" type="string" indexed="true" stored="true"/>

The managed-schema that would be created with the XC instance will be organised to easily identify the commerce entity fields. While fields can be added via Solr, the managed-schema will be regenerated in a less XC friendly manner and make long-term mainentance more difficult.

With our Solr schema updated, if we are working locally, we will need to restart the Solr service, otherwise you will need to manage the deployment of the schema to your deployment environments.

Populating the Index’ Custom Fields

The commerce indexes are created and updated by both the FullIndexMinion and the IncrementalIndexMinion. It is within these minions that we can inject our custom code to populate the new custom fields with our commerce entity properties.

We’ll create a new pipeline block, called InitializeExtendedSellableItemIndexingViewBlock, which we can see the Run() method in the following snippet. We perform the necessary validation and copy our entity properties to custom index fields.

public override EntityView Run(EntityView arg, CommercePipelineExecutionContext context)
{
    // 1. Validation
    Condition.Requires(arg, nameof(arg)).IsNotNull();
    Condition.Requires(context, nameof(context)).IsNotNull();

    var argument = context.CommerceContext.GetObjects<SearchIndexMinionArgument>().FirstOrDefault();
    if (string.IsNullOrEmpty(argument?.Policy?.Name))
    {
        return arg;
    }

    // 2. Prepare Entities
    var entityItems = argument.Entities?.OfType<SellableItem>().ToList();
    if (entityItems == null || !entityItems.Any())
    {
        return arg;
    }

    // 3. Prepare custom properties
    var scopeIndexablePropertiesPolicy = IndexablePropertiesPolicy.GetPolicyByScope(context.CommerceContext, context.CommerceContext.Environment, argument.Policy.Name);
    if (scopeIndexablePropertiesPolicy?.ComposerProperties == null || !scopeIndexablePropertiesPolicy.ComposerProperties.Any())
    {
        return arg;
    }

    var searchViewNames = context.GetPolicy<KnownSearchViewsPolicy>();
    var childViews = arg.ChildViews.OfType<EntityView>().ToList();

    // 4. Iterate over each entity
    foreach (var si in argument.Entities.OfType<SellableItem>())
    {
        // 5. Get existing document entity view
        var documentView = childViews.First(v => v.EntityId.EqualsOrdinalIgnoreCase(si.Id)
            && v.Name.EqualsOrdinalIgnoreCase(searchViewNames.Document));

        // 6. Add custom fields
        AddComposerFields(si, documentView, scopeIndexablePropertiesPolicy);
    }

    return arg;
}

The highlighted section, 3. Prepare custom properties, is used for our AddComposerFields() method.

To add properties to the Solr document, we only need to register the field name and value to a new ViewProperty of the document EntityView and the platform will later translate and push this data into Solr. This would typically look like the following.

documentView.Properties.Add(new ViewProperty
{
    Name = "<Insert Field Name Here>",
    RawValue = "<Insert Field Value Here>"
});

In our AddComposerFields() implementation, we leverage our custom policy to identify and process fields from the composer templates.

protected void AddComposerFields(
    SellableItem si,
    EntityView documentView,
    IndexablePropertiesPolicy scopeIndexablePropertiesPolicy)
{
    // 1. Iterate over each composer template configuration
    foreach (var composerView in scopeIndexablePropertiesPolicy.ComposerProperties)
    {
        // 2. Get property value
        var composerEntityView = si.GetComposerViewFromName(composerView.Key);

        if (composerEntityView == null)
        {
            continue;
        }

        // 3. Iterate over each custom property to index
        foreach (var property in composerView.Value)
        {
            var value = composerEntityView.GetPropertyValue(property.PropertyName);

            if (value == null)
            {
                continue;
            }

            // 4. Add property to index document
            documentView.Properties.Add(new ViewProperty
            {
                Name = property.IndexFieldName,
                RawValue = value
            });
        }
    }
}

The implementation is made flexible by avoiding hard-coding any composer template or property names and instead using the custom IndexablePropertiesPolicy, which we append our configured policy to the SolrSearchPolicySet. In ComposerProperties we add our composer template name, e.g. "ManufacturedDetails", and then an array of composer property models, where the PropertyName represents the composer template property name and the IndexFieldName represents the Solr core field name.

{
  "$type": "Ajsuth.Sample.Catalog.Search.Engine.Policies.IndexablePropertiesPolicy, Ajsuth.Sample.Catalog.Search.Engine",
  "SearchScopeName": "CatalogItemsScope",
  "ComposerProperties": {
    "ManufacturedDetails": [
      {
        "PropertyName": "YearManfactured",
        "IndexFieldName": "yearmanufactured"
      }
    ]
  }
}

We register our pipeline block to both the full and incremental index minion pipelines.

.ConfigurePipeline<IIncrementalIndexMinionPipeline>(pipeline => pipeline
    .Add<Pipelines.Blocks.InitializeExtendedSellableItemIndexingViewBlock>()
        .After<InitializeSellableItemIndexingViewBlock>()
)

.ConfigurePipeline<IFullIndexMinionPipeline>(pipeline =>pipeline
    .Add<Pipelines.Blocks.InitializeExtendedSellableItemIndexingViewBlock>()
        .After<InitializeSellableItemIndexingViewBlock>()
)

By adding our InitializeExtendedSellableItemIndexingViewBlock after the InitializeSellableItemIndexingViewBlock rather than directly replacing and overriding the initial platform pipeline block, we may suffer a minor performance impact as we will need to interate over our entities another time, however we won’t have to copy platform code into our pipeline block, which will ultimately keep our code as clean as possible while preventing complexity for during upgrades.

We then follow our deployment process for our changes:

  1. Deploy our Solr managed-schemas and restart the Solr service to ingest the updated schemas.
  2. Deploying our Commerce Engine code to all instances.
  3. Bootstrap the Commerce Engine to ingest our IndexablePropertiesPolicy configuration.
  4. Restart the minions Commerce Engine instance to consume the updated policy set.
  5. Executing the Run FullIndex Minion – Catalog Items request from postman.
  6. Verify our Solr core now how our custom field indexed.
Figure 3: yearmanufactured property added to Solr core.

If you aren’t seeing the custom field for any indexed document check the *-Rebuild core as that may the current active core.

Searching Against Custom Fields in the Search Component

With the custom properties added to the indexes, we finally need to update the search component to include the new property as part of the search query.

Figure 4: Search does not query against the custom yearmanufactured field by default.

Using Solr Query Syntax

With no further customisation or configuration, we could use the Solr query syntax to apply to set the Search Term with the specific field query, e.g. yearmanufactured:2009, as the search component will process this as a raw query.

Figure 5: Search can query against the custom yearmanufactured field using Solr query syntax.

Including the Custom Field for SearchScope Queries

The search component queries against the Search Term properties appended to the _text_ field in Solr. If we wanted to add a custom field to default search query, then we can update the managed-schema of the Solr cores to include it. Below lists all of the fields that are used for the search queries as well as our custom field.

<copyField source="displayname" dest="_text_"/>
<copyField source="variantid" dest="_text_"/>
<copyField source="variantdisplayname" dest="_text_"/>
<copyField source="productid" dest="_text_"/>
<copyField  source="name" dest="_text_"/>
<copyField source="yearmanufactured" dest="_text_"/>

After deploying and restarting the Solr service, search component queries from the Business Tools will now query the custom fields as well in the default search.

Figure 6: Search queries against the custom yearmanufactured field by default with updated managed-schema.

Customising the Search Results

The last piece of the puzzle is to add our custom indexed fields to the Results entity view as seen below.

The Results entity view is populated with indexed commerce entity data and not raw commerce entity data.

First, we want to update the IndexablePolicy in the SearchPolicySet for our given search scope, with our custom field.

{
"$type": "Sitecore.Commerce.Plugin.Search.IndexablePolicy, Sitecore.Commerce.Plugin.Search",
"SearchScopeName": "CatalogItemsScope",
"Properties": {
  ...,
  "YearManufactured": {
    "TypeName": "System.String",
    "IsKey": false,
    "IsSearchable": true,
    "IsFilterable": true,
    "IsSortable": true,
    "IsFacetable": false,
    "IsRetrievable": true
  }
}

For our implementation purposes, we only need to be concerned about the IsRetrievable and IsSortable properties of the IndexableSettings object. The IsRetrievable property will allow us to provide a fallback property with an empty value, while the IsSortable property will configured the column to be sortable. Both follow the platform implementation pattern.

Next, I would like to tell you that we could extend the Results entity view simply by extending pipelines with additional pipeline blocks, but we will need to copy and replace the original pipeline blocks for the most consistent approach across commerce indexes. There is a ProcessDocumentSearchResultsBlock within the commerce plugins that will relate to the search scope, which we would copy into our solution. In this instance, we copy the pipeline block from the Sitecore.Commerce.Plugin.Catalog.dll.

Without re-writing the platform code, we will have to face a bit more rigid manual coding over a more automated, iterative approach to the IndexableSettings of our IndexablePolicy. The key area of the pipeline block we need to focus on is shown on line 5, where the results view properties are cleared. We need to copy our custom field from the properties before hand (1. Copy the field view property) and then repopulate the results view afterwards (3. Add desired indexed fields back into results view), which effectively strips the results view or the other indexed fields that we don’t want to render.

// 1. Copy the field view property 
var yearmanufactured = child.Properties.FirstOrDefault(p => p.Name.EqualsOrdinalIgnoreCase("YearManufactured"));

// 2. Clear the results view properties
child.Properties.Clear();

if (name != null)
{
    name.UiType = ViewsConstants.EntityLinkUiType;
}

// 3. Add desired indexed fields back into results view
child.AddViewPropertyToEntityViewOrDefault(name, retrievableProperties, CoreConstants.Name, typeof(string).FullName);
child.AddViewPropertyToEntityViewOrDefault(displayName, retrievableProperties, CoreConstants.DisplayName, typeof(string).FullName);
child.AddViewPropertyToEntityViewOrDefault(variantId, retrievableProperties, CatalogConstants.VariantId, typeof(string).FullName);
child.AddViewPropertyToEntityViewOrDefault(variantDisplayName, retrievableProperties, CatalogConstants.VariantDisplayName, typeof(string).FullName);
child.AddViewPropertyToEntityViewOrDefault(createdDate, retrievableProperties, CoreConstants.DateCreated, typeof(DateTimeOffset).FullName);
child.AddViewPropertyToEntityViewOrDefault(updatedDate, retrievableProperties, CoreConstants.DateUpdated, typeof(DateTimeOffset).FullName);
child.AddViewPropertyToEntityViewOrDefault(yearmanufactured, retrievableProperties, "YearManufactured", typeof(string).FullName);

We replace the platform pipeline block in SearchPipeline in our pipeline registrations.

.ConfigurePipeline<ISearchPipeline>(pipeline => pipeline
    .Replace<Sitecore.Commerce.Plugin.Catalog.ProcessDocumentSearchResultBlock, Pipelines.Blocks.ProcessDocumentSearchResultBlock>()
)

The platform pipeline block we are overriding is fully qualified here to to explicitly replace the correct pipeline block.

All that is left is to deploy the code, which is short looks requires:

  • Deploying our Commerce Engine code to all instances.
  • Bootstrap the Commerce Engine to ingest our updated IndexablePolicy configuration.
  • Restart the authoring Commerce Engine instance to consume the updated policy set.

Performing our search now, we will see that we have our custom field appended to the end of the Results entity view, which can be used to sort the results.

Figure 7: The Results entity view is customised with the yearmanufactured indexed field.

Summary

While the search component of BizFx was not developed with the intention of being configurable, we have identified a few ways to customise the search component, which will hopefully cover most business user scenarios and saves us from having to implement our own search component from scratch.

References

Transitioning from Sitecore Experience Commerce to OrderCloud: Tax and Payments

Reading Time: 7 minutes

In this article, we will review and compare tax and payments 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

Tax

The Commerce Engine’s tax plugin is considered sample code that is sufficient enough to support basic tax requirements, however tax calculations will typically be customised to support the tax provider integrations that cater to tax laws for the countries and/or regions the storefront will be servicing.

The following list contains highlights of the tax plugin:

  • Tax is calculated as a cart adjustment when a fulfillment component is applied to the cart.
  • Tax is calculated as a cart line adjustment where a fullfillment component exists on the cart line.
  • XC waits for a cart to contain a fulfillment component before calculating taxes, as the delivery address is intended to identify applicable tax laws, but not catered for without customisation.
  • Sellable item pricing does not include tax.
  • Applying the tag taxexempt will omit sellable items from tax calculations.
  • Tax is calculated as a flat rate, i.e. tax is not dynamically calculated based on country/region tax laws.

The GlobalTaxPolicy provides the configuration properties of the sample tax plugin.

{
  "$type": "Sitecore.Commerce.Plugin.Tax.GlobalTaxPolicy, Sitecore.Commerce.Plugin.Tax",
  "DefaultCartTaxRate": 0.1,
  "DefaultItemTaxRate": 0.1
  "TaxExemptTagsEnabled":true,
  "TaxExemptTag":"taxexempt",
  "TaxCalculationEnabled": true, // Not hooked up
  "PriceIncudesTax": false, // Not hooked up
  "CalculateTaxBasedOn": "ShippingAddress", // Not hooked up
  "ShippingTaxClass": "CartItems", // Not hooked up
  "RoundAtSubTotal": false // Not hooked up
}

When it comes to tax in the OrderCloud platform, it is unopinionated on the methods of which to achieve tax calculations due to the numerous unique tax regulations around the world. Instead, OrderCloud allows the implementer to address tax calculations via the order calculate integration event, whether it be through integrations third party tax providers or complete custom implementation.

Figure 1 shows an overview of the order calculate integration event implementation in regards to calculating taxes. OrderCloud’s /calculate endpoint forwards the request along with the order worksheet to the middleware’s /ordercalculate endpoint. The middleware will then construct and return the OrderCalculateResponse model, containing the calculated TaxTotal, which OrderCloud then copy to the order’s TaxCost property. To track line item level taxes, calculated taxes can be stored in the OrderCalculateResponse‘s xp.

The TaxCost is used in OrderCloud’s calculations of the order Total, i.e. Total = Subtotal + TaxCost + ShippingCost - PromotionDiscount.

Figure 1: The order calculate integration event overview.

The calling system in figure 1 may be the client-side storefront or the middleware, depending on the implementation details.

Payments

Gift Cards vs Spending Accounts

XC provides a gift card payment type, which is integrated into the SXA storefront checkout to demonstrate end-to-end functionality of gift card payments. It is treated as a sample implementation as the functionality in the Commerce Engine is typically replaced with an integration to a gift card payment provider.

Reviewing OrderCloud’s supported payment types, spending accounts most closely resemble XC’s gift card implementation as it has the ability to track the available funds. One minor difference is that spending accounts are created under the context of a buyer and is therefore tied to the buyer users within that buyer, whereas gift cards in XC are globally accessible.

To differentiate spending accounts representing gift cards from other spending account types, xp can be leveraged to specify the type.

Figure 2: Gift card architecture comparison.

Global Payment Configuration

In an XC SXA implementation, payments are configured at a global level under sitecore/Commerce/Commerce Control Panel/Shared Settings, as depicted in figure 3. The configuration is split across Payment Option Types and Payment Options and linked via the PaymentOption Type property on the payment option item, however there really isn’t any discernable difference between the two within the SXA/Commerce Engine implementation.

Figure 3: Payment option to payment option type in the Sitecore Content Editor.

Instead of a BYO payment types approach, OrderCloud has defined three payment types, being purchase order, credit card, and spending account, with each type serving a different approach to handling order payments. For example, the credit card payment type would integrate with a payment service provider for perform transactions, while the spending account manages a balance maintained purely with OrderCloud, including adjusting the balance upon successful order submission.

Figure 4: Payment architecture overview.

As XC contains multiple Payment Option Types that could map to a single OrderCloud payment type, xp can be leveraged to represent multiple sub-types. The following table shows how XC’s Payment Option Types could map to Payment Types in OrderCloud.

Payment Option Type (XC)Payment Type (OrderCloud)xp (OrderCloud)
Card PaymentCredit Cardxp.Type: “Basic Card”
Federated PaymentCredit Cardxp.Type: “Braintree”
Gift Card PaymentSpending Accountxp.Type: “Gift Card”
Loyalty Card PaymentSpending Accountxp.Type: “Loyalty Card”

More information on payment types can be found in OrderCloud: Flexible Fulfillment > Apply Payments.

Storefront Payment Configuration

Payment options are also configured against an SXA storefront with a subset of those configured in the Shared Settings.

Figure 5: Storefront payment configuration.

While OrderCloud doesn’t have the context of storefronts, storefront-specific payment options will likely be a consideration for middleware’s logic for identifying applicable payments options.

Payment Components vs Payments

When it comes to the implementation, this is where the Commerce Engine plugins take the payment configurations and provide the functionality for the payment types. The sample Braintree plugin and the GiftCards plugin will add a FederatedPaymentComponent or a GiftCardComponent to the cart respectively, which will then be used to dictate how those payments are interacted with throughout the platform, during and post order creation.

OrderCloud uses the payments entity in a more concrete way, using the Type property to facilitate the payment behaviour. For example, selecting the spending account type will require the also specifying a spending account that is assigned to the buyer user via buyer or buyer user group assignment.

Figure 6: An order paid for using both a credit card and a gift card payment types.

Sales Activities vs Transactions

XC orders use sales activities to track the transaction activities of each order payment component while OrderCloud uses transactions for the same purpose.

Figure 7: Sales activities vs transaction architecture.

Functionality

The Storefront Checkout Billing Step

Turning our attention to the storefront functionality of the billing checkout steps 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 8: The checkout billing aspects of the storefront user journey.

Viewing the interaction diagram in figure 9, the SXA storefront utilises the BillingDataJsonResult to provide the necessary data to render and facilitate payment option logic in the storefront. Once the user has submitted the payment details, all payment data is then sent to the Commerce Engine together to be added to the cart.

A few notes on the payment functionality:

  • Payments are only supported at the cart-level.
  • The email address is collected per order as the user may wish not to use the email address associated to their account (for registered users). This email is typically used for communications to the customer, such as email confirmation, invoicing, shipments, etc.
  • XC has logic to prevent a gift card sellable item to be purchased with a gift card payment.
Figure 9: The billing checkout step of the SXA storefront.

To replicate similar behaviour in an OrderCloud storefront implementation, figure 10 provides an approach to the billing checkout step.

  1. The /paymentoptions middleware endpoint retrieves the applicable payment options for the order, dependent on the storefront and business requirements.
  2. The email address is PATCHed to the order xp for specific order context.
  3. All payments can be added to the order via ../payments endpoint; the data sent may differ based on payment type.
Figure 10: An example approach for the delivery checkout step in an OrderCloud storefront.

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.

Spending Accounts

The following mapping shows how XC’s default implementation of gift cards can be translated into OrderCloud using spending accounts.

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
buyerIDstringYesN/AN/AN/A
IDstringNoGiftCardGiftCardCodestringUse format “GiftCard-<GiftCardCode>”
NamestringYesGiftCardNamestring
BalancedecimalYesGiftCardBalance.Amountdecimal
AllowAsPaymentMethodbooleanNoN/AN/AN/ASet to true.
RedemptionCodestringNoGiftCardGiftCardCodestring
StartDateDateTimeOffsetNoGiftCardActivationDateDateTimeOffset
EndDateDateTimeOffsetNoN/AN/AN/A
xpobjectNoN/AN/AN/A
xp.TypestringNoN/AN/AN/ASet to "GiftCard".
xp.InitialAmountdecimalNoGiftCardOriginalAmount.Amountdecimal
xp.CurrencystringNoGiftCardOriginalAmount.CurrencyCodestringRecommended for handling multi-currency validation.

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

Transitioning from Sitecore Experience Commerce to OrderCloud: Fulfillments to Shipping

Reading Time: 6 minutes

In this article, we will review and compare Sitecore Experience Commerce fulfillments and OrderCloud shipping 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

Fulfillment Configuration

Global Fulfillment Configuration

In an XC SXA implementation, fulfillments are configured at a global level under sitecore/Commerce/Commerce Control Panel/Shared Settings, as depicted in figure 1. The configuration is split across Fulfillment Option Types and Fulfillment Options and linked via the Fulfillment Option Type property on the fulfillment option item.

Figure 1: Fulfillment option to fulfillment option type in the Sitecore Content Editor.

This tiered architecture can be a little confusing, so each tier can be thought of as follows:

  • Fulfillment Option Type: The high-level approach in which the order will be fulfilled, e.g. Deliver to Address, Digital Delivery, Pick Up From Store, etc.
  • Fulfillment Option: The collection of methods for the type of fulfillment, e.g. Ship items. This could also be as considered a shipping vendor, such as DHL or FedEx.
  • Fulfillment Method: The specific method of delivery, which will be assigned to the order and may be accompanied by a fee.

The fulfillment fees associated to each fulfillment method are then configured in the Commerce Engine environment configuration under GlobalPhysicalFulfillmentPolicy.FulfillmentFees, which also supports multi-currency fees per fulfillment method.

"$type": "Sitecore.Commerce.Plugin.Fulfillment.GlobalPhysicalFulfillmentPolicy, Sitecore.Commerce.Plugin.Fulfillment",
"FulfillmentFees": {
  "$type": "System.Collections.Generic.List`1[[Sitecore.Commerce.Plugin.Fulfillment.FulfillmentFee, Sitecore.Commerce.Plugin.Fulfillment]], mscorlib",
  "$values": [
    {
      "$type": "Sitecore.Commerce.Plugin.Fulfillment.FulfillmentFee, Sitecore.Commerce.Plugin.Fulfillment",
      "Fee": {
        "$type": "Sitecore.Commerce.Core.Money, Sitecore.Commerce.Core",
        "CurrencyCode": "USD",
        "Amount": 15.0
      },
      "Name": "Ground",
      "Policies": {
        "$type": "System.Collections.Generic.List`1[[Sitecore.Commerce.Core.Policy, Sitecore.Commerce.Core]], mscorlib",
        "$values": []
      }
    },
    ...
}

Bringing this together into a friendly-ish diagram view, figure 2 shows the representation of fulfillment architecture across the Sitecore and Commerce Engine platforms.

Figure 2: Fulfillment architecture overview.

In OrderCloud, there are no fulfillment object equivalents to house and support fulfillment configurations. Instead, OrderCloud relies on the middleware application to provide its business logic, integrations, calculations, etc. to address the unique shipping business requirements for the OrderCloud storefront solution, which we cover in greater detail in Ship Estimates.

For now, figure 2 may be used as a reference point when evaluating how fulfillment/shipping data could be designed if business requirements identify that the custom middleware solution should persist shipping configuration data to a custom database.

Storefront Fulfillment Configuration

One last configuration requirement for an SXA storefront is to select the fulfillment options that will be applicable for that particular storefront.

Figure 3: Storefront fulfillment configuration.

Once again, OrderCloud lets the middleware manage the storefront specific shipping logic and calculations, which is covered in Ship Estimates, so this may just be a consideration for your custom middleware implementation.

Functionality

Ship Estimates

While OrderCloud depends on the middleware application to drive shipping data and behaviour, it does not leave the implementer high and dry. The order checkout integration event sees the /estimateshipping endpoint trigger the /ShippingRates endpoint of the middleware, handing it the order worksheet for context. The middleware can then execute its custom logic, which may include integrations to shipping providers, external systems, etc., to determine ship estimates.

The calling system in figure 4 may be the client-side storefront or the middleware, depending on the implementation details.

Figure 4: The order checkout integration event interaction overview.

The resulting ShipEstimateResponse model from the middleware’s /ShippingRates endpoint will be expected to consist of a breakdown of shipping estimates, representing a subset of line items and the shipping methods and costs that are available for the order.

In comparison to the SXA storefront, the ShipEstimateResponse model is basically the equivalent to the DeliveryDataJsonResult application model, which contains the ShippingOptions at the order and line levels.

Figure 5: The high-level view of ship estimates in a ShipEstimateResponse model.

For the integration event to trigger, the order checkout integration event will need to be configured with the middleware’s publicly accessible domain assigned to the CustomImplementationUrl of the integration event, and integration event’s ID assigned to the OrderCheckoutIntegrationEventID of the API Client.

The API Client used for the integration event will likely be the same client used for the Anonymous and Registered Users.

Figure 6: OrderCheckout integration event assignment to API client example.

The Storefront Checkout Delivery Step

Turning our attention to the storefront functionality of the delivery checkout steps from Carts to Unsubmitted Orders: Storefront User Journey, we will look at adding fulfillment details to the cart/unsubmitted order, focusing on varying APIs of the two platforms.

Figure 7: The fulfillment focused view of the storefront user journey.

Viewing the interaction diagram in figure 8, the SXA storefront utilises the DeliveryDataJsonResult to provide the necessary data to render and drive fulfillment functionality. Once the user has submitted the fulfillment details, all fulfillment data is then sent to the Commerce Engine together to be added to the cart.

A few notes on the fulfillment functionality:

  • Fulfillments can be set against the cart as a whole or for each and every cart line.
  • As sellable items can be classified as either a physical or digital item, XC contains logic to tie these to varying fulfillment types, which will collect different data relevant to its classification. For example, physical items require a physical fullfillment, consisting of a physical delivery address and the fulfillment method id, while digital items require digital fulfillments, consisting of recipient email address, gifting message and the fulfillment method id.
Figure 8: The delivery checkout step of the SXA storefront.

To replicate similar behaviour in an OrderCloud storefront implementation, figure 9 provides an approach to the delivery checkout step, which intentionally deviates from the SXA storefront functionality to demonstrate a more granular approach to building up the order using the OrderCloud API.

  1. The /shippingoptions middleware endpoint retrieves the available shipping options for the order, such as delivery or click and collect, with order-level or line-level shipping options, which will then drive storefront functionality for the various data collection methods for the respective shipping option selected.
  2. Fleshing out the approach for physical delivery shipping options, shipping addresses can be input manually or by referencing an existing address assigned to the user.
  3. The ../estimateshipping integration event can leverage the ShippingOption set on the order xp as well as the shipping details, such as shipping addresses, can be used for calculating shipping estimates by our middleware and third party integrations.
  4. The ../shipmethods sets the shipping methods for the order or line items.
Figure 9: An example approach for the delivery checkout step in an OrderCloud storefront.

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

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

Transitioning from Sitecore Experience Commerce to OrderCloud: Sellable Items To Products

Reading Time: 8 minutes

In this article, we will review and compare Sitecore Experience Commerce sellable items and OrderCloud products 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

Physical vs Digital Sellable Items

In XC, a sellable item is considered digital if it contains a tag that is also registered to the DigitalItemTagsPolicy. Digital products will not track inventory and will modify the delivery step of the checkout flow to collect information relevant to digital items, such as an email address and custom messaging, rather than a delivery address and shipping provider information, which is relevant for physical items.

As OrderCloud is impartial to the nature of the products, using existing platform functionality and extended properties xp, a solution can opt into tracking product types and apply custom behaviour the achieve bespoke business requirements. For example, to model products as digital as per XC, we can disable inventory tracking at the product level and copy the tags to the product’s extended properties. The middleware and storefront applications can then identify products with this information as representing digital items and provide custom behaviour throughout the storefront to match that of the SXA storefront.

{
  "Name": "MyDigitalProduct",
  "Inventory": {
    "Enabled": false
  },
  "xp": {
    "tags": "subscription"
  }
}

Sellable Items vs Products

In XC, sellable items can be represented as standalone products, which are sellable items without variations, or as a product family, which consists of a number of item variations each representing an individual product that share mostly similar properties to the other item variations.

OrderCloud can also represent products as both standalone product or a product with variants.

Figure 1: Sellable items with and without item variations vs products with and without variants

Item Variations vs Variants

In XC, the item variations are created within a sellable item entity, each representing its own unique product.

Variation properties are defined in the environment role configuration under VariationPropertyPolicy and sellable item families are expected to use a subset of these properties to create unique combinations.

In OrderCloud, the architecture of product families have the same intention, however there is enforcement around creating only unique variations using specs and spec options. A spec closely relates to the VariationPropertyPolicy in that it specifies the property that will break down the product into a variant, while the spec option specifies all of the values that will be assigned to the spec. A product can have multiple specs assigned, e.g. the first spec could represent colors while the second could represent sizes.

While specs can then be assigned to more than one product, there’s more control in creating specs that are unique to products even if they do represent the same property type, such as size, as this will reduce the long term maintenance.

Figure 2: The product-spec assignment overview.

OrderCloud then provides the endpoint, POST /products/{productID}/variants/generate, to generate variants from all possible combinations of the assigned specs. Referencing figure 3 it can be noted that when migrating sellable items from XC there will be instances where not all combinations are required, however any unwanted variants can be disabled.

Figure 3: An example of a sellable item with item variations vs a product with generated variants.
Item Variation Corruption

While the SXA storefront and commerce data provider will utilise the VariationPropertyPolicy configuration for the item variation controls on the product details page in the storefront, there is no validation during item variation creation. This means that is is possible to corrupt sellable item families in the following ways:

  1. Two or more varations share the same values across all of their variation properties. In figure 4, the product family utilises variation properties color and size, however item variations C and D are both configured with same color and size. In this instance, duplication may have been unintentional or a third variation property is missing that could make the variation unique. The Style property could be added in this instance to all item variations assuming the values between item variations C and D would be different.
  2. The subset of variation properties are not all populated with values. In figure 4, the Color and Size properties are filled out for all item variations, except for item variation E which has a null value for Size.
Figure 4: Examples of how item variations can be inadvertently corrupted.

In figure 4, the Style property is null for all item variations.

Folding Sellable Items with Variations into Standalone Products

During sellable item migration there are two more scenarios that may come up, specifically in relation to sellable items with a single item variation. The first is in noting point 2 of the item variation corruption, a sellable item is flagged as corrupted as no variation properties of the item variation have values, which can be easily folded into a standalone sellable item.

The second scenario is where the single item variation does contain values for one or more variation properties. Here, is where further consideration may be required as to whether this item variation should be folded back into a standalone sellable item. The reason for this consideration is because the Commerce Engine sets a global set of variation properties, however this may not necessary apply to the sellable item in question, but was designed this way because the global configuration in the VariationPropertyPolicy.

Figure 5: Opportunities to fold sellable items with variations into standalone sellable items.

Static and Dynamic Bundles

XC has special product types called static bundles and dynamic bundles, which are configurations of sellable items that are sold together. These bundles can either represent a fixed group of sellable items or a list of sellable items with alternate sellable items, which can be substituted out by the user, and/or be an optional extra into the bundle.

OrderCloud currently does not currently support product bundling natively, however by leveraging the product’s xp it is possible to model product bundles and have functionality be controlled by middleware and buyer and seller applications.

Workshopping product bundle models and behaviour is outside of the scope of this analysis.

Extended Properties

In XC, a sellable item can have its properties extended via composer templates or programatically with custom components, while item variations only be extended programatically. These can be generally be translated over to OrderCloud using its eXtended Properties (xp), however considerations will be required around non-standard data types.

Images

XC leverages Sitecore’s media library for hosting images and stores the Sitecore Id of the image on the ImagesComponent of the sellable item or variant it’s associated to.

Over in OrderCloud, products don’t have an explicit property for images as it relies on external systems, such as DAMs or CDNs, to host imagery instead. In addition, the OrderCloud philosphy is that as there is no one-size-fits-all solution when it comes to working with product images, so the absence of a dedicated property provides the flexibility of allowing a bespoke data model to be added to the product’s xp to best represent the client’s business requirements. Examples of potential requirements include, a single array of images (image urls), image sets representing different views or components of a product, image sets representing varying quaility or sizing for omni-channel optimisation. etc.

Relationship Definitions

XC has a concept of relationship definitions, which allows sellable items to have a relation to another sellable item with a given context. For example, the Habitat catalog relationship definitions for associated sellable items for installation, training, warranty, and most notably related sellable items.

Figure 6: Related sellable items relationship

The Commerce Engine and BizFx application provides an interface for creating these relationships and applies validation to ensure that the related products exist and are not duplicated. The Commerce Engine also includes smarts to filter out invalid sellable items for a given context, e.g. inactive sellable items, sellable items that are not associated to the current catalog, etc.

In OrderCloud, custom product to product assignments are not native in the platform, however a list of associated products can be added to the product’s xp, e.g. xp.RelatedProducts, to represent the relationship. To retrieve the products, a filtered product search, under the me resource, can be queried using the product’s IDs, which will automatically exclude products that don’t exist, are inactive, or are not assigned to the user, e.g. /me/products?ID=<Product B>|<Product C>|<Product D>.

Figure 7: XC related products association architecture vs. a sample approach using xp in OrderCloud.

Inventory and Pricing

The topics of inventory and pricing comparisons between XC and OrderCloud can be found in their dedicated article in Transitioning from Sitecore Experience Commerce to OrderCloud: Inventory and Pricing.

Other Considerations

Entity Versioning and Workflow

As OrderCloud has no concept of entity versioning, one approach towards migration is to only migrate the latest published versions of sellable items. In a similar manner the publishing workflow that applies to sellable items may see a project consider the latest entity version as the source of truth regardless of its published state. Considerations would need to be made on project by project basis, which may entail a level of data cleansing prior to migration.

Property Localisation

XC allows entity properties to be localisable for content that can be displayed in multiple languages. OrderCloud does not explicitly support property localisation, however localisation can be achieved by creating locale-specific entities as documented in How to Globalize your eCommerce.

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.

Products

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
OwnerIDstringNoN/AN/AN/A
DefaultPriceScheduleIDstringNoSellableItemListPriceMoneyPriceSchedule will be created from the SellableItem’s ListPrice, using the Product.ID as the Price Schedule’s ID.
AutoForwardboolNoN/AN/AN/A
IDstringNoSellableItemFriendlyIdstring
NamestringYesSellableItemDisplayNamestring
DescriptionstringNoN/AN/AN/A
QuantityMultiplierintNoN/AN/AN/A
ShipWeightfloatNo[ItemSpecificationsComponent]Weightdouble
ShipHeightfloatNo[ItemSpecificationsComponent]Heightdouble
ShipWidthfloatNo[ItemSpecificationsComponent]Widthdouble
ShipLengthfloatNo[ItemSpecificationsComponent]Lengthdouble
ActivebooleanNoSellableItemPublishedbooleanThis assumes only the latest published entity version is being migrated.
ShipFromAddressIDstringNoN/AN/AN/A
Inventory.EnabledbooleanNoN/AN/AN/AVirtual products won’t have inventory in either XC or OrderCloud. This should be set to disabled if any of the XC tags contain a value representing an XC virtual product.
Inventory.NotificationPointintNoN/AN/AN/A
Inventory.VariantLevelTrackingbooleanNoN/AN/AN/A
Inventory.OrderCanExceedbooleanNoN/AN/AN/A
Inventory.QuantityAvailableintNoInventoryInformationQuantityintIf transitioning a single inventory set/ record per product.
DefaultSupplierIDstringNoN/AN/AN/A
AllSuppliersCanSellbooleanNoN/AN/AN/A
xpobjectNoN/AN/AN/AXC composer views and programatic components can be added to xp as needed.
xp.BrandstringNoSellableItemBrandstring
xp.ManufacturerstringNoSellableItemManufacturerstring
xp.TypeOfGoodstringNoSellableItemTypeOfGoodstring
xp.TagslistNoSellableItemTagslistConvert to list of string using name property only.
xp.ItemDefinitionslistNo[CatalogsComponent].[CatalogComponent]ItemDefinitionstringThe ItemDefinition of each CatalogComponent should be added to the list, excluding duplicate values.
xp.RelatedProductslistNoRelationship (Commerce List)IdstringThe relationships are stored in lists, not entities. All Ids will need to be parsed to their friendly ids.

Specs

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
IDstringNoSellableItem
ItemVariationComponent
FriendlyId
<variation property>
stringRecommended using ‘{SellableItem.FriendlyId}_<variation property name>’ to create unique name.
ListOrderintegerNoN/AN/AN/A
NamestringYesItemVariationComponent<variation property>stringThe name of the variation property, not the value of the variation property.
DefaultValuestringNoN/AN/AN/A
RequiredbooleanNoN/AN/AN/ASet to true.
AllowOpenTextbooleanNoN/AN/AN/Afalse by default.
DefaultOptionIDstringNoN/AN/AN/A
DefinesVariantbooleanNoN/AN/AN/ASet to true.
xpobjectNoN/AN/AN/A

Spec Options

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
specIDstringYesSellableItem
ItemVariationComponent
FriendlyId
<variation property>
stringSee Spec Data Mapping ID property.
IDstringNoItemVariationComponent<variation property>stringThe value of the variation property.
ValuestringYesItemVariationComponent<variation property>stringThe value of the variation property.
ListOrderintegerNoN/AN/AN/A
IsOpenTextbooleanNoN/AN/AN/Afalse by default.
PriceMarkupTypestringNoN/AN/AN/A
PriceMarkupnumberNoN/AN/AN/A
xpobjectNoN/AN/AN/A

Spec Product Assignments

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
SpecIDstringYesSellableItem
ItemVariationComponent
FriendlyId
<variation property>
stringSee Spec Data Mapping ID property.
ProductIDstringYesSellableItemFriendlyIdstring
DefaultValuestringNoN/AN/AN/A
DefaultOptionIDstringNoN/AN/AN/A

Variants

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
productIDstringYesSellableItemN/AN/A
variantIDstringYesItemVariationComponentIdstring
IDstringNoItemVariationComponentIdstring
NamestringYesItemVariationComponentDisplayNamestring
DescriptionstringNoItemVariationComponentN/AN/A
ActivebooleanNoItemVariationComponentDisabledbooleanValue to be inverted. Set to false if not a valid variant.
ShipWeightfloatNo[ItemSpecificationsComponent]Weightdouble
ShipHeightfloatNo[ItemSpecificationsComponent]Heightdouble
ShipWidthfloatNo[ItemSpecificationsComponent]Widthdouble
ShipLengthfloatNo[ItemSpecificationsComponent]Lengthdouble
Inventory.QuantityAvailableintNoInventoryInformationQuantityintIf transitioning a single inventory set/ record per product.
xpobjectNoN/AN/AN/AXC composer views and programatic components can be added to xp as needed.
xp.TagslistNoSellableItemTagslistConvert to list of string using name property only.

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

Transitioning from Sitecore Experience Commerce to OrderCloud: Customers and Buyers – API Access

Reading Time: 7 minutes

In this article, we will review and compare API Access for Sitecore Experience Commerce customers and OrderCloud buyer users 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

Storefront API Access to the Commerce Systems

One aspect that developers tend to glaze over is how an SXA storefront interacts with the Commerce Engine. This is because the installation scripts apply the necessary configurations for the installed topology without the need for further manual intervention.

In an OrderCloud solution, the storefront will be configured to interact with the OrderCloud API server for the relevant region and environment, as documented in OrderCloud Supported Regions and Environments, that hosts the marketplace instance.

Resources

In the Commerce Engine, the CommerceODataController actions are registered to either the /api or /commerceops route, which creates the API under these two primary resources. The api route typically hosts actions that a storefront or the Commerce Business Tools (BizFx) would request, while the commerceops route hosts actions for dev ops interactions by dev ops users and the commerce data provider.

In OrderCloud, resources are not grouped up into resource buckets, however in the OrderCloud portal there is logical grouping within the UI to help navigate between related resources, as seen in figure 1.

For storefront users, the /me and /order resources will cover account management and creation of orders respectively.

Figure 1: Logical grouping of resources in the OrderCloud portal.

Authentication

The Sitecore Identity Server provides bearer token authentication for SXA storefront requests made to the Commerce Engine using the Commerce Engine Connect client, which is configured across Sitecore, Sitecore Identity Server, and Commerce Engine applications.

Figure 2: XC Authentication of Commerce Engine Connect for SXA storefront – Commerce Engine communications.

SXA customer registration and login functionality is accomplished using the SQL authorization provider, instead of the Sitecore Identify Server. This also segregates customer authentication data stored in the security database from commerce data stored in the commerce database.

The related commerce customer entity is created in the commerce database during account creation and the commerce customer reference is stored against the customer account in the security database.

Figure 3: Storefront users are created with a corresponding commerce customer entity to segregate membership data from commerce data.

In an OrderCloud storefront implementation, authentication is handled within OrderCloud itself using API clients, in a similar fashion to Sitecore Identity Server’s clients, returning limited lifetime bearer tokens.

Figure 4: OrderCloud authentication via API client overview.

Storefront user data is stored under a single object and users are typically authenticated directly with all subsequent requests containing the context of a storefront user.

Roles and Authorisation

Requests to the Commerce Engine will see the bearer token validated, which will determine the roles of the authenticated user and compare them to the roles of the API resource, which is configured in the Commerce Engine under the ControllerMethodRolesPolicy in Global.json.

{
  "$type": "Sitecore.Commerce.Core.ControllerMethodRolesPolicy, Sitecore.Commerce.Core",
  "ControllerMethodRegularExpression": "/commerceops/",
  "AuthorizedRoles": [
    "sitecore\\Commerce Business User",
    "commerce\\runtime"
  ]
},
{
  "$type": "Sitecore.Commerce.Core.ControllerMethodRolesPolicy, Sitecore.Commerce.Core",
  "ControllerMethodRegularExpression": "/api/",
  "AuthorizedRoles": [
    "sitecore\\Commerce Business User",
    "commerce\\runtime"
  ]
}

Requests not containing an authorized role will be rejected by the Commerce Engine with the exception of the Commerce Engine Connect client being a special case in that the Commerce Engine identifies this user and appends the commerce\runtime role to the request context to effectively brute force authorisation for its requests.

In contrast, OrderCloud has predefined roles for the various resources, which is detailed further in Understanding Security Profiles, providing more granular control over the resources a user has access to.

During the authentication process the roles are resolved from the user being authenticated, based on security profile assignments at the company, user group, and user levels.

To replicate the behaviour from XC, while having considerations for the necessary restrictions to API resources that wouldn’t be utilised by a storefront user, the following roles would be assigned to a security profile for storefront users.

{
  "Roles": [
    "Shopper",
    "MeAdmin",
    "MeXpAdmin",
    "MeAddressAdmin",
    "MeCreditCardAdmin",
    "PasswordReset"
  ]
}

Anonymous and Registered Users

In the SXA storefront, the customer id of anonymous/guest users and registered customers are passed in as a request header, which the Commerce Engine then resolves to the anonymous or registered user.

In OrderCloud, the user context needs to be resolved at the time of authentication, which will then be passed on to subsequent requests via the access token. Registered users are authenticated with username and password, while anonymous users are authenticated against an API client configured with an IsAnonBuyer flag and the default context user, which is used for resolving roles and not a user context when interacting with the OrderCloud API.

In figure 5, we see an API client configured with the IsAnonBuyer set as true and the DefaultContextUserName set to BUYER USER A. The roles configured to Security Profile A, which is assigned to Buyer A will be resolved when attempting to authenticate a registered buyer user or an anonymous user.

BUYER USER A is created for the specific purpose of being the anonymous user template as using a real user that can be modified or deleted at any time can have adverse effects on the anonymous user behaviour.

Figure 5: API client – buyer – security profile relationship and configuration example for registered and anonymous storefront users.

Service Proxy vs SDKs and Catalyst

The SXA storefront solution, including custom code, typically communicates with the Commerce Engine via the Service Proxy – an SDK containing Commerce Engine data models and strongly typed wrappers for the API. The Service Proxy simplifies custom development effort and complexity, which can also be regenerated after modifying the Commerce Engine to ensure the integrity of the library.

Figure 6: Commer Engine API access via the Service Proxy.

OrderCloud also provides a JavaScript and .Net SDK for the OrderCloud data models and strongly typed wrappers for all public endpoints. The JavaScript SDK allows requests to be made directly from the client browser rather than passed through middleware, whereas the .Net SDK is intended for the middleware for requests that typically require wrapped logic or even placeholder wrappers to insert logic at a later date.

The OrderCloud catalyst is another .Net library which provides additional helpers for authentication, performant bulk requests, error handling, jobs, project setup, etc.

Figure 7: OrderCloud API access via SDK and Catalyst libraries.

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.

Security Profile

The security profile mapped below represents storefront users only. The BizFx Business Tools users would have a different configuration, most notably for roles.

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
IDstringNoSite (Sitecore)Domainstring
NamestringYesSite (Sitecore)Domainstring
RolesarrayNoShopper, MeAdmin, MeXpAdmin, MeAddressAdmin, MeCreditCardAdmin, PasswordResetN/AN/A
CustomRolesarrayNoN/AN/AN/A
PasswordConfig.LimitPasswordReuseintegerNoN/AN/AN/A
PasswordConfig.MaxConsecutiveDupeCharsintegerNoN/AN/AN/A
PasswordConfig.MaximumPasswordAgeintegerNoN/AN/AN/A
PasswordConfig.MinimumPasswordAgeintegerNoN/AN/AN/A
PasswordConfig.AllowedFailedAttemptsintegerNo(web.config)
configuration/system.web/
membership/add[name=”sql”]
maxInvalidPasswordAttemptsstringSet to 5
PasswordConfig.LockoutDurationintegerNoN/AN/AN/A
PasswordConfig.UpperCaseRequiredbooleanNoN/AN/AN/A
PasswordConfig.LowerCaseRequiredbooleanNoN/AN/AN/A
PasswordConfig.SpecialCharacterRequiredbooleanNoN/AN/AN/A
PasswordConfig.NumericRequiredbooleanNoN/AN/AN/A
PasswordConfig.MinimumCharacterCountintegerNo(Commerce.XA) Registration.cshtml
RegistrationUserInputModel.cs
data_val_length_min
MinimumLength
string
integer
Sitecore’s default value is 6, while OrderCloud has a minimum value of 10.

Security Profile Assignment

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
SecurityProfileIDstringYesSite (Sitecore)Domainstring
BuyerIDstringNoSite (Sitecore)Domainstring
SupplierIDstringNoN/AN/AN/A
UserIDstringNoN/AN/AN/A
UserGroupIDstringNoN/AN/AN/A

Buyer User (Anonymous User)

The anonymous buyer user is created to represent an instance of an anonymous user. This user is fleshed out with hard-coded values for mandatory properties.

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
buyerIDstringYesSite (Sitecore)DomainstringIs a resource parameter, not body property.
IDstringNoN/AN/Astring“anonymous-user”
UsernamestringYesN/AN/Astring“{buyerId}-anonymous-user”
PasswordstringNoN/AN/AN/A
FirstNamestringYesN/AN/AN/A“Anonymous”
LastNamestringYesN/AN/AN/A“User”
EmailstringYesN/AN/AN/A“anonymous@user.com”
PhonestringNoN/AN/AN/A
TermsAcceptedstringNoN/AN/AN/A
ActivebooleanYesN/AN/AN/ASet to true
xpobjectNoN/AN/AN/A

API Client

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
ClientSecretstringNoN/AN/AN/A
AccessTokenDurationintegerYesN/AN/AN/ASet to 600 (maximum)
ActivebooleanNoN/AN/AN/ASet to true
AppNamestringYesSite (Sitecore)Domainstring
RefreshTokenDurationintegerNoN/AN/AN/A
DefaultContextUserNamestringNoAnonymous Buyer User (OrderCloud)Usernamestring
AllowAnyBuyerbooleanNoN/AN/AN/A
AllowAnySupplierbooleanNoN/AN/AN/A
AllowSellerbooleanNoN/AN/AN/A
IsAnonBuyerbooleanNoN/AN/AN/ASet to true
OrderCheckoutIntegrationEventIDstringNoN/AN/AN/A
MinimumRequiredRolesarrayNoN/AN/AN/A
MinimumRequiredCustomRolesarrayNoN/AN/AN/A
MaximumGrantedRolesarrayNoN/AN/AN/A
MaximumGrantedCustomRolesarrayNoN/AN/AN/A
xpobjectNoN/AN/AN/A

API Client Assignment

Technically, the API client assignment can be skipped and the AllowAnyBuyer property on the API client can be set to true instead, however this example creates an API client per storefront, which may be preferrable to allow the API client configuration to be modified later without affecting the other storefronts.

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
ApiClientIDstringYesApi Client (OrderCloud)IDstring
BuyerIDstringNoSite (Sitecore)Domainstring
SupplierIDstringNoN/AN/AN/A

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

Transitioning from Sitecore Experience Commerce to OrderCloud: Catalogs and Categories

Reading Time: 6 minutes

In this article, we will review and compare catalogs and categories 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

Catalogs and Categories

In XC, the structure of catalogs, categories, and sellable items/products can be recreated in OrderCloud. In a nutshell, categories are tied to their catalogs and can only have one parent, either another category or the catalog itself, while sellable items or products are separate to the catalog and can be assigned to one or more catalogs and/or categories.

In earlier versions of XC categories were allowed to have multiple parents, but support was subsequently removed. If an XC solution still contains categories with multiple parents this would be a consideration for pre-migration cleansing.

Figure 1: XC relationships between catalogs, categories, and sellable items.
Figure 2: OrderCloud relationships between catalogs, categories, and products.

The Storefront to Catalog Association

In Customer to Buyer Users we discussed that in XC, a storefront was required in order to associate a customer to a catalog. while OrderCloud has a direct assignment between buyers and catalogs. We will now revisit this to complete the context for catalogs.

In the SXA storefront, only a single catalog can be associated to the storefront, which is configured in the Content Editor under /sitecore/content/<tenant>/<site>/Home/Catalogs.

Figure 3: An example catalog configuration for a storefront.

Over in OrderCloud, a catalog assignment creates a link between a catalog and a buyer, bypassing the need for a storefront.

Figure 4: XC associates a catalog to a site while OrderCloud assigns catalogs directly to a buyer

The catalog assignment also contains two additional boolean properties in ViewAllCategories and ViewAllProducts, which as the names suggest allow all categories and products to be viewed by buyer users via the /me/products resource. These properties are important when understanding how XC customers can view sellable items vs OrderCloud buyers viewing products, which we cover in the next section SXA Scope Query vs Product Visibility.

SXA Scope Query vs Product Visibility

Now that we have established that the catalog structure can be identical between XC and OrderCloud, how do both systems handle product search functionality?

In an SXA storefront, there are two primary search scopes that are used for searching sellable items; the catalog scope and the category scope. These scopes are defined in the Content Editor under /sitecore/content/<tenant>/<site>/Settings/Scopes.

Figure 5: The catalog and category scopes in an SXA storefront.
Catalog Scope

The catalog scope is used by the global site search component, and will perform a search query returning all active sellable items associated to the catalog. An optional search term can be provided by the storefront user to further filter the results.

Referencing OrderCloud’s documentation, Product Visibility Requirements, we see Scenario 3 replicates the XC and the SXA storefront visibility rules for the catalog scope, excluding the search term filter. “When I assign a catalog to a buyer organization, I just want everybody in that organization to see everything in the catalog.” In short the ViewAllCategories and ViewAllProducts of a catalog assignment should be set to true.

Category Scope

The category scope is used for the category search results page and is just a more refined scope than the catalog scope in that it will return all active sellable items associated to a specific defined category. Again, an optional search term is available for further filtering.

The Search Term Filter

For the search term filter, the SXA query builder appends a comparison between the search term appends a fuzzy query against the sxacontent index field, which is an array consisting of the following sellable item properties:

  • Tags
  • ProductId
  • Name
  • DisplayName
  • ItemDefinition
  • Brand

The resulting query for a global search will look something like the following, with the highlighted section representing the example search term of “Spectra”.

In OrderCloud, we can achieve an equivalent query using the /me/products resource and applying a filter that allows us to filter on multiple fields with logical “OR” syntax to query each of the raw OrderCloud properties that make up the XC sxacontent index field.

  • Catalog scope: /me/products?catalogID=Habitat_Catalog&ID|Name|xp.Tags|xp.ItemDefinitions|xp.Brand=*Spectra*
  • Category scope: /me/products?catalogID=Habitat_Catalog&categoryID=Televisions&ID|Name|xp.Tags|xp.ItemDefinitions|xp.Brand=*Spectra*

As we will review in an upcoming article, the Name property tends to be a duplicate of the DisplayName, so we won’t bloat the product’s xp in OrderCloud with redundant data, however you can choose to include the Name property and amend the /me/products queries as necessary.

Catalog – Item Definitions

Item definitions allow composer templates to be associated with sellable items to create extended properties on the sellable items. The name of the item definition is also used in storefront search indexes for search components

From an OrderCloud perspective, extended properties (XP) can be used to represent an equivalent result. As represented in figure 6, all item definition properties are merged into a consistent xp schema along with the item definitions as its own xp, which will be indexed and available for search queries, product facets, etc.

Figure 6: The XC architecture for item definitions vs a sample architecture approach in OrderCloud.

Other Considerations

Entity Versioning and Workflow

In lieu of entity versioning in OrderCloud, one approach towards migration is to only migrate the latest published versions of catalogs and categories. In a similar manner the publishing workflow that applies to catalogs and categories may see a project consider the latest entity version as the source of truth regardless of its published state. Considerations would need to be made on project by project basis, which may entail a level of data cleansing.

Catalogs and Categories Pending Purge

A gotcha in migrating catalogs and categories is that the entities can still exist if they are pending purge, which is represented on the entity with an instance of PurgeCatalogsComponent or PurgeCategoriesComponent. These entities can be safely ignored during migration.

Property Localisation

XC provides entity properties to be localisable for content that can be displayed in multiple languages. OrderCloud does not explicitly support property localisation, however localisation can be achieved by creating locale-specific entities as documented in How to Globalize your eCommerce.

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.

Catalogs

Catalog mapping is fairly basic as there aren’t many properties on the catalogs of either system

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
IDstringYesCatalogFriendlyIdstring
OwnerIDstringNoN/AN/AN/A
NamestringYesCatalogDisplayNamestring
DescriptionstringNoN/AN/AN/A
ActivebooleanNoCatalogPublishedbooleanThis assumes only the latest published entity version is being migrated.
xpobjectNoN/AN/AN/AXC composer views and programatic components can be added to xp as needed.

Catalog Assignments

Catalog assignments are simply a relational mapping with the added control over category and product visibility.

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
CatalogIDstringYesCatalogFriendlyIdstring
BuyerIDstringNoN/AN/AN/AThe Sitecore Domain taken from the Storefront with the associated catalog.
ViewAllCategoriesbooleanNoN/AN/AN/ASet to true.
ViewAllProductsbooleanNoN/AN/AN/ASet to true.

Categories

XC orders categories alphabetically using list ids, while OrderCloud generates the ListOrder property, so this may result in different ordering between the two systems.

OC PropertyData TypeRequiredXC Entity/ComponentXC PropertyData TypeNotes
catalogIDstringYesCategoryFriendlyIdstringIs a resource parameter, not body property.
IDstringNoCategoryFriendlyIdstring
NamestringYesCategoryDisplayNamestring
DescriptionstringNoCategoryDescriptionN/A
ListOrderstringNoN/AN/AN/AListOrder will be generated (auto-increment) when creating categories.
ActivebooleanNoCategoryPublishedbooleanThis assumes only the latest published entity version is being migrated.
xpobjectNoN/AN/AN/AXC composer views and programatic 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