openxava / documentación / Lección 11: @DefaultValueCalculator en colecciones

Curso: 1. Primeros pasos | 2. Modelo básico del dominio (1) | 3. Modelo básico del dominio (2) | 4. Refinar la interfaz de usuario | 5. Desarrollo ágil | 6. Herencia de superclases mapeadas | 7. Herencia de entidades | 8. Herencia de vistas | 9. Propiedades Java | 10. Propiedades calculadas | 11. @DefaultValueCalculator en colecciones | 12. @Calculation y totales de colección | 13. @DefaultValueCalculator desde archivo | 14. Evolución del esquema manual | 15. Cálculo de valor por defecto multiusuario | 16. Sincronizar propiedades persistentes y calculadas | 17. Lógica desde la base de datos | 18. Validando con @EntityValidator | 19. Alternativas de validación  | 20. Validación al borrar  21. Anotación Bean Validation propia | 22. Llamada REST desde una validación  | 23. Atributos en anotaciones  | 24. Refinar el comportamiento predefinido | 25. Comportamiento y lógica de negocio | 26. Referencias y colecciones | A. Arquitectura y filosofía | B. Java Persistence API | C. Anotaciones | D. Pruebas automáticas

Tabla de contenidos

Lección 11: @DefaultValueCalculator en colecciones
Usar @DefaultValueCalculator
Resumen
Hemos agregado lógica del negocio a nuestra aplicación utlizando propiedades persistentes y propiedas calculadas, ahora utilizaremos la anotación @DefaultValueCalculator para las colecciones.
Si no te gustan los videos sigue las instrucciones a continuación.

Usar @DefaultValueCalculator

La forma en que calculamos el importe de la línea de detalle no es la mejor. Tiene, al menos, dos inconvenientes. El primero es que el usuario puede querer tener la posibilidad de cambiar el precio unitario. Y segundo, si el precio de un producto cambia los importes de todas las facturas cambian también, y esto no es bueno.
Para evitar estos inconvenientes lo mejor es almacenar el precio de cada producto en cada línea de detalle. Añadamos pues una propiedad persistente precioPorUnidad a la clase Detalle y calculemos su valor desde precio de Producto usando un @DefaultValueCalculator. De tal forma que consigamos el efecto que puedes ver en la siguiente figura:
business-logic_es020.png
La lógica para calcular el valor inicial la tendremos en CalculadorPrecioPorUnidad que simplemente lee el precio del producto. Observa el código de este calculador:
package com.tuempresa.facturacion.calculadores; // En el paquete calculadores
 
import org.openxava.calculators.*;
import com.tuempresa.facturacion.modelo.*;
import lombok.*;
 
import static org.openxava.jpa.XPersistence.*; //Para usar getManager()
 
public class CalculadorPrecioPorUnidad implements ICalculator {
 
    @Getter @Setter
    int numeroProducto;
 
    @Override
    public Object calculate() throws Exception {
        Producto producto = getManager() // getManager() de XPersistence
            .find(Producto.class, numeroProducto); // Busca el producto
        return producto.getPrecio();    // Retorna su precio
    }
 
}
El siguiente paso es añadir la propiedad precioPorUnidad. Añade el siguiente código a la clase Detalle:
@DefaultValueCalculator(
    value=CalculadorPrecioPorUnidad.class, // Esta clase calcula el valor inicial
    properties=@PropertyValue(
        name="numeroProducto", // La propiedad numeroProducto del calculador...
        from="producto.numero") // ... se llena con el valor de producto.numero de la entidad
)
@Money
BigDecimal precioPorUnidad; // Una propiedad persistente convencional
De esta forma cuando el usuario escoge un producto el campo de precio unitario se rellena con el precio del producto, pero dado que es una propiedad persistente, el usuario puede cambiar este valor. Y si en el futuro el precio del producto cambiara este precio unitario de la línea de detalle no cambiaría.
Esto implica que has de adaptar la propiedad calculada importe:
@Money
@Depends("precioPorUnidad, cantidad") // precioPorUnidad en vez de producto.numero
public BigDecimal getImporte() {
    if (precioPorUnidad == null) return BigDecimal.ZERO; // precioPorUnidad en vez de producto y producto.getPrecio()
    return new BigDecimal(cantidad).multiply(precioPorUnidad); // precioPorUnidad en vez de producto.getPrecio()
}
Ahora getImporte() usa precioPorUnidad como fuente en lugar de producto.precio.
Finalmente, debemos editar la entidad DocumentoComercial y modificar la lista de propiedades de la colección para mostrar la nueva propiedad:
@ElementCollection
@ListProperties("producto.numero, producto.descripcion, cantidad, precioPorUnidad, importe") // precioPorUnidad añadida
Collection<Detalle> detalles;
Prueba los módulos Pedido y Factura y podrás observar el nuevo comportamiento al añadir líneas de detalle.

Resumen

En esta lección hemos aprendido cómo podemos utilizar la anotación @DefaultValueCalculator para definir el valor de diferentes propiedades.

Descargar código fuente de esta lección

¿Problemas con la lección? Pregunta en el foro ¿Ha ido bien? Ve a la lección 12