Merge: Customizing the Master Article Finder

The master article finder is available with version 7.1.02.00 and replaces the product finder (extension point com.heiler.ppm.merge.core.ProductFinder). The product finder extension point is still available. Internally, the product finder is wrapped in this case in a master article finder (class MasterArticleFinderAdapter).

Introduction

The master article finder finds items in the master catalog for a given list of supplier catalog items. A typical example is that the GTIN number is used to find a corresponding item in the master catalog.

When an item of a supplier catalog is merged into the master catalog, it is first checked if an item in the master catalog exists that is already linked to the item of the supplier catalog. (The supplier relation contains that information.) If such an item is found, it is used for the merge. If such an item is not found, it is checked weather a master item finder is contributed. If this is not a case, a new item is created in the master catalog.

If a master article finder is contributed, it is called and in case the master article finder returns a corresponding master item, this item is used for the merge. If the master article finder does not find an item in the master catalog, a new item is created.

Additionally, the master article finder provides the functionality to skip supplier items that shouldn't be merged. It is possible to add messages in the merge protocol to document a skipped item. The master article finder can also be configured so that the finder is always used even if the supplier item is already linked to a master item.

Batch mode vs. single mode

The master article finder can operate in two modes:

Batch mode The finder is called once for all items of the supplier catalog / assortment and the actual merge of the items is performed afterwards.

Single mode The finder is called for every item directly before it gets merged.

Advantages of batch mode

  • No read / write operations on the database for master items at the same time, avoiding resource deadlocks in the database (see also HPM-17648).

  • Batch processing allows more efficient processing, like less DB queries.

Disadvantages of batch mode

  • Larger time gap between the search of the master item and the actual merge of the supplier item. Modifications of the items in between may lead to incorrect merge results.

Extension point com.heiler.ppm.merge.core.MasterArticleFinder

A master article finder can be contributed using the extension point com.heiler.ppm.merge.core.MasterArticleFinder.

The extension point contains the properties name, description, class, batchMode and useAlwaysFinder. The class has to be a class implementing the interface com.heiler.ppm.merge.core.extpoints.MasterArticleFinder and contains the actual implementation of the master article finder.

For details, please check the JavaDoc of the extension point and the interface.

Example

The example contains a finder that uses the GTIN to find corresponding the corresponding master items. (This example does not contain any optimizations for the batch mode.)

GTINMasterArticleFinder
public class GTINMasterArticleFinder implements MasterArticleFinder
{
private boolean batchMode;
private boolean useAlwaysFinder;
@Override
public void setBatchMode( boolean batchMode )
{
this.batchMode = batchMode;
}
@Override
public boolean isBatchMode()
{
return this.batchMode;
}
@Override
public void setUseAlwaysFinder( boolean useAlwaysFinder )
{
this.useAlwaysFinder = useAlwaysFinder;
}
@Override
public boolean isUseAlwaysFinder()
{
return this.useAlwaysFinder;
}
@Override
public void find( MasterArticleFinderContext context, List< SupplierMasterArticlePair > supplierMasterItemPairList )
throws CoreException
{
for ( SupplierMasterArticlePair pair : supplierMasterItemPairList )
{
if ( this.useAlwaysFinder || !pair.isMappedToMaster() )
{
Short entityId = context.getEntity()
.getEntityId();
CatalogProxy supplierCatalog = ( CatalogProxy ) context.getSupplierCatalog();
long suppliertID = pair.getSupplierArticleId();
ArticleProxy articleProxy = new ArticleProxy( entityId, supplierCatalog, suppliertID );
LoadHint loadHint = new LoadHintBuilder( context.getEntity()
.getEntityType() ).build();
EntityDetailModel detailModel = articleProxy.getDetailModel( loadHint );
Long id = find( context, pair, detailModel );
if ( id != null )
{
pair.setNewMasterArticleId( id );
}
}
}
}
public Long find( MasterArticleFinderContext context, SupplierMasterArticlePair pair, EntityDetailModel detailModel )
{
try
{
detailModel.acquireRead();
try
{
SearchService searchService = EntitySearchComponent.getSearchService();
RepositoryService repositoryService = RepositoryComponent.getRepositoryService();
Field field = repositoryService.getFieldByIdentifier( "Article.EAN" );
FieldPath fieldPath = RepositoryUtils.getFieldPath( field );
String gtin = ( String ) detailModel.getFieldValue( fieldPath );
Expression expression = new EqualExpression( new FieldPathExpression( fieldPath ), new ValueExpression( gtin ) );
String dataSource = DataSource.MASTER.getIdentifier();
SearchParameters searchParameters = new SearchParameters( dataSource, "ArticleType", expression );
EntityProxy[] entityProxies = searchService.searchAsEntityProxies( new NullProgressMonitor(), searchParameters,
null );
if ( entityProxies.length > 1 )
{
String msg = "{0_number} master items found, expected one";
msg = MessagesUtils.replace( msg, false, entityProxies.length );
StatusExt status = new StatusExt( Categories.CARDINALITY, IStatus.ERROR, MergeCorePlugin.PLUGIN_ID, 0, msg,
null );
context.getProblemLog()
.log( ProblemLogFactory.createProblem( status ) );
pair.setSkipMerge();
}
else if ( entityProxies.length == 1 )
{
return entityProxies[ 0 ].getInternalId();
}
return null;
}
finally
{
detailModel.releaseRead();
}
}
catch ( InterruptedException | CoreException e )
{
throw new RuntimeException( e );
}
}
}