Cómo crear una regla de descuento para Liferay Commerce - DEV24 - (Parte 1)

DISCLAIMER: This blog  post has Spanish and English version.

Hola a todos, tal como prometí en Dev24, aquí tenéis el blog con el paso a paso de principio a fin. Aquí puedes aprender cómo crear una extensión de “Regla de Descuento” para Liferay Commerce, haciéndola además configurable. 

Aquí tienes el enlace en caso de que no pudieras asistir en vivo al evento de Dev24 donde dimos una introducción: “Forget Amazon, help your users to buy with Alexa from Liferay Commerce (thanks to its headless APIs)

En este momento, las extensiones para Liferay Commerce extensions no están disponibles como plantillas de BLADE y por ello, explico toda la estructura de proyectos y andamiaje necesario.

Si entiendes toda la estructura que es necesaria, podrás aplicarlo para crear diferentes tipos de extensiones para Liferay Portal 7.X o Liferay Commerce

Crearemos una regla de descuento basada en la edad del comprador. Vamos allá.

Paso 1, creando nuestra nueva extensión de Regla de Descuento para Commerce:

1.  Con Blade CLI, podemos comenzar creando un Liferay Workspace, en caso de que no tengas uno ya para Liferay 7.3.

blade init -v 7.3 liferay-workspace-7-3

2.  En tu “Liferay Workspace”, creamos un nuevo módulo de descuento: 

blade create -t api -p com.liferay.commerce.discount.rule.by.age -c CommerceDiscountRuleByAge commerce-discount-rule-by-age

3.  Ahora deberías tener una estructura de proyecto como la siguiente:

4.  Abre el fichero gradle.properties con un editor de texto, descomenta y cambia el valor de la propiedad de “liferay.workspace.home.dir=bundle” a la ruta donde se encuentra Liferay Commerce instalado (por ej.: “liferay.workspace.home.dir=/Users/roselainedefaria/Liferay/Source/bundles”). Esta configuración nos permitirá desplegar el módulo automáticamente.

Recuerda: comenzamos creando un Liferay Workspace para la versión 7.3, así que nuestro bundle de Liferay Portal/DXP debe ser de 7.3 (en mi caso y en el momento de escribir este blog, este bundle fue construido desde el código fuente de master, clonado desde Liferay Github, y construido con el comando “ant all”). Si quieres usar otra versión, comienza de nuevo y presta atención a las versiones de las dependencias  en el fichero build.gradle file, que tendrás que adecuar. 

5.  Ahora, renombra el paquete de “com.liferay.commerce.discount.rule.by.age.api” a “com.liferay.commerce.discount.rule.by.age” para eliminar la palabra “api” que “blade CLI” genera automáticamente (esto es porque usamos la plantilla “api” previamente).

6.  En el fichero “bnd.bnd” elimina la línea “Export-Package: com.liferay.commerce.discount.rule.by.age.api”.

7.  Abre la carpeta “resources” (i.e.:liferay-workspace-7-3/modules/commerce-discount-rule-by-age/src/main/resources) y borra todo en ella.

8.  Abre el fichero “CommerceDiscountRuleByAge.java” y cambia el tipo de interface a class que implementa “CommerceDiscountRuleType” interfaz y guarda.

9.  Ahora añadimos las dependencias a nuestro proyecto. Abre el fichero build.gradle file, elimina todo de este fichero y añade las siguientes dependencias:

dependencies {
  compileOnly group: "org.osgi", name: "org.osgi.core", version: "5.0.0"
  compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations"
  compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel"
  compileOnly group: "com.liferay.commerce", name: "com.liferay.commerce.api", version: "22.2.4"
  compileOnly group: "com.liferay.commerce", name: "com.liferay.commerce.discount.api", version: "7.0.0"
  compileOnly group: "javax.servlet", name: "javax.servlet-api", version: "3.0.1"
}

