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 provides three discount rule types out-of-the-box: AddedAllCommerceDiscountRuleTypeImpl, AddedAnyCommerceDiscountRuleTypeImpl, and CartTotalCommerceDiscountRuleTypeImpl.
Deploy an Example Discount Rule Type
Start a new Liferay instance by running
docker run -it -m 8g -p 8080:8080 liferay/portal:7.4.3.120-ga120
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 instance:
-
Download and unzip the Acme Commerce Discount Rule Type.
curl https://resources.learn.liferay.com/commerce/latest/en/developer-guide/promotions/liferay-m6a8.zip -O
unzip liferay-m6a8.zip
-
Build and deploy the example.
./gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
NoteThis command is the same as copying the deployed jars to
/opt/liferay/osgi/modules
on the Docker container. -
Confirm the deployment in the Liferay Docker container console.
STARTED com.acme.m6a8.web_1.0.0
-
Verify that the example discount rule type was added. Open your browser to
https://localhost:8080
. Then click the Global Menu () and navigate to Commerce → Discounts. Click Edit within the menu for any discount. Scroll down to the Rules section and click Add () to add a new discount rule. The new discount rule type (“Has a minimum number of products”) is present under the Type dropdown.
In Commerce 2.1 and earlier, you can find discounts by navigating to Control Panel → Commerce → Discounts. Click any Edit within the menu for any discount and then click Rules at the top of the screen.
Congratulations, you’ve successfully built and deployed a new discount rule type that implements CommerceDiscountRuleType
.
Anatomy of a Discount Rule Type
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
- Review the
CommerceDiscountRuleType
Interface - Annotate the JSP Contributor Class for OSGi Registration
- Review the
CommerceDiscountRuleTypeJSPContributor
Interface - Complete the Discount Rule Type
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 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 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 commerceDiscountId, long commerceDiscountRuleId,
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 comprises of the backend 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.
- Configure the
ServletContext
for the module. - Implement the
CommerceDiscountRuleTypeJSPContributor
’srender
method. - Add the evaluation logic to
evaluate
. - Add a JSP to render the custom UI input.
- Add the language keys to
Language.properties
.
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.