user4046031
user4046031

Reputation:

Multi-firing of BXGY promotion - consumption of qualifying order entries

I have implemented BXGY custom promotion. To simplify my case let's say after you have X products in your cart you'll get Y free products.

If I have 2*X qualifying products in cart expected behavior is to get 2*Y free products.

My problem is multi-firing of that promotion. If I set maximum count of multifiring to 4, than action (get free products) is activated 4 times although I have 2*X products in cart.

So my conclusion is that I need to consume qualifying products (entries with that product) like free products that user gets are consumed so they cannot participate in qualification for another promotion.

Conclusion of my research is that I need to consume entries in (YFreeGift)RAOAction, but then I need to retrieve all conditional products and I don't feel that it's a right way (condition in action).

Do anybody knows how to consume qualifying entries?

Condition translator for BXGY (I have mandatory qualifying products too but that isn't important for this problem)

public class RuleHeinekenBXGYFQualifyingProductsConditionTranslator implements RuleConditionTranslator
{
    @Override
    public RuleIrCondition translate(RuleCompilerContext context, RuleConditionData ruleCondition, RuleConditionDefinitionData conditionDefinition) throws RuleCompilerException
    {
        List<String> mandatoryProducts = (List) getConditionParameterValue(ruleCondition, "mandatoryQualifyingProducts");
        List<String> alternativeProducts = (List) getConditionParameterValue(ruleCondition, "alternativeQualifyingProducts");
        Integer qualifyingCount = (Integer) getConditionParameterValue(ruleCondition, "qualifyingCount");

        if(isEmpty(mandatoryProducts) && isEmpty(alternativeProducts) || qualifyingCount == null || qualifyingCount <= 0)
            throw new PromotionConditionParametersValidationException();

        String cartRAO = context.generateVariable(CartRAO.class);
        List<RuleIrCondition> mandatoryProductsConditions = getMandatoryProductsConditions(context, mandatoryProducts, cartRAO);
        List<RuleIrCondition> qualifyingCountCondition = getQualifyingCountCondition(context, qualifyingCount, cartRAO);

        RuleIrGroupCondition qualifyingConditions = new RuleIrGroupCondition();
        qualifyingConditions.setOperator(RuleIrGroupOperator.AND);
        qualifyingConditions.setChildren(listUnion(mandatoryProductsConditions, qualifyingCountCondition));
        return qualifyingConditions;
    }

    private List<RuleIrCondition> getQualifyingCountCondition(RuleCompilerContext context, Integer qualifyingCount, String cartRAO)
    {
        String qualifyingCountRAO = context.generateVariable(QualifyingCountRAO.class);
        String promotionCode = context.getRule().getCode();

        return getListOfRuleConditions(
            aRuleCondition()
                .withModelRAO(qualifyingCountRAO)
                .withAttribute("promotionCode")
                .withOperator(EQUAL)
                .withValue(promotionCode)
                .buildAttributeCondition(),

            aRuleCondition()
                .withModelRAO(qualifyingCountRAO)
                .withAttribute("qualifyingCount")
                .withOperator(GREATER_THAN_OR_EQUAL)
                .withValue(qualifyingCount)
                .buildAttributeCondition(),

            aRuleCondition()
                .withModelRAO(cartRAO)
                .withAttribute("qualifyingCounts")
                .withOperator(CONTAINS)
                .withTargetVariable(qualifyingCountRAO)
                .buildAttributeRelationCondition());
    }

    private List<RuleIrCondition> getMandatoryProductsConditions(RuleCompilerContext context, List<String> mandatoryProducts, String cartRAO)
    {
        if(isEmpty(mandatoryProducts))
            return emptyList();

        return getMapOfQualifyingProductsWithQuantities(mandatoryProducts)
                 .entrySet().stream()
                 .map(entry -> getMandatoryProductCondition(context, cartRAO, entry.getKey(), entry.getValue()))
                 .collect(toList());
    }

    private RuleIrExistsCondition getMandatoryProductCondition(RuleCompilerContext context, String cartRAO, String product, int qualifyingCount)
    {
        RuleIrLocalVariablesContainer variablesContainer = context.createLocalContainer();
        String containsProductRAO = context.generateLocalVariable(variablesContainer, ProductRAO.class);
        String containsOrderEntryRAO = context.generateLocalVariable(variablesContainer, OrderEntryRAO.class);

        List<RuleIrCondition> listOfConditions = getListOfRuleConditions(
            aRuleCondition()
                .withModelRAO(containsProductRAO)
                .withAttribute("code")
                .withOperator(EQUAL)
                .withValue(product)
                .buildAttributeCondition(),

            aRuleCondition()
                .withModelRAO(containsOrderEntryRAO)
                .withAttribute("product")
                .withOperator(EQUAL)
                .withTargetVariable(containsProductRAO)
                .buildAttributeRelationCondition(),

            aRuleCondition()
                .withModelRAO(containsOrderEntryRAO)
                .withAttribute("quantity")
                .withOperator(GREATER_THAN_OR_EQUAL)
                .withValue(qualifyingCount)
                .buildAttributeCondition(),

            aRuleCondition()
                .withModelRAO(cartRAO)
                .withAttribute("entries")
                .withOperator(CONTAINS)
                .withTargetVariable(containsOrderEntryRAO)
                .buildAttributeRelationCondition());

        RuleIrExistsCondition mandatoryProductsExistCondition = new RuleIrExistsCondition();
        mandatoryProductsExistCondition.setVariablesContainer(variablesContainer);
        mandatoryProductsExistCondition.setChildren(listOfConditions);
        return mandatoryProductsExistCondition;
    }
}

Upvotes: 0

Views: 1033

Answers (2)

Sebastian
Sebastian

Reputation: 1783

what version are you on? There is a bug in the free gift action prior to 6.4 or 6.3 AFAIK. The free gift action is comprised of 2 actions internally (add product to cart and give 100% discount on added product). Due to the bug you might have to increase the maximum rule executions to double the amount you intend originally (as each action "add to cart" and "discount product" will count as one execution).

To answer your highlighted question about order entry consumption: The promotionengine doesn't support order entry consumption OOTB right now. This is a feature that was part of the legacy promotions but has been dropped in favor of stackability.

But more generally, the approach you seem to take is not how hybris intends the free gift action to be used. If you want a BOGOF (or BXGYF) you would normally define a promotion that checks if both your qualifying product X AND the to-be-discounted products Y are in the cart in sufficient quantity. The action would then simply discount the Y products. You can also add a potential promotion checking only for the product X that will trigger a message like Add n products Y to your cart to get them for free in order to alert the customer that s/he is eligible for a promotion.

If you look at the promotionenginesamplesaddon extension/addon you will find those two promotions that should do exactly what I mean above:

potential_product_buy_x_get_y_free
product_buy_x_get_y_free

Hope this helps,

Sebastian

Upvotes: 0

arsen_adzhiametov
arsen_adzhiametov

Reputation: 696

Rule Engine provides a mechanism for you to configure a limit on the number of times a rule can trigger Action Items.

The maximum rule executions allow you to control the maximum number of times the action for a rule can be executed, as long as the conditions are met. For all out-of-the-box promotions actions, the value of this attribute should be set to one.

Look at RuleConfigurationRRD attributes.

Taken from help.hybris.com.

Upvotes: 0

Related Questions