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
}
OrderCloud relies on the middleware application to supply tax calculations, similar to the Ship Estimates covered previously, which is triggered via calculate integration event; The /calculate endpoint calls the middleware's /ordercalculate endpoint to perform tax calculations as well as any line item or promotion cost overrides.
Complete feature parity is not something that can be achieved for the tax implementation purely because the middleware is responsible for providing tax, which could involve an integration with a tax provider or custom databases to store and retrieve tax data from. Instead, we will only suggest that by utilising the details covering XC's tax plugin above as considerations, along with the order calculate event integration event overview in figure 1, this may serve as a starting point for designing your own tax solution in OrderCloud.
The calling system in figure 1 may be the client-side storefront or the middleware, depending on the implementation details.
Figure 1: The order calculate integration event overview.
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 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 8, 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.
The /paymentoptions middleware endpoint retrieves the applicable payment options for the order, dependent on the storefront and business requirements.
The email address is PATCHed to the order xp for specific order context.
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 Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
buyerID
string
Yes
N/A
N/A
N/A
ID
string
No
GiftCard
GiftCardCode
string
Use format "GiftCard-<GiftCardCode>"
Name
string
Yes
GiftCard
Name
string
Balance
decimal
Yes
GiftCard
Balance.Amount
decimal
AllowAsPaymentMethod
boolean
No
N/A
N/A
N/A
Set to true.
RedemptionCode
string
No
GiftCard
GiftCardCode
string
StartDate
DateTimeOffset
No
GiftCard
ActivationDate
DateTimeOffset
EndDate
DateTimeOffset
No
N/A
N/A
N/A
xp
object
No
N/A
N/A
N/A
xp.Type
string
No
N/A
N/A
N/A
Set to "GiftCard".
xp.InitialAmount
decimal
No
GiftCard
OriginalAmount.Amount
decimal
xp.Currency
string
No
GiftCard
OriginalAmount.CurrencyCode
string
Recommended for handling multi-currency validation.
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.
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 OrderCloud API.
The /shippingoptions middleware endpoint retrieves the available shipping options for the order, such as order-level or line-level shipping, for different data collection based on the type of fulfillment required.
Shipping address(es) can be input manually or by referencing an existing address assigned to the user.
The ../estimateshipping integration event can leverage the shipping addresses for destination-specific logic.
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.
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
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 essentially combines the cart and order into the order object for the entire cart/order lifecycle. 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 inital 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:
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.
The Commerce Engine's /carts endpoint, which retrieves the cart, requires the cartId parameter and does not have consideration for the user context.
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, accomponied with a list entry, which represent the relationship between the customer and the orders they have placed.
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.
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 it's relationship to the user as it's tightly coupled to the owner via the FromUserID property.
The /me/orders endpoint retrieves the order based on user context, which is extracted from the auth token, rather than the order id.
The order id is also generated by default, so there's no chance of an ID conflict when allowing the platform to generate it.
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.
Storefront User Journey
In figure 3, 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 3: 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} endpoint, but it won't provide the line item details, which can be retrieved in bulk using GET /orders/{direction}/{orderID}/lineitems or individually via GET /orders/{direction}/{orderID}/lineitems/{lineitemID}.
Alternately, OrderCloud also provides GET /orders/{direction}/{orderID}/worksheet, which returns the order object along with all line items and all responses from integration events. providing a more complete view, however some objects such as applied promotions will still require additional requests from the orders resource to retrieve them on an as needed basis.
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 calculated by the middleware using OrderCloud's GET/orders/{direction}/{orderID}/payments endpoint.
Cart (XC)
Calculation
Order (OC)
Calculation
Match
SubTotal
Sum of all line-level SubTotals.
Subtotal
Sum of all LineItem.LineSubtotals.
Yes
AdjustmentsTotal
Sum of all cart-level Adjustments.
ShippingCost
Handled by middleware via OrderCalculate integration event.
No
TaxCost
Handled by middleware via OrderCalculate integration event.
No
PromotionDiscount
Sum of all line item-level promotion discount amounts applied.
No
GrandTotal
SubTotal + sum of all cart-level adjustments flagged with IncludeInGrandTotal being true + sum of all line-level AdjustmentsTotals
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.
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.
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 4: 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 5: 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.
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.
OrderCloud currently only stores the resolved UnitPrice on the line item, so while you can trust that the UnitPrice is resolved from the correct PriceBreak and prioritises SalePrice over Price, this information is not apparent.
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 pricecards resources can no longer be considered the source of truth. In the interim, you may wish to create a webhook to add the additional PriceSchedule data to the line item's xp for historical and reconciliation purposes.
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.
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 at the level.
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)
Calculation
LineItem (OC)
Calculation
Match
SubTotal
SellPrice * Quantity
LineSubtotal
UnitPrice * Quantity
Yes
AdjustmentsTotal
Sum of all line-level Adjustments.
PromotionDiscount
Sum of all line item-level promotion discount amounts applied for the current line item.
No
GrandTotal
SubTotal + sum of line-level adjustments, where adjustments are flagged with IncludeInGrandTotal being true.
LineTotal
LineSubtotal - PromotionDiscount
Yes
PaymentsTotal
Payments 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 6: 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 7: 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.
In OrderCloud, unsubmitted order cleanup is an internal automated process with the rules outlined below, which are not configurable.
User Type
Has Line Items?
Retention Policy
All
No
24 hours
Anonymous
Yes
7 days
Profiled
Yes
90 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 Property
Data Type
Required
Notes
direction
string
Yes
Is a resource parameter, not body property. Set to Outgoing for buyer users.
ID
string
No
Allowing the ID to be generated by the platform effectively matches XC order behaviour.
FromCompanyID
string
No
This value will default to the buyer, which effectively matches XC.
ToCompanyID
string
No
This value will default to the marketplace owner, which effectively matches XC.
FromUserID
string
No
Defaults to the current user context.
BillingAddressID
string
No
Not required at the time of creation.
ShippingAddressID
string
No
Not required at the time of creation.
Comments
string
No
ShippingCost
number
No
Not required at the time of creation.
TaxCost
number
No
Not required at the time of creation.
xp
object
No
Any 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 Property
Data Type
Required
XC Property
Notes
direction
string
Yes
Is a resource parameter, not body property. Set to Outgoing for buyer users.
orderID
string
Yes
Is a resource parameter, not body property. OrderCloud's order.ID
ID
string
No
Both XC and OrderCloud generate this ID by default.
ProductID
string
Yes
ItemId
Only the SellableItemId portion of the ItemId is required.
Quantity
string
Yes
Quantity
This value will default to the marketplace owner, which is effectively matches XC.
UnitPrice
string
No
Will be resolved from the PriceSchedule assignment of the current user context.
CostCenter
string
No
DateNeeded
string
No
ShippingAccount
string
No
ShippingAddressID
string
No
ShipFromAddressID
string
No
InventoryRecordID
string
No
Specs
object
No
ItemId
Used over variant ID. All spec IDs and specOptionIDs that represent the variant product will need to be specified.
In this article, we will review how we can redirect traffic from OrderCloud's integration events and webhooks to our local web server to assist debugging efforts in local development.
The OrderCloud Headstart application will be referenced in this example, which may differ slightly from your integration service/middleware application.
Introduction
As OrderCloud's integration events and webhooks require public urls to be available for testing and having complete functionality available, the question quickly arises as to how developers will be able to develop and debug the respective middleware endpoints in a local environment. One solution we will review is to use ngrok, a free service, which allows us to expose a web server running on our local machine to the internet.
Installation, Configuration, and Testing
High-level steps are provided below, however steps may vary based on operating systems and command tools. See ngrok: Getting Started > Setup & Installation, and register or log in to a free account, for alternative setup steps.
Now we can start HTTP tunnel forwarding to our local middleware.
ngrok http https://localhost:<port number>
The tunnel will then be created with a random URL, which we need to take note of for our OrderCloud configurations.
Figure 1: Randomly generated URL from grok.
Configuring OrderCloud's Integration Events
In OrderCloud, we can create or update an integration event using the generated URL from ngrok as the CustomImplementationUrl. The HashKey will also need to be configured to match the OrderCloudSettings:WebhookHashKey appsettings in HeadStart.
As ngrok does generate a random URL each time when using free accounts, the CustomImplementationUrl will need to be patched each time you expose the local web server.
If the integration event has been newly created, don't forget to assign it to the API client's OrderCheckoutIntegrationEventID so that the integration event can trigger the endpoints of the middleware.
When configuring webhooks, the Url will require the complete application service/middleware application endpoint to be defined, along with the HashKey, which should be configured to match the OrderCloudSettings:WebhookHashKey appsettings in HeadStart.
Webhooks also require the ApiClientIDs to be assigned on the resource itself rather that the API client and the WebhooksRoutes need to be specified in order for the OrderCloud API to trigger the webhook, which should then be redirected from ngrok through to the local web server.
With Visual Studio's debugger attached to the local IIS site or is being debugged directly, when an integration event or webhook endpoint is requested via an appropriate API client, ngrok will redirect the request to the local web server and the debugger will be able to capture the traffic for local debugging.
Figure 2: Visual Studio's debugger successfully triggered from OrderCloud's estimateshipping endpoint redirected to a localwebserver using grok.
Troubleshooting
An attempt was made to access a socket in a way forbidden by its access permissions
When attempting to run ngrok, if the response "An attempt was made to access a socket in a way forbidden by its access permissions" is received, restarting the Host Network Services Windows service should resolve this issue.
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.
While OrderCloud doesn't have explicit digital product flagging and accompanying behaviour, there are some options available for us to model products as digital items. For example, we can disable inventory tracking at the product level and copy the tags across to the product's extended properties (xp), and then have the middleware be responsible for using this information to treat the product as digital and manage the checkout process accordingly.
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:
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.
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 support any type of product bundling. It is possible to create a pseudo-bundle using a product, however this would need to be workshopped separately to cover data and functional requirements, which sits 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, there is no explicit product to product assignment available, however the list of associated products can be added to the product's xp, e.g. xp.RelatedProducts, to cover the data architecture aspect of this feature gap.
Figure 7: XC related products association architecture vs. a sample approach using xp in OrderCloud.
With a suggested data architecture addressed, the remaining considerations will reside in the functional behaviour. The following functionality may need to be implemented in the middleware of the OrderCloud solution to replicate the XC behaviour:
Validate related product exists on association/assignment.
Filter out products not eligible in the current context:
Inactive products
Products not associated to catalogs
When deleting a product, remove product id from other products that it is assigned to in their xp.RelatedProducts.
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 support localisation, so this may be a consideration for workshopping a solution to be handled by the custom middleware.
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 Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
OwnerID
string
No
N/A
N/A
N/A
DefaultPriceScheduleID
string
No
SellableItem
ListPrice
Money
PriceSchedule will be created from the SellableItem's ListPrice, using the Product.ID as the Price Schedule's ID.
AutoForward
bool
No
N/A
N/A
N/A
ID
string
No
SellableItem
FriendlyId
string
Name
string
Yes
SellableItem
DisplayName
string
Description
string
No
N/A
N/A
N/A
QuantityMultiplier
int
No
N/A
N/A
N/A
ShipWeight
float
No
[ItemSpecificationsComponent]
Weight
double
ShipHeight
float
No
[ItemSpecificationsComponent]
Height
double
ShipWidth
float
No
[ItemSpecificationsComponent]
Width
double
ShipLength
float
No
[ItemSpecificationsComponent]
Length
double
Active
boolean
No
SellableItem
Published
boolean
This assumes only the latest published entity version is being migrated.
ShipFromAddressID
string
No
N/A
N/A
N/A
Inventory.Enabled
boolean
No
N/A
N/A
N/A
Virtual 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.NotificationPoint
int
No
N/A
N/A
N/A
Inventory.VariantLevelTracking
boolean
No
N/A
N/A
N/A
Inventory.OrderCanExceed
boolean
No
N/A
N/A
N/A
Inventory.QuantityAvailable
int
No
InventoryInformation
Quantity
int
If transitioning a single inventory set/ record per product.
DefaultSupplierID
string
No
N/A
N/A
N/A
AllSuppliersCanSell
boolean
No
N/A
N/A
N/A
xp
object
No
N/A
N/A
N/A
XC composer views and programatic components can be added to xp as needed.
xp.Brand
string
No
SellableItem
Brand
string
xp.Manufacturer
string
No
SellableItem
Manufacturer
string
xp.TypeOfGood
string
No
SellableItem
TypeOfGood
string
xp.Tags
list
No
SellableItem
Tags
list
Convert to list of string using name property only.
xp.ItemDefinitions
list
No
[CatalogsComponent].[CatalogComponent]
ItemDefinition
string
The ItemDefinition of each CatalogComponent should be added to the list, excluding duplicate values.
xp.RelatedProducts
list
No
Relationship (Commerce List)
Id
string
The relationships are stored in lists, not entities. All Ids will need to be parsed to their friendly ids.
Specs
OC Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
ID
string
No
SellableItem ItemVariationComponent
FriendlyId <variation property>
string
Recommended using '{SellableItem.FriendlyId}_<variation property name>' to create unique name.
ListOrder
integer
No
N/A
N/A
N/A
Name
string
Yes
ItemVariationComponent
<variation property>
string
The name of the variation property, not the value of the variation property.
DefaultValue
string
No
N/A
N/A
N/A
Required
boolean
No
N/A
N/A
N/A
Set to true.
AllowOpenText
boolean
No
N/A
N/A
N/A
false by default.
DefaultOptionID
string
No
N/A
N/A
N/A
DefinesVariant
boolean
No
N/A
N/A
N/A
Set to true.
xp
object
No
N/A
N/A
N/A
Spec Options
OC Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
specID
string
Yes
SellableItem ItemVariationComponent
FriendlyId <variation property>
string
See Spec Data Mapping ID property.
ID
string
No
ItemVariationComponent
<variation property>
string
The value of the variation property.
Value
string
Yes
ItemVariationComponent
<variation property>
string
The value of the variation property.
ListOrder
integer
No
N/A
N/A
N/A
IsOpenText
boolean
No
N/A
N/A
N/A
false by default.
PriceMarkupType
string
No
N/A
N/A
N/A
PriceMarkup
number
No
N/A
N/A
N/A
xp
object
No
N/A
N/A
N/A
Spec Product Assignments
OC Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
SpecID
string
Yes
SellableItem ItemVariationComponent
FriendlyId <variation property>
string
See Spec Data Mapping ID property.
ProductID
string
Yes
SellableItem
FriendlyId
string
DefaultValue
string
No
N/A
N/A
N/A
DefaultOptionID
string
No
N/A
N/A
N/A
Variants
OC Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
productID
string
Yes
SellableItem
N/A
N/A
variantID
string
Yes
ItemVariationComponent
Id
string
ID
string
No
ItemVariationComponent
Id
string
Name
string
Yes
ItemVariationComponent
DisplayName
string
Description
string
No
ItemVariationComponent
N/A
N/A
Active
boolean
No
ItemVariationComponent
Disabled
boolean
Value to be inverted. Set to false if not a valid variant.
ShipWeight
float
No
[ItemSpecificationsComponent]
Weight
double
ShipHeight
float
No
[ItemSpecificationsComponent]
Height
double
ShipWidth
float
No
[ItemSpecificationsComponent]
Width
double
ShipLength
float
No
[ItemSpecificationsComponent]
Length
double
Inventory.QuantityAvailable
int
No
InventoryInformation
Quantity
int
If transitioning a single inventory set/ record per product.
xp
object
No
N/A
N/A
N/A
XC composer views and programatic components can be added to xp as needed.
xp.Tags
list
No
SellableItem
Tags
list
Convert to list of string using name property only.
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 Engine
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.
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.
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.
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 using the default context user an IsAnonBuyer flag configurations of the API client. Anonymous users only utilise the default context user for resolving roles and will not interact with OrderCloud using the default context user as the user context.
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 simplies 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.
Sitecore's default value is 6, while OrderCloud has a minimum value of 10.
Security Profile Assignment
OC Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
SecurityProfileID
string
Yes
Site (Sitecore)
Domain
string
BuyerID
string
No
Site (Sitecore)
Domain
string
SupplierID
string
No
N/A
N/A
N/A
UserID
string
No
N/A
N/A
N/A
UserGroupID
string
No
N/A
N/A
N/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 Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
buyerID
string
Yes
Site (Sitecore)
Domain
string
Is a resource parameter, not body property.
ID
string
No
N/A
N/A
string
"anonymous-user"
Username
string
Yes
N/A
N/A
string
"{buyerId}-anonymous-user"
Password
string
No
N/A
N/A
N/A
FirstName
string
Yes
N/A
N/A
N/A
"Anonymous"
LastName
string
Yes
N/A
N/A
N/A
"User"
Email
string
Yes
N/A
N/A
N/A
"anonymous@user.com"
Phone
string
No
N/A
N/A
N/A
TermsAccepted
string
No
N/A
N/A
N/A
Active
boolean
Yes
N/A
N/A
N/A
Set to true
xp
object
No
N/A
N/A
N/A
API Client
OC Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
ClientSecret
string
No
N/A
N/A
N/A
AccessTokenDuration
integer
Yes
N/A
N/A
N/A
Set to 600 (maximum)
Active
boolean
No
N/A
N/A
N/A
Set to true
AppName
string
Yes
Site (Sitecore)
Domain
string
RefreshTokenDuration
integer
No
N/A
N/A
N/A
DefaultContextUserName
string
No
Anonymous Buyer User (OrderCloud)
Username
string
AllowAnyBuyer
boolean
No
N/A
N/A
N/A
AllowAnySupplier
boolean
No
N/A
N/A
N/A
AllowSeller
boolean
No
N/A
N/A
N/A
IsAnonBuyer
boolean
No
N/A
N/A
N/A
Set to true
OrderCheckoutIntegrationEventID
string
No
N/A
N/A
N/A
MinimumRequiredRoles
array
No
N/A
N/A
N/A
MinimumRequiredCustomRoles
array
No
N/A
N/A
N/A
MaximumGrantedRoles
array
No
N/A
N/A
N/A
MaximumGrantedCustomRoles
array
No
N/A
N/A
N/A
xp
object
No
N/A
N/A
N/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.
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".
7512 07:28:09 INFO Solr Query - ?q=((((((((commercesearchitemtype_t:("sellableitem") AND _latestversion:("True"))
AND excludefromwebsitesearchresults_b:("False")) AND parentcataloglist_sm:("59ddadc19b88727e9e143f6cf321ae0f"))
AND (_path:("ccd5fda3aaba4f46b4f9b8bb7938bae5") AND searchable_b:("True")))
AND (sxacontent_txm:(*Spectra*) OR sxacontent_txm:("Spectra")))
AND _language:("en")) AND _latestversion:("True"))
AND ((_displayname:(*Furry*))^1000 OR (-_name:("") _name:[* TO *]))) AND _val_:__boost&start=0&rows=0&fq=_indexname:(sitecore_web_index)&wt=xml
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.
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
OrderCloud also has the capability of creating extended properties (XP), however it requires that the XP object on all Product objects be consistent to prevent breaking indexes, therefore we merge all non-standard properties into a consistent xp structure along side the item definition itself.
Figure 6: The XC architecture for item definitions vs a sample architecture approach in OrderCloud.
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 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 support localisation, so this may be a consideration for workshopping a solution to be handled by the custom middleware.
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 Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
ID
string
Yes
Catalog
FriendlyId
string
OwnerID
string
No
N/A
N/A
N/A
Name
string
Yes
Catalog
DisplayName
string
Description
string
No
N/A
N/A
N/A
Active
boolean
No
Catalog
Published
boolean
This assumes only the latest published entity version is being migrated.
xp
object
No
N/A
N/A
N/A
XC 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 Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
CatalogID
string
Yes
Catalog
FriendlyId
string
BuyerID
string
No
N/A
N/A
N/A
The Sitecore Domain taken from the Storefront with the associated catalog.
ViewAllCategories
boolean
No
N/A
N/A
N/A
Set to true.
ViewAllProducts
boolean
No
N/A
N/A
N/A
Set 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 Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
catalogID
string
Yes
Category
FriendlyId
string
Is a resource parameter, not body property.
ID
string
No
Category
FriendlyId
string
Name
string
Yes
Category
DisplayName
string
Description
string
No
Category
Description
N/A
ListOrder
string
No
N/A
N/A
N/A
ListOrder will be generated (auto-increment) when creating categories.
Active
boolean
No
Category
Published
boolean
This assumes only the latest published entity version is being migrated.
xp
object
No
N/A
N/A
N/A
XC composer views and programatic components can be added to xp as needed.
In this article, we will review and compare Sitecore Experience Commerce inventory and pricing entities against OrderCloud's inventory and pricing 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 the closest path to parity as possible in OrderCloud, using the Habitat catalog and SXA Storefront.
Conceptual Architecture and Features
Inventory
Single Inventory Approach
When it comes to inventory management, in its most basic form, a sellable item or item variation will contain inventory information on an associated entity within an inventory set. To be able to resolve the inventory, the sellable item must be associated to a catalog (or category within a catalog) and the catalog must be associated to the inventory set.
Figure 1: The sellable item and item variation relationship to inventory information.
In a more simplified comparison, OrderCloud also has a separate underlying inventory object assigned to a product or variant, however it's represented and managed as a sub-object of the product/variant. No relationship to a catalog or inventory set equivalent is required.
Figure 2: The product and variant relationship to inventory, equivalent to XC's standard inventory architecture for single inventory management.
Multi-Inventory Approach
When it comes to dealing with multiple catalogs in XC, the approach has a new layer of complexity; catalogs may have different associated inventory sets, which means that the inventory information entity the commerce engine resolves to is based on catalog-inventory set association.
Figure 3: Sellable item vs item variation inventory management in XC.
In OrderCloud, it is also possible to store multiple sets of inventory data using inventory records, providing two paths for inventory management. For implementations where single sets of inventory data will only ever be required, the traditional inventory management approach in figure 2 can be used. Where multi-inventory management is required, inventory records can be leveraged.
In OrderCloud's multi-inventory management approach, admin addresses are effectively the inventory set equivalent in creating a logical grouping of inventory records; catalog assignments are still not required to resolve inventory.
Multiple inventory records can be assigned to an address, and although it's not required in the XC to OrderCloud migration, it's mentioned simply for a more complete understanding of the OrderCloud architecture.
Figure 4: OrderCloud's inventory record approach for both single and multi-inventory support.
The most notable difference between the two systems is the Commerce Engine controls the logic to resolve the appropriate inventory information entity, while the middleware application of an OrderCloud implementation would be responsible for resolving applicable inventory records.
Inventory sets can represent the grouping of inventory information entities, typically for source or purpose, e.g. warehouse stock or reserved inventory for online sales. These are used in common customisations, such as click and collect, where more than one inventory set is utilised in a storefront. Using inventory records in OrderCloud can achieve the same result.
Inventory Properties
Apart from the expected quantity property, XC's inventory information also contains information such as invoice unit price and currency, preorderable and backorderable details. While the commerce engine does perform some logic for decrementing backorder and preorder inventory quantities, the implementation is not complete, therefore we won't treat this as a functional gap between the two systems and simply adding them to OrderCloud's extended properties (xp) will suffice from the data migration perspective.
Inventory records have the additional benefit of owning their xp. This means that additional properties such as preorderable and backorderable details better fit the context in the inventory record xp, over the product xp, which would be required when using the traditional product inventory approach.
Pricing
The pricing architecture is where we run into some challenges during an XC to OrderCloud migration. As XC supports both list prices and price cards at both the sellable item level and the item variation level, including a mix of both, we'll try and cover the most likely scenarios.
List Pricing Approach - Sellable Item Level
Starting with the most basic approach to pricing migration, sellable items have a list price policy that stores a list of multi-currency pricing. Note that item variations do not have list pricing in this example and will inherit from the sellable item. Also leaving the price card architecture out of the equation, the list pricing approach is represented in figure 5.
Figure 5: XC list pricing for sellable items and item variations.
In OrderCloud, pricing is managed via price schedule objects that are assigned to products. In order to replicate the XC architecture, a price schedule is required to represent each list price, then a product assignment is used to create the relationship between the product, the price schedule, and finally a buyer user group. The buyer user group represents the currency that will be resolved by the OrderCloud API and the custom middleware would then be responsible for moving a buyer user between the buyer groups.
Figure 6: A multi-currency approach for profiled users.
If we only need to support a single currency then we can take advantage of a more simplified approach using just the product and price schedule, using the DefaultPriceScheduleID.
Figure 7: OrderCloud single price approach for products and products with variants.
List Pricing Approach - Item Variation Level
XC also allows for list prices to be set at the item variation level, which includes fallback to the sellable item list pricing if not supplied. Excluding the fallback pricing, we see the following representation of a sellable item with list pricing configured against the item variations in figure 8.
Figure 8: XC list pricing at the variant level.
While OrderCloud does support price markups on spec options, which does alter variant pricing, the functionality is quite rigid and we will find that we won't be able to replicate variant level pricing that XC allows.
In XC, a price book can be associated to multiple catalogs, which represents the price book that the catalog will attempt to resolve price cards from for a given sellable item.
Figure 9: Price Books can be associated to more than one catalog.
Price cards can be resolved, by name or by tag association, to more than one price card across multiple price books. The Commerce Engine contains some additional logic to identify the eligible price cards, prioritise them, and determine the winning price card.
Figure 10: Sellable items are resolved back to a price book based on catalog-price book associations, however multiple price cards may also be resolved. Associations may be resolved by price card name or by tag.
Scheduled Pricing
Taking a step back to look at the minimal configuration of a single price card associated to a sellable item, the price snapshots on the price card represent scheduled pricing. Each snapshot supports multi-currency tiered pricing, and also has approval workflow to prevent new pricing from accidently going live.
Figure 11: Resolving a price from a single price card association in XC.
In OrderCloud, the price schedule contains PriceBreaks, allowing for tier pricing for a single currency. The SaleStart and SaleEnd date/time properties dictate the timeframe when the SalePrice values are active, which is also flagged by the calculated property IsOnSale.
PriceSchedule assignments support price scheduling without needing to differ from the assignment architecture shown in figure 6 and figure 7.
With an undertanding of XC price card functionality, OrderCloud does not have the capabilities to replicate the functionality. A summary of the challenges facing price card migration are documented in the table below.
XC
OrderCloud
A sellable item can resolve to multiple price cards, which has logic to prioritise and resolve a single price card.
Where OrderCloud has multiple price schedules assigned at the same level of specificity, there is no logic to identify which price schedule should be prioritised, which may result in unexpected pricing being applied.
Price cards are grouped into price books, which can be resolved by the price book association of the catalog.
Price schedules can be assigned to a specific buyer group, however this is not enough to replicate XC behaviour for determining price by catalog assignment.
Price cards may contain multiple price snapshots for scheduled pricing support.
Price schedules allow for sale pricing, based on a specific date and time, however I future sale price cannot be prepared ahead of time if a sale price is currently active.
Using price cards without list pricing allows standard pricing to be scheduled.
Price schedules only allow sale pricing to be scheduled, however the standard price may still be updated in real-time.
Price cards support multi-currency.
OrderCloud has partial multi-currency support, however there is no support for products that require variant-specific pricing.
XC pricing has support for item variation-specific pricing.
OrderCloud has no support for products that require variant-specific pricing, although it does have price markups per spec option, which does not align with XC's item variation-specific pricing.
XC Price Snapshots have approval workflow.
Price schedules have no approval workflow.
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.
Admin Addresses
While admin addresses are a representation of inventory sets, there are a number of mandatory fields for address details that do not exist in XC. These fields can either be filled out with dummy data or enriched with the appropriate values.
OC Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
ID
string
No
InventorySet
FriendlyId
string
CompanyName
string
No
N/A
N/A
N/A
FirstName
string
Yes
N/A
N/A
N/A
LastName
string
No
N/A
N/A
N/A
Street1
string
Yes
N/A
N/A
N/A
Sample data will need to be supplied.
Street2
string
No
N/A
N/A
N/A
City
string
Yes
N/A
N/A
N/A
Sample data will need to be supplied.
State
string
Yes
N/A
N/A
N/A
Sample data will need to be supplied.
Zip
string
Yes
N/A
N/A
N/A
Sample data will need to be supplied.
Country
string
Yes
N/A
N/A
N/A
Sample data will need to be supplied.
Phone
string
No
N/A
N/A
N/A
AddressName
string
No
InventorySet
DisplayName
string
xp
object
No
N/A
N/A
N/A
xp.Description
string
No
InventorySet
Description
string
Inventory Records
The xp in inventory records should only be mapped if they are being used in the XC implementation to avoid unnecessary bloating of the xp. The xp mapping can also be used on the product for the traditional inventory management approach.
OC Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
productID
string
Yes
SellableItem
FriendlyId
string
ID
string
No
InventoryInformation
FriendlyId
string
OwnerID
string
No
N/A
N/A
N/A
AddressID
string
No
InventorySet
FriendlyId
string
OrderCanExceed
boolean
No
N/A
N/A
N/A
QuantityAvailable
integer
No
InventoryInformation
Quantity
integer
xp
object
No
N/A
N/A
N/A
xp.InvoiceUnitAmount
decimal
No
InventoryInformation
InvoiceUnitPrice.Amount
decimal
xp.InvoiceUnitCurrency
string
No
InventoryInformation
InvoiceUnitPrice.CurrencyCode
string
xp.Preorderable
boolean
No
[PreorderableComponent]
Preorderable
boolean
xp.PreorderAvailabilityDate
datetimeoffset
No
[PreorderableComponent]
PreorderAvailabilityDate
datetimeoffset
xp.PreorderedQuantity
integer
No
[PreorderableComponent]
PreorderedQuantity
integer
xp.PreorderLimit
integer
No
[PreorderableComponent]
PreorderLimit
integer
xp.Backorderable
boolean
No
[BackorderableComponent]
Backorderable
boolean
xp.BackorderAvailabilityDate
datetimeoffset
No
[BackorderableComponent]
BackorderAvailabilityDate
datetimeoffset
xp.BackorderedQuantity
integer
No
[BackorderableComponent]
BackorderedQuantity
integer
xp.BackorderLimit
integer
No
[BackorderableComponent]
BackorderLimit
integer
Price Schedules
As price schedules only support the equivalent of static list pricing of XC's sellable items, this data mapping only covers this scenario.
OC Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
OwnerID
string
No
N/A
N/A
N/A
ID
string
No
SellableItem [ListPricePolicy].Prices
FriendlyId CurrencyCode
string string
Name
string
Yes
SellableItem [ListPricePolicy].Prices
FriendlyId CurrencyCode
string string
ApplyTax
string
No
N/A
N/A
N/A
ApplyShipping
boolean
No
N/A
N/A
N/A
MinQuantity
integer
No
N/A
N/A
N/A
MaxQuantity
integer
No
~[LineQuantityPolicy]
Maximum
decimal
XC stores this in the environment policies.
UseCumulativeQuantity
boolean
No
~[RollupCartLinesPolicy]
Rollup
boolean
XC stores this in the environment policies.
RestrictedQuantity
boolean
No
N/A
N/A
N/A
PriceBreaks
array
No
N/A
N/A
N/A
PriceBreaks.Quantity
integer
No
N/A
N/A
N/A
Set to 1.
PriceBreaks.Price
float
No
[ListPricePolicy].Prices
Amount
decimal
For a given price in the [ListPricePolicy].Prices array.
Currency
string
No
[ListPricePolicy].Prices
CurrencyCode
string
For a given price in the [ListPricePolicy].Prices array.
In this article, we will review and compare 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.
Customers
The Customer to Storefront Association
In an XC SXA implementation, customer accounts are registered under a security domain, which can be configured in the Content Editor on the /sitecore/Content/<tenant>/<site>/Settings/Site Grouping/<storefront> item.
Figure 1: An example domain configuration for a storefront.
Customers can only be registered to a single security domain and will not be accessible in storefronts configured to a different domain. A separate customer account would need to be created under the other security domains, but it would have no knowledge or relationship to accounts across domains. However, a security domain can be registered to multiple storefronts allowing a customer to share an account across storefronts.
Figure 2: XC Security Domains have a zero to many relationship with Storefronts.
In OrderCloud, customers can be represented as buyer users, which are registered under buyer companies, and can only belong to a single buyer company. This can be compared directly to the relationship between security domains and customers.
The relationship of a buyer to an OrderCloud storefront is not tightly coupled like the SXA storefront. Access to an OrderCloud storefront is managed through API clients, security profiles and their assignments to buyers. See Getting Started > Establishing API Access for more information.
Figure 3: The basic structure comparison between customers to users.
The Customer to Catalog Association
Buyers also contain a DefaultCatalogID property, which will create an associated catalog with an ID matching the buyer ID if not specified during the creation of a buyer. While it may seem like this limits Buyer A to a single catalog (Catalog A), multiple catalogs can be assigned via OrderCloud's catalog assignments, which matches XC's equivalent in supporting both Catalog A and Catalog C associations to Security Domain A via storefronts (figure 4).
The DefaultCatalogID property will be utilised in certain APIs when a catalogID is not supplied in their request, however is can be overridden. For now it is only important to understand that this is not a limiting factor in OrderCloud functionality for migration of data or functionality.
Figure 4: XC relationship between customers and catalogs vs. OrderCloud's relationship between buyer users and catalogs.
OrderCloud User Groups
OrderCloud also allows buyer users to be assigned to one or more buyer user groups, which can be used to supersede assignments to buyer users in bulk, e.g. provide access to special pricing for VIP buyer users.
Figure 5: OrderCloud can group buyer users in buyer user groups.
Over in XC, a common customisation is an implementation of customer groups to assign customers in order to provide customer-group specific pricing, promotions, etc. While the implementation details may have the customer group represented as a separate custom entity or a property on a customer, conceptually both approaches achieve the same outcome (figure 6) and are on par with OrderCloud's architecture (figure 5), allowing a seamless transition path to OrderCloud.
Figure 6: Customer groups are a common customisation in XC, which can be thought of in a simliar respect to OrderCloud's buyer user groups, however it is not part of the default ecosystem.
Customer Addresses
In XC, customers have explicit addresses associations as the address component lives on the customer entity. The IsPrimary flag is used to allow a default address selection in implementations.
Figure 7: Customer addresses in XC.
In OrderCloud, addresses are separate objects that have an immediate association to the buyer, then with buyer address assignments addresses can have direct assignments to the buyer users or indirect assignments via buyer user groups.
Figure 8: Buyer address architecture in OrderCloud.
To bring across customer addresses into OrderCloud figure 9 shows that we would simply create buyer addresses with buyer address assignments for the buyer user only, not the buyer user groups, as well as adding the IsPrimary flag to the buyer address xp.
Figure 9: OrderCloud's representation of XC customer structure.
For buyer address assignments, there are two additional properties to consider, in IsShipping and IsBilling. These properties control whether the address can be assigned to an order as a shipping address or billing address respectively. As the SXA Storefront allows customer addresses to be used for both of these addresses, these flags on the buyer address assignments should be set to true.
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.
Buyers
The DefaultCatalogID property will be reviewed further during the catalog analysis article.
OC Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
ID
string
No
Customer
Domain
string
Active
string
Yes
N/A
N/A
N/A
Set to true by default.
Name
string
No
Customer
Domain
string
DefaultCatalogID
string
Yes
N/A
N/A
N/A
Assign to an existing catalog or PATCH later.
xp
object
No
N/A
N/A
N/A
Any custom fields can be mapped to xp.
Buyer Users
There are a few properties in OrderCloud are required while not mandatory in the SXA Storefront, so considerations for handling these properties will need to be made in these instances, e.g. applying fallback values.
OC Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
buyerID
string
Yes
Customer
Domain
string
Is a resource parameter, not body property.
ID
string
No
Customer
FriendlyId
string
Username
string
Yes
Customer
LoginName
string
Password
string
No
N/A
N/A
N/A
Password should not be migrated.
FirstName
string
Yes
Customer
FirstName
string
Not a mandatory field in SXA, so a fallback value will need to be populated.
LastName
string
Yes
Customer
LastName
string
Not a mandatory field in SXA, so a fallback value will need to be populated.
Email
string
Yes
Customer
Email
string
Phone
string
No
CustomerDetailsComponent
PhoneNumber
string
The XC PhoneNumber exists under an entity view of the CustomerDetailsComponent, so consider this a rough mapping.
TermsAccepted
string
No
N/A
N/A
N/A
Active
boolean
Yes
Customer
AccountStatus
string
Set to true where AccountStatus == "ActiveAccount"
xp
object
No
N/A
N/A
N/A
Any custom fields can be mapped to xp.
Buyer Addresses
For buyer addresses, IDs need to be unique, therefore creating a convention to append the customer's FriendlyId and the XC address' unique identifier, AddressName, ensures there will be no conflicts.
OrderCloud only stores the ISO 3166-1 alpha-2 2-letter country code. Retrieving additional country details, such as country name, would best be handled by custom middleware, outside of OrderCloud.
OC Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
buyerID
string
Yes
Customer
Domain
string
Is a resource parameter, not body property.
ID
string
No
Customer AddressComponent
FriendlyId Party.AddressName
string
Using the convention <Customer.FriendlyId>_<Party.AddressName> will keep the address ID unique.
FirstName
string
No
AddressComponent
FirstName
string
SXA Storefront does not capture FirstName. May wish to use Customer.FirstName instead.
LastName
string
Yes
AddressComponent
LastName
string
SXA Storefront does not capture LastName. May wish to use Customer.LastName instead.
Street1
string
Yes
AddressComponent
Party.Address1
string
Street2
string
No
AddressComponent
Party.Address2
string
City
string
Yes
AddressComponent
Party.City
string
State
string
Yes
AddressComponent
Party.StateCode
string
Can also use Party.State for full state name, depending on requirement.
Zip
string
Yes
AddressComponent
Party.ZipPostalCode
string
Country
string
Yes
AddressComponent
Party.CountryCode
string
Phone
string
No
AddressComponent
Party.PhoneNumber
string
Will be empty in SXA Storefront default implementation. May wish to use Customer.[CustomerDetailsComponent].PhoneNumber instead.
AddressName
string
No
AddressComponent
Party.AddressName
string
xp
object
No
N/A
N/A
N/A
Any custom fields can be mapped to xp.
xp.IsPrimary
object
No
AddressComponent
Party.IsPrimary
boolean
Buyer Address Assignments
Buyer address assignments are a straight forward mapping as per the table below.
OC Property
Data Type
Required
XC Entity/Component
XC Property
Data Type
Notes
buyerID
string
Yes
Customer
Domain
string
Is a resource parameter, not body property.
AddressID
string
Yes
Customer AddressComponent
FriendlyId Party.AddressName
string
<Customer.FriendlyId>_<Party.AddressName> to keep the address ID unique
In this article, we will review how to extend the schema of catalog, category, and sellable item (including its variants), entities by programatically creating and assigning components to them, render the components to their respective entity page in the BizFx, and allow the CommerceConnect dataprovider to consume the component data when syncing the catalog items to Sitecore.
The usage of the term 'catalog items' throughout this article is a collective term referring to catalog, category and sellable item commerce entities as well as variants, a.k.a. item variation components.
Implementation Details
Components can be used as a programmatic alternative to composer templates to extend catalog items. Components can be more powerful than composer templates as developers have more control over the implementation of custom components, although the trade off requires additional development effort. Some additional highlights of differences between composer templates and components are listed below.
Composer templates cannot be utilised to extend variants, only the sellable items themselves, therefore components are required to extend variants.
Components do not require developers to create a migration plan to move composer templates between deployment environments.
Composer templates are restricted to a specific set of data types for custom properties, while components can utilise a larger set of data types and UI Types for improved business user experience.
Components require get view blocks, populate view action blocks, and do action blocks to allow business users to view and edit component properties within the Commerce Tools, whereas the composer templates requires configuration, which can be completed controlled by a business user.
Creating New Components
We first create our components that will extend our catalog items and add our custom properties.
For extending our catalog items, only samples for sellable items and variants will be provided, however to extend categories and catalogs, this would be achieved by following the sellable item samples and substituting sellable item references with that of category or catalog references.
public class SellableItemExtensionComponent : Component
{
public string CountryOfOrigin { get; set; } = "Australia";
public int EnergyRating { get; set; } = 10;
}
public class VariationExtensionComponent : Component
{
public string Material { get; set; } = "Steel";
public bool IsClearance{ get; set; } = true;
}
Default values for properties are usually omitted in implementations, however values have been assigned here to demonstrate reading these values for the get view blocks in the next step.
Rendering Components in BizFx Using Get View Blocks
To render the properties in the Commerce Tools, we add entity views to the appropriate Merchandising pages using get view blocks and registering them to IGetEntityViewPipeline.
Important: Ensure component property names match view property names. When translating components to entity views, it is not only good practice to assign the name of the view property with the corresponding name of component property, but it is crucial for Commerce Connect data provider to populate the values of the Sitecore catalog items. The data provider effectively maps properties by matching names if the Sitecore catalog item field names to the commerce entity properties from its representation within a JToken.
public class GetSellableItemExtensionViewBlock : SyncPipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
{
public override EntityView Run(EntityView entityView, CommercePipelineExecutionContext context)
{
// Ensure parameters are provided
Condition.Requires(entityView, nameof(entityView)).IsNotNull();
Condition.Requires(context, nameof(context)).IsNotNull();
var viewsPolicy = context.GetPolicy<KnownCatalogViewsPolicy>();
var request = context.CommerceContext.GetObject<EntityViewArgument>();
// Validate that the page entity is a sellable item
// Validate the view name matches the requested page
if (!(request?.Entity is SellableItem) ||
!request.ViewName.EqualsOrdinalIgnoreCase(viewsPolicy.Master))
{
return entityView;
}
var sellableItem = request.Entity as SellableItem;
var extensionComponent = sellableItem.GetComponent<SellableItemExtensionComponent>();
// Create an entity view to hold the component properties
var propertiesEntityView = new EntityView
{
DisplayName = "Extended Information",
Name = "Extended Information",
EntityId = entityView.EntityId,
EntityVersion = entityView.EntityVersion,
ItemId = entityView.ItemId
};
// Add the entity view as a child of the page entity view
entityView.ChildViews.Add(propertiesEntityView);
// Add the component properties to the entity view
propertiesEntityView.Properties = new List<ViewProperty>
{
CreateViewProperty(nameof(extensionComponent.CountryOfOrigin), extensionComponent.CountryOfOrigin),
CreateViewProperty(nameof(extensionComponent.EnergyRating), extensionComponent.EnergyRating)
};
return entityView;
}
/// Helper method to simplify ViewProperty creation
private ViewProperty CreateViewProperty(string name, object value, string uiType = "")
{
return new ViewProperty
{
Name = name,
RawValue = value,
UiType = uiType
};
}
}
public class GetVariationExtensionViewBlock : SyncPipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
{
public override EntityView Run(EntityView entityView, CommercePipelineExecutionContext context)
{
// Ensure parameters are provided
Condition.Requires(entityView, nameof(entityView)).IsNotNull();
Condition.Requires(context, nameof(context)).IsNotNull();
var viewsPolicy = context.GetPolicy<KnownCatalogViewsPolicy>();
var request = context.CommerceContext.GetObject<EntityViewArgument>();
// Validate that the page entity is a sellable item
// Validate the view name matches the requested page
if (!(request?.Entity is SellableItem) ||
!request.ViewName.EqualsOrdinalIgnoreCase(viewsPolicy.Variant))
{
return entityView;
}
var sellableItem = request.Entity as SellableItem;
// Get the component from the sellable items variant (ItemId) and do not inherit from the sellable item if not present on the variant
var extensionComponent = sellableItem.GetComponent<VariationExtensionComponent>(entityView.ItemId, false);
// Create an entity view to hold the component properties
var propertiesEntityView = new EntityView
{
DisplayName = "Extended Information",
Name = "Extended Information",
EntityId = entityView.EntityId,
EntityVersion = entityView.EntityVersion,
ItemId = entityView.ItemId
};
entityView.ChildViews.Add(propertiesEntityView);
// Add the entity view as a child of the page entity view
propertiesEntityView.Properties = new List<ViewProperty>
{
CreateViewProperty(nameof(extensionComponent.Material), extensionComponent.Material),
CreateViewProperty(nameof(extensionComponent.IsClearance), extensionComponent.IsClearance)
};
return entityView;
}
/// Helper method to simplify ViewProperty creation
private ViewProperty CreateViewProperty(string name, object value, string uiType = "")
{
return new ViewProperty
{
Name = name,
RawValue = value,
UiType = uiType
};
}
}
The get view blocks are registered to IGetEntityViewPipeline in the pipeline configuration.
public class ConfigureSitecore : IConfigureSitecore
{
public void ConfigureServices(IServiceCollection services)
{
var assembly = Assembly.GetExecutingAssembly();
services.RegisterAllPipelineBlocks(assembly);
services.RegisterAllCommands(assembly);
services.Sitecore().Rules(config => config.Registry(registry => registry.RegisterAssembly(assembly)));
services.Sitecore().Pipelines(pipelines => pipelines
.ConfigurePipeline<IGetEntityViewPipeline>(pipeline => pipeline
.Add<Pipelines.Blocks.GetSellableItemExtensionViewBlock>().After<PopulateEntityVersionBlock>()
.Add<Pipelines.Blocks.GetVariationExtensionViewBlock>().After<PopulateEntityVersionBlock>()
)
);
}
}
Upon deployment, we can see the new entity views appear on the Sellable Item and Variant pages with our default values.
SellableItemExtensionComponent rendered as Extended Information on the Sellable Item Details Page.VariationExtensionComponent rendered as Extended Information on the Variant Details Page.
View property display names can be managed via Commerce terms under the Commerce Control Panel in the Sitecore Content Editor, allowing language specific user-friendly names to be configured for the property names rendered in the entity views.
Registering Components for Sitecore Catalog Item Templates
With components translated to entity views and rendered on our Merchandising pages, we need to register our component properties so that the Commerce Connect data provider can updating the commerce data templates in Sitecore.
In the screenshot below, registered components can be seen under /sitecore/templates/Commerce/Catalog Generated/Components/ConnectSellableItem, which are registered by the data provider to the ConnectSellableItem template.
Both sellable items and variants share the same template. This means that properties registered to a sellable item will appear on the variant and vice versa.
To register our components, we simply need to include an additional view name in the validation section of our get view blocks. This view name would be either ConnectCatalog, ConnectCategory, or ConnectSellableItem, depending of the entity context.
if (!(request?.Entity is SellableItem) ||
(!request.ViewName.EqualsOrdinalIgnoreCase(viewsPolicy.Master) &&
!request.ViewName.EqualsOrdinalIgnoreCase(viewsPolicy.ConnectSellableItem)))
{
return entityView;
}
if (!(request?.Entity is SellableItem) ||
(!request.ViewName.EqualsOrdinalIgnoreCase(viewsPolicy.Variant) &&
!request.ViewName.EqualsOrdinalIgnoreCase(viewsPolicy.ConnectSellableItem)))
{
return entityView;
}
We should now see our components registered as templates in Sitecore.
Once the changes have been deployed, if the changes are not reflected in Sitecore, run the Update Data Templates command from the Commerce ribbon menu.
As we have created the entity view with the name 'Extended Information' for both sellable items and variants, properties are merged into a single template.
Populating Component Property Values on Sitecore Items
Our templates have been generated in Sitecore, however if we look at a sellable item or variant Sitecore item, we will find that the values have not been populated. This is because the data provider uses different Commerce Engine APIs to generate the templates and populate the Sitecore catalog item property values.
In our get view block examples, GetComponent<SellableItemExtensionComponent>() is creating a new component, but is not persisted to the sellable item and its variants. The data provider populates Sitecore items from each instance of the commerce entity, rather than our entity view representation of the commerce entity, which is why no values are populated at this time.
Important: Ensure component properties are unique across the commerce entity. The data provider essentially flattens the commerce entity when populating the Sitecore catalog items. This means that if a property with the same name exists multiple times across the commerce entity and its nested component properties, the data provider will use the first instance to populate the Sitecore catalog items.
Sitecore Content Editor: Extended Information values have not been populated
In order for us to populate the values, we need to take a step back to populate and persist property values to the catalog items in the Commerce Engine.
First, we will register populate view actions blocks, containing an edit action, to the Extended Information entity views in the Merchandising pages.
public class PopulateSellableItemExtensionViewActionsBlock : SyncPipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
{
public override EntityView Run(EntityView entityView, CommercePipelineExecutionContext context)
{
// Ensure parameters are provided
Condition.Requires(entityView, nameof(entityView)).IsNotNull();
var viewsPolicy = context.GetPolicy<KnownCatalogViewsPolicy>();
var request = context.CommerceContext.GetObject<EntityViewArgument>();
// Validate the context of the request, i.e. entity, view name, and action name
if (!(request?.Entity is SellableItem) ||
!request.ViewName.EqualsOrdinalIgnoreCase(viewsPolicy.Master) ||
!entityView.Name.EqualsOrdinalIgnoreCase("Extended Information") ||
!string.IsNullOrEmpty(request.ForAction))
{
return entityView;
}
// Add action to entity view
var actionPolicy = entityView.GetPolicy<ActionsPolicy>();
actionPolicy.Actions.Add(
new EntityActionView
{
Name = "EditExtensionProperties",
DisplayName = "Edit Extension Properties",
Description = "Edit Extension Properties",
IsEnabled = true,
EntityView = "ExtensionProperties",
Icon = "edit"
});
return entityView;
}
}
public class PopulateVariationExtensionViewActionsBlock : SyncPipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
{
/// <summary>Executes the pipeline block's code logic.</summary>
/// <param name="entityView">The entity view.</param>
/// <param name="context">The context.</param>
/// <returns>The <see cref="EntityView"/>.</returns>
public override EntityView Run(EntityView entityView, CommercePipelineExecutionContext context)
{
// Ensure parameters are provided
Condition.Requires(entityView, nameof(entityView)).IsNotNull();
var viewsPolicy = context.GetPolicy<KnownCatalogViewsPolicy>();
var request = context.CommerceContext.GetObject<EntityViewArgument>();
// Validate the context of the request, i.e. entity, view name, and action name
if (!(request?.Entity is SellableItem) ||
!request.ViewName.EqualsOrdinalIgnoreCase(viewsPolicy.Variant) ||
!entityView.Name.EqualsOrdinalIgnoreCase("Extended Information") ||
!string.IsNullOrEmpty(request.ForAction))
{
return entityView;
}
// Add action to entity view
var actionPolicy = entityView.GetPolicy<ActionsPolicy>();
actionPolicy.Actions.Add(
new EntityActionView
{
Name = "EditExtensionProperties",
DisplayName = "Edit Extension Properties",
Description = "Edit Extension Properties",
IsEnabled = true,
EntityView = "ExtensionProperties",
Icon = "edit"
});
return entityView;
}
}
The get view blocks created earlier are then amended and refactored to include support for the create/edit action, which will render the entity view in the modal.
public override EntityView Run(EntityView entityView, CommercePipelineExecutionContext context)
{
// Ensure parameters are provided
Condition.Requires(entityView, nameof(entityView)).IsNotNull();
Condition.Requires(context, nameof(context)).IsNotNull();
var viewsPolicy = context.GetPolicy<KnownCatalogViewsPolicy>();
var request = context.CommerceContext.GetObject<EntityViewArgument>();
// Determine the context of rendering the entity view, e.g. create/edit or view (page or via commerce context)
var isViewAction = (request.ViewName.EqualsOrdinalIgnoreCase(viewsPolicy.Master) ||
request.ViewName.EqualsOrdinalIgnoreCase(viewsPolicy.ConnectSellableItem)) &&
string.IsNullOrWhiteSpace(request.ForAction);
var isEditAction = request.ViewName.EqualsOrdinalIgnoreCase("ExtensionProperties") && request.ForAction.EqualsOrdinalIgnoreCase("EditExtensionProperties");
// Validate the context of the request, i.e. entity, view name, and action name
if (!(request?.Entity is SellableItem) ||
(!isViewAction &&
!isEditAction))
{
return entityView;
}
var sellableItem = request.Entity as SellableItem;
var extensionComponent = sellableItem.GetComponent<SellableItemExtensionComponent>();
var propertiesEntityView = entityView;
if (isViewAction)
{
// Create an entity view to host the component properties
propertiesEntityView = new EntityView
{
DisplayName = "Extended Information",
Name = "Extended Information",
EntityId = entityView.EntityId,
EntityVersion = entityView.EntityVersion,
ItemId = entityView.ItemId
};
// Add the entity view as a child of the current entity view
entityView.ChildViews.Add(propertiesEntityView);
}
// Add the component properties to the entity view
propertiesEntityView.Properties = new List<ViewProperty>
{
CreateViewProperty(nameof(extensionComponent.CountryOfOrigin), extensionComponent.CountryOfOrigin),
CreateViewProperty(nameof(extensionComponent.EnergyRating), extensionComponent.EnergyRating)
};
return entityView;
}
public override EntityView Run(EntityView entityView, CommercePipelineExecutionContext context)
{
// Ensure parameters are provided
Condition.Requires(entityView, nameof(entityView)).IsNotNull();
Condition.Requires(context, nameof(context)).IsNotNull();
var viewsPolicy = context.GetPolicy<KnownCatalogViewsPolicy>();
var request = context.CommerceContext.GetObject<EntityViewArgument>();
// Determine the context of rendering the entity view, e.g. create/edit or view (page or via commerce context)
var isViewAction = (request.ViewName.EqualsOrdinalIgnoreCase(viewsPolicy.Variant) ||
request.ViewName.EqualsOrdinalIgnoreCase(viewsPolicy.ConnectSellableItem)) &&
string.IsNullOrWhiteSpace(request.ForAction);
var isEditAction = request.ViewName.EqualsOrdinalIgnoreCase("ExtensionProperties") && request.ForAction.EqualsOrdinalIgnoreCase("EditExtensionProperties");
// Validate the context of the request, i.e. entity, view name, and action name
if (!(request?.Entity is SellableItem) ||
(!isViewAction &&
!isEditAction))
{
return entityView;
}
var sellableItem = request.Entity as SellableItem;
// Get the component from the sellable items variant (ItemId) and do not inherit from the sellable item if not present on the variant
var extensionComponent = sellableItem.GetComponent<VariationExtensionComponent>(entityView.ItemId, false);
var propertiesEntityView = entityView;
if (isViewAction)
{
// Create an entity view to hold the component properties
propertiesEntityView = new EntityView
{
DisplayName = "Extended Information",
Name = "Extended Information",
EntityId = entityView.EntityId,
EntityVersion = entityView.EntityVersion,
ItemId = entityView.ItemId
};
// Add the entity view as a child of the current entity view
entityView.ChildViews.Add(propertiesEntityView);
}
// Add the component properties to the entity view
propertiesEntityView.Properties = new List<ViewProperty>
{
CreateViewProperty(nameof(extensionComponent.Material), extensionComponent.Material),
CreateViewProperty(nameof(extensionComponent.ReleaseDate), extensionComponent.ReleaseDate, "FullDateTime")
};
return entityView;
}
The default values from the components are also removed to provide a true representation of the catalog item property values that are persisted to the database.
public class SellableItemExtensionComponent : Component
{
public string CountryOfOrigin { get; set; }
public int EnergyRating { get; set; }
}
public class VariationExtensionComponent : Component
{
public string Material { get; set; }
public bool isClearance { get; set; }
}
To persist the changes made to the Edit Extension Properties entity view, do action blocks are responsibile for processing the entity view submission action, which in this case is translating the entity view properties back to the component on the commerce entity and then persisting it.
public class DoActionEditSellableItemExtensionViewBlock : AsyncPipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
{
protected CommerceCommander Commander { get; set; }
public DoActionEditSellableItemExtensionViewBlock(CommerceCommander commander)
{
this.Commander = commander;
}
public override async Task<EntityView> RunAsync(EntityView entityView, CommercePipelineExecutionContext context)
{
// Ensure parameters are provided
Condition.Requires(entityView, nameof(entityView)).IsNotNull();
var viewsPolicy = context.GetPolicy<KnownCatalogViewsPolicy>();
// Validate the context of the request, i.e. entity, view name, and action name
var entity = context.CommerceContext.GetObject<CommerceEntity>(p => p.Id.EqualsOrdinalIgnoreCase(entityView.EntityId));
if (!(entity is SellableItem) ||
!entityView.Name.EqualsOrdinalIgnoreCase("ExtensionProperties") ||
!entityView.Action.EqualsOrdinalIgnoreCase("EditExtensionProperties") ||
!string.IsNullOrWhiteSpace(entityView.ItemId))
{
return entityView;
}
var sellableItem = entity as SellableItem;
var extensionComponent = sellableItem.GetComponent<SellableItemExtensionComponent>();
// Assign component property values from the entity view property values
extensionComponent.CountryOfOrigin = entityView.GetPropertyValueByName(nameof(extensionComponent.CountryOfOrigin));
extensionComponent.EnergyRating = int.Parse(entityView.GetPropertyValueByName(nameof(extensionComponent.EnergyRating)));
// Persist the changes to the sellable item
await Commander.PersistEntity(context.CommerceContext, sellableItem).ConfigureAwait(false);
return entityView;
}
}
public class DoActionEditVariationExtensionViewBlock : AsyncPipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
{
protected CommerceCommander Commander { get; set; }
public DoActionEditVariationExtensionViewBlock(CommerceCommander commander)
{
this.Commander = commander;
}
public override async Task<EntityView> RunAsync(EntityView entityView, CommercePipelineExecutionContext context)
{
// Ensure parameters are provided
Condition.Requires(entityView, nameof(entityView)).IsNotNull();
var viewsPolicy = context.GetPolicy<KnownCatalogViewsPolicy>();
// Validate the context of the request, i.e. entity, view name, and action name
var entity = context.CommerceContext.GetObject<CommerceEntity>(p => p.Id.EqualsOrdinalIgnoreCase(entityView.EntityId));
if (!(entity is SellableItem) ||
!entityView.Name.EqualsOrdinalIgnoreCase("ExtensionProperties") ||
!entityView.Action.EqualsOrdinalIgnoreCase("EditExtensionProperties") ||
string.IsNullOrWhiteSpace(entityView.ItemId))
{
return entityView;
}
var sellableItem = entity as SellableItem;
var extensionComponent = sellableItem.GetComponent<VariationExtensionComponent>(entityView.ItemId, false);
// Assign component property values from the entity view property values
extensionComponent.Material = entityView.GetPropertyValueByName(nameof(extensionComponent.Material));
extensionComponent.IsClearance = bool.Parse(entityView.GetPropertyValueByName(nameof(extensionComponent.IsClearance)));
// Persist the changes to the sellable item
await Commander.PersistEntity(context.CommerceContext, sellableItem).ConfigureAwait(false);
return entityView;
}
}
Now we should see our persisted values render in the Extended Infomation entity views of the Merchandising pages and against their respective catalog items in the Sitecore Content Editor.
If the property values are not reflected in Sitecore, run the Refresh Commerce Cache command from the Commerce ribbon menu.
Extended Information populated on sellable item Sitecore item.Extended Information populated on variant Sitecore item.
Variants will have sellable item properties populated, while sellable items will not have variant properties populated as variants represent a specialised form of the sellable item, while the sellable item should not know the specifics of any one variant.
Summary
Implementation Highlights
The following bullet points can be utilised as a checklist to ensure that all bases have been covered :
Create components to house custom properties.
Create get view blocks to retrieve components properties for BizFx pages (view), BizFx modals (create and edit actions) and CommerceConnect (generate catalog item template).
Create populate view actions blocks to allow business user to trigger actions, e.g. create, edit, delete.
Create do action blocks to persist create, edit, delete actions to commerce entities.
Considerations for Extending Catalog Items
Logically group properties into multiple components over using a catch all component. This could mean grouping related properties, grouping only properties that will be mapped to the Sitecore catalog items, or grouping properties into a component that will be utilised to extend cart lines.
View property names should match their component property names in order for the values to be mapped from the commerce entity to the Sitecore catalog item properties.
Property names must be unique for the catalog item commerce entity, otherwise this can cause a conflict in mapping values; properties are resolved by mapping the first instance of a property with the given name on the commerce entity.
Sellable items and variants Sitecore items inherit the same catalog generated templates.
Sellable items will host the variant properties on their Sitecore items, however these values will not be populated.
Variants will host the sellable item properties on their Sitecore items and these properties will be populated with the sellable item property values.