Documentation

Adding a New Discount Rule Type

You can add a new discount rule type by implementing two interfaces: CommerceDiscountRuleType and CommerceDiscountRuleTypeJSPContributor.

Discount rule types define conditions for evaluating when discounts are applied to an order. Liferay Commerce provides three discount rule types out-of-the-box: AddedAllCommerceDiscountRuleTypeImpl, AddedAnyCommerceDiscountRuleTypeImpl, and CartTotalCommerceDiscountRuleTypeImpl.

Out-of-the-box discount rule types

Deploy an Example

Start a new Liferay instance by running

docker run -it -m 8g -p 8080:8080 liferay/portal:7.4.3.29-ga29

Sign in to Liferay at http://localhost:8080. Use the email address test@liferay.com and the password test. When prompted, change the password to learn.

Then, follow these steps to deploy an example discount rule type on your Liferay Commerce instance:

  1. Download and unzip the Acme Commerce Discount Rule Type.

    curl https://learn.liferay.com/commerce/latest/en/developer-guide/promotions/liferay-m6a8.zip -O
    
    unzip liferay-m6a8.zip
    
  2. Build and deploy the example.

    ./gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
    

    Note

    This command is the same as copying the deployed jars to /opt/liferay/osgi/modules on the Docker container.

  3. Confirm the deployment in the Liferay Docker container console.

    STARTED com.acme.m6a8.web_1.0.0
    
  4. Verify that the example discount rule type was added. Open your browser to https://localhost:8080. Then click the Applications Menu (Applications Menu) and navigate to CommerceDiscounts. Click Edit within the menu for any discount. Scroll down to the Rules section, click the (+) icon to add a new discount rule. The new discount rule type (“Has a minimum number of products”) is present under the Type dropdown.

Note

In Liferay Commerce 2.1 and earlier, find the discounts by navigating to Control PanelCommerceDiscounts. Click any Edit within the menu for any discount and then click Rules at the top of the screen.

New discount rule type

Congratulations, you’ve successfully built and deployed a new discount rule type that implements CommerceDiscountRuleType.

Next, you’ll dive deeper to learn more.

Walk Through the Example

Now it’s time to review the example you deployed. There are two classes: a discount rule type class and a JSP contributor for a custom UI input. Follow these steps:

Annotate the Discount Rule Type Class for OSGi Registration

