Commerce Connect – How to synchronize categories (classifications) from ECS to Sitecore

Image

Here is a quick how to guide for implementing a one-way synchronization of categories from an ECS (External Commerce System) into Sitecore, and a follow up to Dan Schoenberg's post on syncing divisions.Note: This guide was written while using Commerce Connect 7.2/7.5Step 1: Update configuration settings for 1-way synchronizationTo support a one-way synchronization of categories where data from your ECS is populated in Sitecore upon a Sync operation, make the following configuration changes. Note, I wanted to avoid making changes to the default commerce connect configuration files, so I made my changes in a separate configuration file, and that's why I used the delete patch approach.[sourcecode language="xml"]<commerce.synchronizeProducts.synchronizeClassifications> <processor type="Sitecore.Commerce.Pipelines.Products.SynchronizeClassifications.ReadSitecoreClassifications, Sitecore.Commerce"> <patch:delete /> </processor> <processor type="Sitecore.Commerce.Pipelines.Products.SynchronizeClassifications.ResolveClassificationsChanges, Sitecore.Commerce"> <patch:delete /> </processor> <processor type="Sitecore.Commerce.Pipelines.Products.SynchronizeClassifications.SaveClassificationsToExternalCommerceSystem, Sitecore.Commerce"> <patch:delete /> </processor></commerce.synchronizeProducts.synchronizeClassifications>[/sourcecode]Step 2: Create custom classification synchronization processorsWhen using Commerce Connect, a category structure will typically be contained within a ClassificationGroup, and your categories will be setup as a Classification hierarchy. To demonstrate both of these concepts, I put together basic code to setup custom processors for both the ClassificationGroup and the Classifications. There are a few interesting things to make note of when reviewing the code.Using the Request object from the given ServicePipelineArgs will allow you to retrieve a current collection of data keyed by property name in the pipeline and act upon it. If you get an empty collection, you should create the collection and assign that to the Request object which will all the other processors in the in the pipeline to act upon the data accordingly.The ClassificationGroup processor keys off of the "ClassificationGroups" property name and the Classification processor keys off of the "Classification" property name.When setting up your categories (Classifications) you'll want to assign them to a ClassificationGroup. To make this work, you will need to assign the collection of categories (Classifications) to an instance of a ClassificationGroup as demonstrated in lines 58-63.In order to create your category hierarchy, simply use the ExternalParentId property on the Classification object; as in the mock data that was setup in lines 76-97. This example only shows one level deep but the nesting has no foreseeable limitations.Ok, here's the code:[sourcecode language="csharp"]public abstract class AbstractSyncProcessor : PipelineProcessor<ServicePipelineArgs>{ protected List<T> ExtractDataCollection<T>(ServicePipelineArgs args, string propertyIdentifier) { List<T> collection = args.Request.Properties[propertyIdentifier] as List<T>; if (collection == null) { collection = new List<T>(); args.Request.Properties[propertyIdentifier] = collection; } return collection; }}public class ClassificationGroupSyncProcessor : AbstractSyncProcessor{ public static string ClassificationGroupPropertyName = "ClassificationGroups"; public ClassificationGroupSyncProcessor() { } public override void Process(ServicePipelineArgs args) { try { List<ClassificationGroup> groups = ExtractDataCollection<ClassificationGroup>(args, ClassificationGroupPropertyName); ProcessMockData(groups); args.Result.Success &= true; } catch (Exception e) { args.Result.Success = false; throw e; } } public void ProcessMockData(List<ClassificationGroup> groups) { groups.Add(new ClassificationGroup { Name = "Launch Sitecore Taxonomy", ExternalId = "LaunchSitecoreTaxonomy-CG", Description = "A default category structure for organizing product information." }); }}public class ClassificationSyncProcessor : AbstractSyncProcessor{ public static string ClassificationPropertyName = "Classification"; public ClassificationSyncProcessor() { } public override void Process(ServicePipelineArgs args) { try { List<Classification> classifications = ExtractDataCollection<Classification>(args, ClassificationPropertyName); ProcessMockData(classifications); List<ClassificationGroup> groups = ExtractDataCollection<ClassificationGroup>(args, ClassificationGroupSyncProcessor.ClassificationGroupPropertyName); foreach(ClassificationGroup group in groups) { if (group.ExternalId == "LaunchSitecoreTaxonomy-CG") group.Classifications = new ReadOnlyCollection<Classification>(classifications); } args.Result.Success &= true; } catch (Exception e) { args.Result.Success = false; throw e; } } public void ProcessMockData(List<Classification> classifications) { classifications.Add(new Classification { Name = "Sitecore Modules", ExternalId = "SitecoreModules-CAT", ExternalParentId = null }); classifications.Add(new Classification { Name = "Developer Accelerators", ExternalId = "DeveloperAccelerators-CAT", ExternalParentId = "SitecoreModules-CAT" }); classifications.Add(new Classification { Name = "Administration", ExternalId = "Administration-CAT", ExternalParentId = "SitecoreModules-CAT" }); classifications.Add(new Classification { Name = "Client Features", ExternalId = "ClientFeatures-CAT", ExternalParentId = "SitecoreModules-CAT" }); classifications.Add(new Classification { Name = "Visitor Interaction", ExternalId = "VisitorInteraction-CAT", ExternalParentId = "SitecoreModules-CAT" }); classifications.Add(new Classification { Name = "Translation and Localization", ExternalId = "TranslationAndLocalization-CAT", ExternalParentId = "SitecoreModules-CAT" }); classifications.Add(new Classification { Name = "Media", ExternalId = "Media-CAT", ExternalParentId = "SitecoreModules-CAT" }); classifications.Add(new Classification { Name = "Website Optimization", ExternalId = "WebsiteOptimization-CAT", ExternalParentId = "SitecoreModules-CAT" }); classifications.Add(new Classification { Name = "Portlets", ExternalId = "Portlets-CAT", ExternalParentId = "SitecoreModules-CAT" }); classifications.Add(new Classification { Name = "Reporting", ExternalId = "Reporting-CAT", ExternalParentId = "SitecoreModules-CAT" }); classifications.Add(new Classification { Name = "Search", ExternalId = "Search-CAT", ExternalParentId = "SitecoreModules-CAT" }); classifications.Add(new Classification { Name = "Setup and Deployment", ExternalId = "SetupAndDeployment-CAT", ExternalParentId = "SitecoreModules-CAT" }); classifications.Add(new Classification { Name = "Social and Community", ExternalId = "SocialAndCommunity-CAT", ExternalParentId = "SitecoreModules-CAT" }); classifications.Add(new Classification { Name = "Webmaster Tools", ExternalId = "WebmasterTools-CAT", ExternalParentId = "SitecoreModules-CAT" }); classifications.Add(new Classification { Name = "Sitecore Classes and Training", ExternalId = "SitecoreClassesAndTraining-CAT", ExternalParentId = null }); classifications.Add(new Classification { Name = "Content Editor", ExternalId = "ContentEditor-CAT", ExternalParentId = "SitecoreClassesAndTraining-CAT" }); classifications.Add(new Classification { Name = "Page Editor", ExternalId = "PageEditor-CAT", ExternalParentId = "SitecoreClassesAndTraining-CAT" }); classifications.Add(new Classification { Name = "Personalization", ExternalId = "Personalization-CAT", ExternalParentId = "SitecoreClassesAndTraining-CAT" }); classifications.Add(new Classification { Name = "Engagement Plans", ExternalId = "EngagementPlans-CAT", ExternalParentId = "SitecoreClassesAndTraining-CAT" }); classifications.Add(new Classification { Name = "Visitor Profiles", ExternalId = "VisitorProfiles-CAT", ExternalParentId = "SitecoreClassesAndTraining-CAT" }); classifications.Add(new Classification { Name = "Campaigns", ExternalId = "Campaigns-CAT", ExternalParentId = "SitecoreClassesAndTraining-CAT" }); }}[/sourcecode]Step 3: Patch in processor configurations for custom classification synchronization processors Add the custom synchronization processors into the pipeline via configuration updates below the settings you made in step 1. After your changes your final configuration should like something similar to this. Make sure that in your final configuration that the ClassificationGroup processor runs before the Classification processor or you could face issues with your expected data not being present in the Request object.[sourcecode language="xml"]<commerce.synchronizeProducts.synchronizeClassifications> <processor type="Sitecore.Commerce.Pipelines.Products.SynchronizeClassifications.ReadSitecoreClassifications, Sitecore.Commerce"> <patch:delete /> </processor> <processor type="Sitecore.Commerce.Pipelines.Products.SynchronizeClassifications.ResolveClassificationsChanges, Sitecore.Commerce"> <patch:delete /> </processor> <processor type="Sitecore.Commerce.Pipelines.Products.SynchronizeClassifications.SaveClassificationsToExternalCommerceSystem, Sitecore.Commerce"> <patch:delete /> </processor> <!-- Setup a custom processor to handle synchronization of Mock Data for Classifications --> <processor type="YourNamespaceHere.ClassificationSyncProcessor, YourLibraryNameHere" patch:after="processor[@type='Sitecore.Commerce.Pipelines.Products.SynchronizeClassifications.ReadExternalCommerceSystemClassifications, Sitecore.Commerce']" /> <processor type="YourNamespaceHere.ClassificationGroupSyncProcessor, YourLibraryNameHere" patch:after="processor[@type='Sitecore.Commerce.Pipelines.Products.SynchronizeClassifications.ReadExternalCommerceSystemClassifications, Sitecore.Commerce']" /></commerce.synchronizeProducts.synchronizeClassifications>[/sourcecode]Step 4: Synchronize the Product RepositoryBuild your solution and "Synchronize artifacts" on your product repository: .

CommerceConnect-Sync

Once complete, your categories will be populated into your Commerce Connect product repository in Sitecore with results similar to this:

CommerceConnect-MockCategories