Gratis demo

Automatic synchronization of products in SAP Commerce

Wilco Menge

, Automatic synchronization of products in SAP Commerce, AcorelIn SAP Commerce, products information is organized into Catalog Versions. These Catalog Versions allow you to have more than one record of a product – Typically, you would have a catalog with a ‘Staged’ version and an ‘Online’ version:

  • The ‘Online’ version contains the products as they are shown in the storefront.
  • The ‘Staged’ version acts as a place where you can modify your product information without any impact on the information shown in the storefront.

By having different versions of the same product you can safely edit the ‘Staged’ version of you product, while your ‘Online’ version stays intact. Only when you are happy with the ‘Staged’ version, you copy the ‘Staged’ version to the ‘Online’ version.

Product synchronization is the act of copying product information from one catalog to another. A sound strategy for product synchronization is an important part of a successfull PIM approach.

In this blog I will discuss the process of setting up automatic product synchronization.

How to setup automatic product synchronization

A common requirement is to have a mechanism to automatically synchronize approved products from the ‘Staged’ to the ‘Online’ Catalog Version. This way, PIM users only have to approve the product in the Staged catalog, after which the products are available in the ‘Online’ Catalog Version. You would think that this is a scenario that works out of the box, and the OOTB impexes seems to suggest that this is possible. However, when implementing this scenario at a client I ran into some issues.

Initial setup

I started out with the following requirements:

  • Synchronization should happen automatically in the background at scheduled intervals
  • Only approved products are allowed to be synchronized. (For the purpose of the this post, we define a product as Approved when product.approvalstatus = ‘Approved’, but you can also use a custom field to define your filter)
  • As little custom development as needed. After all, this should be a fairly common scenario.

In order to synchronize (either manually or automatically) you need to define a CatalogVersionSyncJob, so let’s do that:


INSERT_UPDATE CatalogVersionSyncJob; code[unique = true] ; $sourceVersion ; $targetVersion ; createNewItems; removeMissingItems; rootTypes(code) ; syncLanguages(isocode); nodeGroup[default = 'integration']
; mySyncJob ; $productCatalog:Online ; $productCatalog:Staged ; TRUE ; FALSE ; Product,Category,Media,MediaContainer ; nl,en ;


In order to run this job periodically you would need to add a Trigger. Lets set it up so that it runs every night at 12am:

INSERT_UPDATE Trigger; cronJob(code)[unique = true]; cronExpression;
; mySyncJob ; 0 0 0 * * ?


In order to filter on approved products you would apply a JobSearchRestriction to the job:

INSERT_UPDATE JobSearchRestriction; code[unique = true] ; job(code) ; query ; type(code)
; mySyncJob-restriction ; mySyncJob ; {approvalStatus} IN ({{ SELECT {pk} FROM {ArticleApprovalStatus} WHERE {code}= 'approved'}}) ; Product


And you’re finished! Or not. With this setup we had a couple of problems:

  1. The delta (“What products have been changed since the last synchronization run?”) is only calculated when an instance of CatalogVersionSyncJob is created. This means that the job works on the first run, but on the subsequent runs  any new modification will not be synced!
  2. The JobSearchRestriction will be ignored. I’m not sure if this due to the type of job, some people suggest that the JobSearchRestriction is decrepated, but I haven’t found a clear message from SAP.

Modified setup

Step 1: Correctly determine delta’s

In order to calculate a new delta on every run, we will need a new instance of the JobSearchRestriction for every run. We chose to use a small Groovy script to create and start the new job:


syncFacade = spring.getBean('synchronizationFacade')

request = new SyncRequestData()
request.catalogId = 'myCatalog'
request.sourceVersionId = 'Staged'
request.targetVersionId = 'Online'



If we save this script with name ‘synchronization-script’, we can create and schedule a Script job that calls the script on every run:

INSERT_UPDATE ScriptingJob; code[unique = true]; scriptURI;
; synchronization-script-job ; model://synchronization-script

INSERT_UPDATE CronJob; code[unique = true]; job(code) ; singleExecutable; sessionLanguage(isocode); nodeGroup[default = integration];sessionUser(uid)[default=admin]
; synchronization-script-cronjob ; synchronization-script-job ; false ; en

INSERT_UPDATE Trigger; cronJob(code)[unique = true]; cronExpression;
; synchronization-script-cronjob ; 0 0 0 * * ?


That takes care of the scheduling: because a new instance of the JobSearchRestriction is made for every run, delta’s will be calculated correctly.

Step 2: Filter only approved products

But how about the filtering? If we cannot use a JobSearchRestriction, why not use a normal SearchRestriction, that can be assigned to a user or usergroup?

Lets create and assign a Search Restriction:

INSERT_UPDATE UserGroup; uid[unique = true]
; syncusergroup

INSERT_UPDATE Employee; uid[unique = true]; sessionLanguage(isocode); password[default = 1234]; name ; groups(uid); loginDisabled
; syncuser ; en ; ; Sync User ; syncusergroup

INSERT_UPDATE SearchRestriction; code[unique = true] ; name[lang = en] ; query ; principal(uid); restrictedType(code); active; generate
; syncuser_restriction ; Restrict Products for syncuser ; {approvalStatus} IN ({{ SELECT {pk} FROM {ArticleApprovalStatus} WHERE {code}= 'approved'}}) ; syncusergroup ; Product ; true ; true


And we need to modify the CatalogVersionSyncJob so that it is run by the syncuser:

INSERT_UPDATE CatalogVersionSyncJob; code[unique = true] ; sessionUser(UID);
mySyncJob; syncuser ;


And that’s it. We now have working synchronization job with minimal custom development! If you have any questions, feel free to contact us.

Wilco Menge