@Component(
    property = {
    "commerce.discount.rule.type.key=m6a8",
    "commerce.discount.rule.type.order:Integer=51"
    },
    service = CommerceDiscountRuleType.class
)
public class M6A8CommerceDiscountRuleTypeImpl
    implements CommerceDiscountRuleType {

It is important to provide a distinct key for the discount rule type so that Liferay Commerce can distinguish the new type from others in the discount rule type registry. Declaring a key that is already in use overrides the existing associated type.

The commerce.discount.rule.type.order value indicates how far in the list of available discount rule types this type appears. For example, the “added all” discount rule type has a value of 50. Giving your discount rule type a value of 51 ensures that it appears immediately after the “added all” type.

Review the CommerceDiscountRuleType Interface

Implement the following methods:

public boolean evaluate(
        CommerceDiscountRule commerceDiscountRule,
        CommerceContext commerceContext)
    throws PortalException;

This method is where you implement the business logic for evaluating when the discount rule is applied.

public String getKey();

This provides a unique identifier for the discount rule type in the discount rule type registry. The key can be used to fetch the new type from the registry.

public String getLabel(Locale locale);

This returns a text label that describes how the discount rule is applied. See the implementation in M6A8CommerceDiscountRuleTypeImpl.java for a reference in retrieving the label with a language key.

Annotate the JSP Contributor Class for OSGi Registration

@Component(
    property = "commerce.discount.rule.type.jsp.contributor.key=m6a8",
    service = CommerceDiscountRuleTypeJSPContributor.class
)
public class M6A8CommerceDiscountRuleTypeJSPContributor
    implements CommerceDiscountRuleTypeJSPContributor {

It is important to provide a distinct key for the JSP contributor so that Liferay Commerce can distinguish the contributor from others in the discount rule type JSP contributor registry. Declaring a key that is already in use overrides the existing associated type.

Review the CommerceDiscountRuleTypeJSPContributor Interface

Implement the following method:

public void render(
        long l, long l1, HttpServletRequest httpServletRequest,
        HttpServletResponse httpServletResponse)
    throws Exception;

This is where the code to render a custom UI input for our discount rule type goes.

Complete the Discount Rule Type

The discount rule type is comprised of back-end logic for evaluating when to apply a discount rule to an order, logic to render UI inputs for the discount rule type, and the custom UI inputs themselves. Follow these steps:

Configure the ServletContext for the Module

Define the ServletContext in the JSP contributor class using the bundle’s symbolic name so it can find the JSP:

@Reference(target = "(osgi.web.symbolicname=com.acme.m6a8.web)")
private ServletContext _servletContext;

The value set for osgi.web.symbolicname matches the value for Bundle-SymbolicName in the bnd.bnd file. These values must match for the ServletContext to locate the JSP.

Declare a unique value for Web-ContextPath in the bnd.bnd file so the ServletContext is correctly generated. In this example, Web-ContextPath is set to /m6a8-web. See the bnd.bnd file for a reference on these values.

Implement the CommerceDiscountRuleTypeJSPContributor’s render Method

@Override
public void render(
        long commerceDiscountId, long commerceDiscountRuleId,
        HttpServletRequest httpServletRequest,
        HttpServletResponse httpServletResponse)
    throws Exception {

    _jspRenderer.renderJSP(
        _servletContext, httpServletRequest, httpServletResponse,
        "/view.jsp");
}

Use a JSPRenderer to render the JSP for the discount rule type’s custom UI input (in our example, view.jsp). Provide the ServletContext as a parameter to find the JSP.

Add the Evaluation Logic to evaluate

@Override
public boolean evaluate(
        CommerceDiscountRule commerceDiscountRule,
        CommerceContext commerceContext)
    throws PortalException {

    CommerceOrder commerceOrder = commerceContext.getCommerceOrder();

    if (commerceOrder == null) {
        return false;
    }

    List<CommerceOrderItem> commerceOrderItems =
        commerceOrder.getCommerceOrderItems();

    int mininumNumberOfItems = GetterUtil.getInteger(
        commerceDiscountRule.getSettingsProperty(
            commerceDiscountRule.getType()));

    if (commerceOrderItems.size() >= mininumNumberOfItems) {
        return true;
    }

    return false;
}

Implement any conditions here that must be true for a discount rule to be applied. This example checks that the order contains at least a minimum number of items, using a minimum value defined by a custom UI input (stored as a String within the CommerceDiscountRule).

The CommerceOrder object represents information about the order being evaluated. See CommerceOrder.java and CommerceOrderModel.java to find more information you can get from a CommerceOrder.

Add a JSP to Render the Custom UI Input

The example uses a JSP called view.jsp with a numeric input for a minimum number of products.

<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>

<aui:input label="minimum-number-of-items" name="typeSettings" type="text">
    <aui:validator name="digits" />
    <aui:validator name="min">1</aui:validator>
</aui:input>

Implement UI elements to present when defining a discount rule. These appear immediately after selecting the discount rule type. Defining an input causes the saved value to be stored in the discount rule’s settings properties.

See Using AUI Taglibs for more information on using AUI inputs.

Add the Language Keys to Language.properties

Add the language keys and their values to a Language.properties file:

has-a-minimum-number-of-items=Has a Minimum Number of Items
minimum-number-of-items=Minimum Number of Items

See Localizing Your Application for more information.

Conclusion

Congratulations! You now know the basics for implementing the CommerceDiscountRuleType interface, and have added a new discount rule type with a custom UI input to Liferay Commerce.

Additional Information