10.  Vuelve a la clase “CommerceDiscountRuleByAge”, importa el paquete requerido por la interfaz implementa los tres métodos definidos por la interfaz. 

11.  Crea la siguiente variable (KEY):

public static final String KEY = "key-rule-gold-person";

​​​​​​

Ten en cuenta: “Esta KEY identificará a tu módulo ya que es importante proveer una clave distinta para cada nuevo módulo (de extensión de tipo de regla de descuento), y así Liferay Commerce pueda distinguir unas de otras. Si reutilizas una clave existente que esté ya en uso, reemplazarás el módulo de existente”

12.  Anota esta clase con  @Component y establece 3 propiedades. Debería quedar así:

@Component(
      immediate = true,
      property = {
              "commerce.discount.rule.type.key=" + CommerceDiscountRuleByAge.KEY,
              "commerce.discount.rule.type.order=:Integer=61"
      },service = CommerceDiscountRuleType.class
)
  • En el array de propiedades, establece los siguientes 2 valores necesarios para nuestro módulo de descuento:

    • "commerce.discount.rule.type.key=" + CommerceDiscountRuleOldPerson.KEY

    •  "commerce.discount.rule.type.order:Integer=61"

El valor de la propiedad ”commerce.discount.rule.type.order” indica el lugar que ocupará en la lista de reglas de descuento disponibles . En este momento, el último valor disponible en Commerce es de 60, así que usaremos 61 para que sea la última.
  • Como servicio, declara la interfaz que estamos usando en nuestra clase:

    • service = CommerceDiscountRuleType.class . 

13.  Ahora en el método “getKey” incluya la sentencia “return KEY”  para devolver la clave que declaramos antes, y en el método “getLabel” útil solo para poder probar el módulo, incluye la sentencia “return “New Custom Discount Type”.  La clase @Component debería quedar así:

package com.liferay.commerce.discount.rule.by.age;
import com.liferay.commerce.context.CommerceContext;
import com.liferay.commerce.discount.model.CommerceDiscountRule;
import com.liferay.commerce.discount.rule.type.CommerceDiscountRuleType;
import com.liferay.portal.kernel.exception.PortalException;
import org.osgi.service.component.annotations.Component;
import java.util.Locale;
/**
* @author roselainedefaria
*/
@Component(
      immediate = true,
      property = {
              "commerce.discount.rule.type=" + CommerceDiscountRuleByAge.KEY,
              "commerce.discount.rule.order=:Integer=61"
      },service = CommerceDiscountRuleType.class
)
public class CommerceDiscountRuleByAge implements CommerceDiscountRuleType {

  public static final String KEY = "key-rule-gold-person";

  @Override
  public boolean evaluate(CommerceDiscountRule commerceDiscountRule, CommerceContext commerceContext) throws PortalException {
      return false;
  }

  @Override
  public String getKey() {
      return CommerceDiscountRuleByAge.KEY;
  }

  @Override
  public String getLabel(Locale locale) {
      return "New Custom Discount Type";
  }
}

14.  Despliega tu módulo ejecutando el siguiente comando en la carpeta de tu “Liferay Workspace”:

liferay-workspace-7-3 % ./gradlew deploy

