How We Add New Rule Condition Parameter

Mehmet Karahan
5 min readApr 26, 2021

As you know, the hybris promotion mechanism is very strongest, the out of the box, it consists of too many promotion templates to create new promotion faster and easier as shown below image:

and so on…

Promotion Engine runs on top of Rule Engine. This architecture provides an overview of the technical components used to build the Promotion Engine.

The first layer represents the SAP Commerce platform, which serves as the building block of the modules.

The second layer consists of Rule Engine, which is split into two categories, Commerce Rule Engine and Drools Rule Engine. Each particular category contains its own Data Model, Data (ImpEx), API, and Backoffice modules.

Commerce Rule Engine runs on top of Drools Rule Engine. This layer introduces Commerce-related data and functionality to Backoffice to create the Rule Builder, which is the user interface to create custom rules.

And also, I want to add some extra explanations for drools. Drools is a Business Rules Management System (BRMS) solution. It provides a core Business Rules Engine (BRE), a web authoring and rules management application (Drools Workbench). Drools is open source software, released under the Apache License 2.0. It is written in 100% pure Java™, runs on any JVM, and is available in the Maven Central repository too. To get more information, you can visit the website that is https://www.drools.org/

What if we want to achieve the created drools rule for any promotion, firstly navigate to Marketing -> Promotion Rules in backoffice panel, and click any promotion rule which is created by us as shown below image:

To get the main point as mentioned the article’s title, I’ll try to explain step by step. Our training scenario contains that adding a “brands” condition for the rule.

Firstly, we need to prepare Impex to add condition parameter to any specified promotion rule:

$lang = en

INSERT_UPDATE RuleConditionDefinition; id[unique = true] ; name[lang = $lang] ; priority; breadcrumb[lang = $lang] ; allowsChildren; translatorId ; translatorParameters; categories(id)
; y_category_total_quantity ; Category total Quantity ; 700 ; Category {categories} total {operator} {quantity} ; false ; ruleCategoryTotalQuantityConditionTranslator ; ; general

INSERT_UPDATE RuleConditionDefinitionParameter; definition(id)[unique = true]; id[unique = true]; priority; name[lang = $lang] ; description[lang = $lang] ; type ; value ; required[default = true]
; y_category_total_quantity ; brands ; 1150 ; Brands ; List of brands to be evaluated ; List(ItemType(Category)) ; ; false;

“y_category_total_quantity” is a rule condition name that has been created for our hybris environment.

Secondly, we’ll add the brands attribute our rule condition translator java class as shown below images and source codes:

Added the source codes lines :

RuleParameterData brandsParameter = conditionParameters.get("brands");if (verifyAllPresent(operatorParameter, quantityParameter, categoriesParameter,brandsParameter)) {//add brands parameter variable for verifyAllPresent
List<String> brandCodes = brandsParameter.getValue();//get brands
......//add this method to check product that is contains the brand code or not
RuleIrAttributeCondition createBrandCodeCondition(RuleCompilerContext context, List<String> brands) {
return RuleIrAttributeConditionBuilder.newAttributeConditionFor(context.generateVariable(BrandsRAO.class))
.withAttribute("code")
.withOperator(RuleIrAttributeOperator.IN)
.withValue(brands)
.build();
}
//..and we need to add product brands relation condition
RuleIrAttributeRelCondition createBrandProductRelation(RuleCompilerContext context) {
return RuleIrAttributeRelConditionBuilder.newAttributeRelationConditionFor(context.generateVariable(ProductRAO.class))
.withAttribute("brands")
.withOperator(RuleIrAttributeOperator.CONTAINS) .withTargetVariable(context.generateVariable(BrandsRAO.class))
.build();
}

Thirdly, we need to create BrandsRao class, converter, and populator for that class which is the base of Rule-Aware Objects.

Add this below line where <extensionname>-beans.xml like that:

<bean class="de.hybris.platform.ruleengineservices.rao.BrandsRAO">
<property name="code" type="String" equals="true"/>
</bean>

Add this below line where <extensionname>-spring.xml like that:

<alias name="defaultBrandsRaoConverter" alias="brandsRaoConverter" />
<bean id="defaultBrandsRaoConverter" parent="abstractPopulatingConverter">
<property name="targetClass" value="de.hybris.platform.ruleengineservices.rao.BrandsRAO" />
<property name="populators">
<list>
<ref bean="brandsRaoPopulator"/>
</list>
</property>
</bean>

<alias name="defaultBrandsRaoPopulator" alias="brandsRaoPopulator" />
<bean id="defaultBrandsRaoPopulator"
class="tr.com.karahan.ruleengineservices.populators.BrandsRaoPopulator">
</bean>

Brands attribute is related to product model so, I’ll create a relationship with productRao and brandsRao like that in <extensionname>-beans.xml:

<bean class="de.hybris.platform.ruleengineservices.rao.ProductRAO"
extends="de.hybris.platform.ruleengineservices.rao.AbstractActionedRAO">
<property name="brands" type="java.util.Set&lt;de.hybris.platform.ruleengineservices.rao.BrandsRAO>" />
</bean>

Add this below line where <extensionname>-spring.xml like that:

<alias name="defaultKarahanProductRaoPopulator" alias="productRaoPopulator" />
<bean id="defaultKarahanProductRaoPopulator" class="tr.com.karahan.ruleengineservices.populators.KarahanProductRaoPopulator" parent="defaultProductRaoPopulator">
<property name="brandsConverter" ref="brandsRaoConverter" /> <property name="karahanCategoryService" ref="karahanCategoryService"/>
</bean>
//----IMPORTANT-----//
//--add brands provider to provide the facts required to evaluate rules and apply actions
<alias name="defaultBrandsRaoProvider" alias="brandsRaoProvider"/>
<bean id="defaultBrandsRaoProvider" class="tr.com.karahan.ruleengineservices.rao.provider.BrandsRaoProvider"/>
//-cart provider extractors run our brandsraoprovider with this line
<bean depends-on="cartRAOProviderExtractors" parent="listMergeDirective">
<property name="add" ref="brandsRaoProvider"/>
</bean>

After adding the above source code line for related XML files, we need to implement java classes:

public class KarahanProductRaoPopulator extends ProductRaoPopulator {

private KarahanCategoryService karahanCategoryService;
private Converter<CategoryModel, BrandsRAO> brandsConverter;

@Override
public void populate(ProductModel source, ProductRAO target) {
super.populate(source, target);
CategoryModel brandModel = karahanCategoryService.getBrand(source);
HashSet<BrandsRAO> brandsCats = new HashSet(Collections.singleton(getBrandsConverter().convert(brandModel)));
target.setBrands(brandsCats);
}

public Converter<CategoryModel, BrandsRAO> getBrandsConverter() {
return brandsConverter;
}

public void setBrandsConverter(Converter<CategoryModel, BrandsRAO> brandsConverter) {
this.brandsConverter = brandsConverter;
}

public KarahanCategoryService getKarahanCategoryService() {
return karahanCategoryService;
}

public void setKarahanCategoryService(KarahanCategoryService karahanCategoryService) {
this.karahanCategoryService = karahanCategoryService;
}
}
public class BrandsRaoPopulator implements Populator<CategoryModel, BrandsRAO> {

@Override
public void populate(CategoryModel source, BrandsRAO target) throws ConversionException {
target.setCode(source.getCode());
}
}

Brands Rao Provider Class:

public class BrandsRaoProvider implements RAOFactsExtractor {

private static final String EXPAND_BRANDS_DEFINITION = "EXPAND_BRANDS";

@Override
public Set expandFact(Object fact) {
Preconditions.checkArgument(fact instanceof CartRAO, "CartRAO type is expected here");
Set<Object> facts = new HashSet<>();
CartRAO cartRAO = (CartRAO)fact;
if (CollectionUtils.isNotEmpty(cartRAO.getEntries())) {
for(OrderEntryRAO orderEntryRAO : cartRAO.getEntries()){
ProductRAO productRAO = orderEntryRAO.getProduct();
if(Objects.nonNull(productRAO)) {
facts.addAll(productRAO.getBrands());
}
}
}
return facts;
}

@Override
public String getTriggeringOption() {
return EXPAND_BRANDS_DEFINITION;
}

@Override
public boolean isMinOption() {
return false;
}

@Override
public boolean isDefault() {
return true;
}
}

So after that these above implementations, we’ll notice that running drools rule as shown below image:

If you have any questions or problems, don’t hesitate to contact me.

Note: I created this training in the 18.11 hybris version.

--

--