15.  Si todo fue bien, el resultado será el siguiente (se enviará tu módulo a tu “Liferay Portal/DXP Bundle"):

> Task :modules:commerce-discount-rule-by-age:deploy
Files of project ':modules:commerce-discount-rule-by-age' deployed to /Users/roselainedefaria/Liferay/Source/bundles/osgi/modules

BUILD SUCCESSFUL in 4s

En Liferay Portal/DXP: Abre el Menu > Commerce > Descuentos > Añadir Nuevo Descuento. En el descuento creado, al final habrá una sección de  “Reglas”, haz click en “+” para añadir una nueva.  La nueva “Regla de Descuento” creada debería aparecer en la lista.

                       El código usado en este paso está disponible en una tag llamada “step-1” en mi repo de github. 

Paso 2, eliminando el “hard-code” e internacionalizando:

Como sabrás, es una mala práctica utilizar un String con un valor codificado “a fuego” (o hard-code) como hicimos en el paso previo en el método “getLabel”  (especialmente en un portal multi-idioma). Es importante que nuestro módulo sea multi-idioma, como muchas cosas en el mundo Liferay, así que tendremos que transformar el módulo para incluir capacidades multilenguaje. 

1.  Para ello... en el método “getLabel” usaremos un objeto “ResourceBundle”. Copia el siguiente fragmento de código y pégalo en el método “getLabel”. Después, importa todos los paquetes necesarios apoyándote en el IDE que utilices como Liferay Developer Studio.

ResourceBundle resourceBundle = ResourceBundleUtil.getBundle( "content.Language", locale, getClass());
return LanguageUtil.get(resourceBundle, "discount-rule-by-age");
  • “content.Language” es el nombre base del fichero de traducciones. 
  • “locale” se obtiene del usuario actual.
  • El método “getClass()” es la clase actual.

2.  Ahora, crearemos los ficheros de propiedades con las traducciones en el directorio “resources”:

  • Dentro de la carpeta resources (i.e.: liferay-workspace-7-3/modules/commerce-discount-rule-by-age/src/main/resources), crea una nueva carpeta llamada “content” 
  • Dentro de la carpeta ”content” crea un fichero con el idioma por defecto “Language.properties”,  y en mi caso también los ficheros “Language_pt_BR.properties” y “Language_es_ES.properties”.

  • Copia la clave declarada antes: “rule-gold-person” en el método “getLabel”. Incluyela en los 3 ficheros de propiedades (a continuación, encontrarás las traducciones para cada idioma).

En Language.properties: 

discount-rule-by-age=Discount Rule By Age

En Language_es_ES.properties 

discount-rule-by-age=Regla de Descuento por edad

En Language_pt_BR.properties 

discount-rule-by-age=Regra de Desconto por idade                                                                                                                                                             

3.  Guarda los cambios y despliega el módulo ejecutando el siguiente comando dentro del directorio de tu Liferay Workspace:

liferay-workspace-7-3 % ./gradlew deploy

En Liferay Portal/DXP, abre el Menu > Commerce > Descuentos > haz click en la regla de descuento creada antes. Hacia el final, habrá una sección con “Reglas”, haz click en “+” para añadir una nueva regla. Como resultado, aparecerá en la lista Discount Rule By Age.

4.  Para probar con otros idiomas (portugués y español):

    • Ve a “localhost:8080” y al final de la URL añade “/es-ES/“ (http://localhost:8080/es-ES/)

    • Después, abre Menú > Commerce > Descuentos > haz click en el descuento creado antes. Hacia el final, habrá una sección para “Reglas”, haz click en “+” para añadir una nueva. Como resultado, en la lista debería aparecer el texto en español Regla de Descuento por edad.

 

    • Ve a “localhost:8080” y al final de la URL añade “/pt-BR/“ (http://localhost:8080/pt-BR/)

    • Después, abre Menú > Commerce > Descuentos > haz click en el descuento creado antes. Hacia el final, habrá una sección para “Reglas”, haz click en “+” para añadir una nueva. Como resultado, en la lista debería aparecer el texto en portugués Regla de Descuento por edad.

                             ¡Felicidades! Ya has creado tu primera extensión de Commerce internacionalizada.

El código usado en este paso está disponible en una etiqueta con nombre “step-2” en mi repo de github. 

Paso 3, incluyendo la lógica para el descuento por edad:

1.  Vuelve a tu clase Component “CommerceDiscountRuleByAge”, dentro del método evaluaterecuperaremos el CommerceOrder del CommerceContext, y comprobaremos is commerceOrder es NULL (para parar el flujo).

CommerceOrder commerceOrder = commerceContext.getCommerceOrder();

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

2.  Al final de la clase, inyecta el servicio “UserLocalService” con la anotación @Reference.

@Reference
protected UserLocalService userLocalService;

3.  In the “evaluate” method, now you will recover the user, using the “userLocalService”.

User user = userLocalService.fetchUser(commerceOrder.getUserId());

4.  Implementa un poco más de código para recuperar la edad del usuario actual, y así decidir si la regla se aplicará.

User user = userLocalService.fetchUser(commerceOrder.getUserId());
Calendar birthdayCal = CalendarFactoryUtil.getCalendar();
birthdayCal.setTime(user.getBirthday());

int birthdayMonth = birthdayCal.get(Calendar.MONTH) +1;
int birthdayDay = birthdayCal.get(Calendar.DATE);
int birthdayYear = birthdayCal.get(Calendar.YEAR);

LocalDate birthdayLocalDate = LocalDate.of(birthdayYear, birthdayMonth, birthdayDay);
LocalDate now = LocalDate.now();
Period period = Period.between(birthdayLocalDate, now);

if(period.getYears() >= 70){
  return true;
}

5.  Tras el paso previo, el método evaluate debería quedar como sigue:

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

  CommerceOrder commerceOrder = commerceContext.getCommerceOrder();

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

  User user = userLocalService.fetchUser(commerceOrder.getUserId());

  Calendar birthdayCal = CalendarFactoryUtil.getCalendar();
  birthdayCal.setTime(user.getBirthday());

  int birthdayMonth = birthdayCal.get(Calendar.MONTH) +1;
  int birthdayDay = birthdayCal.get(Calendar.DATE);
  int birthdayYear = birthdayCal.get(Calendar.YEAR);

  LocalDate birthdayLocalDate = LocalDate.of(birthdayYear, birthdayMonth, birthdayDay);
  LocalDate now = LocalDate.now();
  Period period = Period.between(birthdayLocalDate, now);

  if(period.getYears() >= 70){
      return true;
  }

  return false;
}

6.  Guarda los cambios y  despliega el módulo ejecutando el comando siguiente en tu “Liferay Workspace”.

liferay-workspace-7-3 % ./gradlew deploy

Adicionalmente, puedes añadir trazas en este clase. Para más información, consulta mi repo de GitHub.

El código usado en este paso está disponible en una etiqueta con nombre “step-3” en mi repo de github. 

Pruebas:

  •  
  • En Liferay Portal/DXP, abre Menú > Commerce > Descuentos > haz click en el descuento creado previamente. Hacia el final, en la sección “Reglas”, si hay alguna regla, borrala, y haz click en “+” para añadir una regla de descuento. Como resultado, en la lista debería aparecer Discount Rule By Age'' creada, dale un nombre  y presiona en “Submit”. En la lista d e reglas, asegúrate de que sólo aparece tu regla de descuento para poder probar.

  • La configuración de descuentos debería ser más o menos así (resalto las importantes a configurar):

  • En la pestaña Aptitud (o “Eligibility” en inglés) puedes ver más configuración para asociar el descuento a “Grupos de cuentas específicas”,”Cuenta específica” o “Canal específico”.

Son configuraciones muy útiles y flexibles. Podrás aplicarlas más tarde (por ahora, las podemos dejar en su configuración por defecto, así que serán aplicadas a todas las cuentas y canales). Así que, no cambies nada aquí.

  • Para probar necesitamos a un usuario del portal con más de 70 años. Para probar: Abre Menú > Control Panel > Usuarios y Organizaciones > haz click en el usuario actual, y cambian la fecha de nacimiento a una fecha de hace más de 70 años.

Ahí lo tienes, módulo finalizado, pero podemos hacerlo mejor, en la próxima publicación del blog, aprenderá cómo hacer que la edad sea configurable y eliminar la "edad" como hard-code en su código.