SIGESA: Crear Bitacora con TRASIENT Maestro detalle

  • REFERENCIAS:
    • ORGINAL BitacoraPXE->PlanAccionEstrategica (BTPPI-452)
    • TRABAJADA: BitacoraEvaluacionPOA -> EvaluacionOperativa
  • Vamos a crear un maestro detalle para crear la relación de maestro (evaluacionOperativa) y detalle (bitacoraEvaluacionPOA)
  • TAREA: BTPPI-457 Crear pestaña de visualización de la bitácora de aprobación de las evaluaciones operativas
Crear una pestaña en la pantalla de Mantenimiento de Evaluación Operativa (evaluacionOperativaEditForm.xhtml)

Bitácora de Aprobación

Crear una tabla para visualizar la bitácora de aprobación de las Evaluaciones Operativas

La tabla debe mostrar los siguientes campos:

Estado

Fecha y hora

Usuario

Mostrar la cédula

Nombre

Mostrar el nombre completo

Rol

Mostrar los roles del usuario que pertenezcan al módulo PPI-PPO

Unidad Ejecutora

Mostrar la unidad ejecutora del usuario con la cual tenga rol ACL para el módulo PPI-PPO-POA
  • Aquí lo que cabe explicar es que la bitacora solo tiene el id del usuario por tanto la siguiente información la vamos a sacar directamente del JPA
    • EstadoFormulacionPXENombre
    • usuarioIdentificacion
  • Pero la información de los
    • usuarioNombreCompleto
    • usuarioRoles
    • usuarioUnidadEjecutora
  • La debemos obtener del servicio ya que lleva todo un proceso de obtención de la información

BitacoraEvaluacionPOA.java

  • Acá vamos a crear unos campos tipo trasient String que nos permitan almacenar la información adicional a la entidad
# ******************************************************
# Imports
# ******************************************************
import javax.persistence.Transient;

# ******************************************************
# Creamos campos trasient para almacenar la información faltante
# ******************************************************
@Transient
    private String estadoEvaluacionPOANombre;

    @Transient
    private String usuarioIdentificacion;

    @Transient
    private String usuarioNombreCompleto;

    @Transient
    private String usuarioRoles;

    @Transient
    private String usuarioUnidadEjecutora;

# ******************************************************
# Datos que podemos obtener directamente del JPA
# ******************************************************
public String getEstadoEvaluacionPOANombre() {
        this.estadoEvaluacionPOANombre = "";
        if (this.estadoEvaluacionPOA != null) {
            this.estadoEvaluacionPOANombre = estadoEvaluacionPOA.getNombre();
        }
        return this.estadoEvaluacionPOANombre;
    }

    public void setEstadoEvaluacionPOANombre(String estadoEvaluacionPOANombre) {
        this.estadoEvaluacionPOANombre = estadoEvaluacionPOANombre;
    }

    public String getUsuarioIdentificacion() {
        this.usuarioIdentificacion ="";
        if (this.usuario != null) {
            this.usuarioIdentificacion = usuario.getNombreUsuario();
        }
        return this.usuarioIdentificacion;
    }

    public void setUsuarioIdentificacion(String usuarioIdentificacion) {
        this.usuarioIdentificacion = usuarioIdentificacion;
    }

# ******************************************************
# Datos que debemos de obtener desde un service
# ******************************************************
public String getUsuarioNombreCompleto() {
        return this.usuarioNombreCompleto;
    }

    public void setUsuarioNombreCompleto(String usuarioNombreCompleto) {
        this.usuarioNombreCompleto = usuarioNombreCompleto;
    }

    public String getUsuarioRoles() {
        return this.usuarioRoles;
    }

    public void setUsuarioRoles(String usuarioRoles) {
        this.usuarioRoles = usuarioRoles;
    }

    public String getUsuarioUnidadEjecutora() {
        return this.usuarioUnidadEjecutora;
    }

    public void setUsuarioUnidadEjecutora(String usuarioUnidadEjecutora) {
        this.usuarioUnidadEjecutora = usuarioUnidadEjecutora;
    }
  • Con esto estamos cargando los datos que podemos de una vez y los demás los obtendremos luego
  • RESULTADO
/*
 * Copyright (c) 2022.
 *
 * Centro de Gestion Informatica
 * Direccion de Tecnologias de la Informacion y Comunicacion
 * Universidad Nacional - Costa Rica
 * http://www.una.ac.cr
 *
 */

/**********************************************************************/
/*********************       PACKAGE     ******************************/
/**********************************************************************/

package cr.ac.una.cgi.sigesa.ppi.ppo.domain;

/**********************************************************************/
/*********************       IMPORT'S    ******************************/
/**********************************************************************/
import java.util.Date;

import javax.persistence.AttributeOverride;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.Transient;

import cr.ac.una.cgi.sdkuna.domain.CampoConfigurable;
import cr.ac.una.cgi.sdkuna.domain.Usuario;
import cr.ac.una.cgi.sdkuna.generic.BaseEntity;

/**********************************************************************/
/*********************    DOCUMENTACIÓN  ******************************/
/**********************************************************************/
/**
 * Entity para la administración de la entidad {@link BitacoraEvaluacionPOA}
 *
 * @author: Gustavo Matamoros González
 * @fechaCreacion:  31/03/2023
 * @Version:        1.0.0
 */
@Entity
@Table(name = "BITACORA_EVALUACION_POA")
@AttributeOverride(name = "id", column = @Column(name = "ID_BITACORA_EVALUACION_POA"))
@SequenceGenerator(name = "sequence", sequenceName = "SQ_BITACORA_EVALUACION_POA", allocationSize = 1)
public class BitacoraEvaluacionPOA extends BaseEntity<Usuario, CampoConfigurable> {

    @ManyToOne
    @JoinColumn(name = "EVALUACION_OPERATIVA")
    private EvaluacionOperativa evaluacionOperativa;

    @ManyToOne
    @JoinColumn(name = "ESTADO_EVALUACION_POA")
    private EstadoEvaluacionPOA estadoEvaluacionPOA;

    @ManyToOne
    @JoinColumn(name = "USUARIO")
    private Usuario usuario;

    @Column(name = "FECHA")
    @Temporal(javax.persistence.TemporalType.TIMESTAMP)
    private Date fecha;

    @Transient
    private String estadoEvaluacionPOANombre;

    @Transient
    private String usuarioIdentificacion;

    @Transient
    private String usuarioNombreCompleto;

    @Transient
    private String usuarioRoles;

    @Transient
    private String usuarioUnidadEjecutora;


    public EvaluacionOperativa getEvaluacionOperativa() {
        return evaluacionOperativa;
    }

    public void setEvaluacionOperativa(EvaluacionOperativa evaluacionOperativa) {
        this.evaluacionOperativa = evaluacionOperativa;
    }

    public EstadoEvaluacionPOA getEstadoEvaluacionPOA() {
        return estadoEvaluacionPOA;
    }

    public void setEstadoEvaluacionPOA(EstadoEvaluacionPOA estadoEvaluacionPOA) {
        this.estadoEvaluacionPOA = estadoEvaluacionPOA;
    }

    public Usuario getUsuario() {
        return usuario;
    }

    public void setUsuario(Usuario usuario) {
        this.usuario = usuario;
    }

    public Date getFecha() {
        return fecha;
    }

    public void setFecha(Date fecha) {
        this.fecha = fecha;
    }

    public String getEstadoEvaluacionPOANombre() {
        this.estadoEvaluacionPOANombre = "";
        if (this.estadoEvaluacionPOA != null) {
            this.estadoEvaluacionPOANombre = estadoEvaluacionPOA.getNombre();
        }
        return this.estadoEvaluacionPOANombre;
    }

    public void setEstadoEvaluacionPOANombre(String estadoEvaluacionPOANombre) {
        this.estadoEvaluacionPOANombre = estadoEvaluacionPOANombre;
    }

    public String getUsuarioIdentificacion() {
        this.usuarioIdentificacion ="";
        if (this.usuario != null) {
            this.usuarioIdentificacion = usuario.getNombreUsuario();
        }
        return this.usuarioIdentificacion;
    }

    public void setUsuarioIdentificacion(String usuarioIdentificacion) {
        this.usuarioIdentificacion = usuarioIdentificacion;
    }

    public String getUsuarioNombreCompleto() {
        return this.usuarioNombreCompleto;
    }

    public void setUsuarioNombreCompleto(String usuarioNombreCompleto) {
        this.usuarioNombreCompleto = usuarioNombreCompleto;
    }

    public String getUsuarioRoles() {
        return this.usuarioRoles;
    }

    public void setUsuarioRoles(String usuarioRoles) {
        this.usuarioRoles = usuarioRoles;
    }

    public String getUsuarioUnidadEjecutora() {
        return this.usuarioUnidadEjecutora;
    }

    public void setUsuarioUnidadEjecutora(String usuarioUnidadEjecutora) {
        this.usuarioUnidadEjecutora = usuarioUnidadEjecutora;
    }

}

BitacoraEvaluacionPOABean.java

  • Luego vamos a crear en bean para el manejo del listform del framework
  • Aqupi lo que hacemos que cada campo del BitacoraEvaluacionPOA.java que deseamos mostrar debe estar presente
  • RESULTADO

package cr.ac.una.cgi.sigesa.ppi.ppo.view.model;
/*
 * Copyright (c) 2023.
 *
 * Centro de Gestion Informatica
 * Direccion de Tecnologias de la Informacion y Comunicacion
 * Universidad Nacional - Costa Rica
 * 
http://www.una.ac.cr/

 *
 */

import cr.ac.una.cgi.sdkuna.view.commons.ASER;
import cr.ac.una.cgi.sdkuna.view.commons.ASERColumn;
import cr.ac.una.cgi.sdkuna.view.commons.ASERImpl;
import cr.ac.una.cgi.sigesa.ppi.ppo.domain.BitacoraEvaluacionPOA;

import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

/**
 *
 * @author Gustavo Matamoros González
 * @since 03/05/2024
 * @version 1.0.0
 * 
 */
@Component
@Scope("session")
public class BitacoraEvaluacionPOABean extends ASERImpl<BitacoraEvaluacionPOA> implements ASER {

    public BitacoraEvaluacionPOABean() {

    }

    @PostConstruct
    public void init() {
        List<ASERColumn> columns = new ArrayList();

        ASERColumn column1 = new ASERColumn("estadoEvaluacionPOANombre",    getI18n("bitacoraEvaluacionPOA_estadoEvaluacionPOANombre_header"),  true);
        ASERColumn column2 = new ASERColumn("fecha",                        getI18n("bitacoraEvaluacionPOA_fecha_header"),                      true);
        ASERColumn column3 = new ASERColumn("usuarioIdentificacion",        getI18n("bitacoraEvaluacionPOA_usuarioIdentificacion_header"),      true);
        ASERColumn column4 = new ASERColumn("usuarioNombreCompleto",        getI18n("bitacoraEvaluacionPOA_usuarioNombreCompleto_header"),      true);
        ASERColumn column5 = new ASERColumn("usuarioRoles",                 getI18n("bitacoraEvaluacionPOA_usuarioRoles_header"),               true);
        ASERColumn column6 = new ASERColumn("usuarioUnidadEjecutora",       getI18n("bitacoraEvaluacionPOA_usuarioUnidadEjecutora_header"),     true);

        columns.add(column1);
        columns.add(column2);
        columns.add(column3);
        columns.add(column4);
        columns.add(column5);
        columns.add(column6);

        this.setColumnsDetails(columns);
    }

}

EvaluacionOperativa.java

  • Ya que tenemos los campos adicionales en la bitacora y tenemos un bean que puede presentar la información necesaria
  • Debemos hacer que el maestro en este caso EvaluacionOperativa tenga una lista de bitacora
  • Para esto hacemos
# Agregamos una lista hija donde de van a relacionar EvaluacionOperativa->BitacoraEvaluacionPOA a través del campo de bitacora llamado "evaluacionOperativa"


@OneToMany(fetch = FetchType.LAZY, mappedBy = "evaluacionOperativa")
private List<BitacoraEvaluacionPOA> listaBitacoraEvaluacionPOA;

# SET y GET
public List<BitacoraEvaluacionPOA> getListaBitacoraEvaluacionPOA() {
        return this.listaBitacoraEvaluacionPOA;
    }

    public void setListaBitacoraEvaluacionPOA(List<BitacoraEvaluacionPOA> listaBitacoraEvaluacionPOA) {
        this.listaBitacoraEvaluacionPOA = listaBitacoraEvaluacionPOA;
    }

EvaluacionOperativaService.java

  • Ahora que tenemos dentro de la evaluación operativa una lista de bitacora debemos crear un servicio que no retorne nada pero obtenga el resto de información que requerimos como roles, nombre de usuario y unidades ejecutoras
  • Creamos el servicio
/**
     * Método que retorna la bitácora de EvaluacionOperativa pasada como parámetro
     * @param evaluacionOperativa
     * @bug BTPPI-457
     * @author Gustavo Matamoros González
     * @since 02/05/2024
     */
    public void obtenerBitacoraEvaluacionOperativa(EvaluacionOperativa evaluacionOperativa);

EvaluacionOperativaServiceImpl.java

  • Ahora creamos el servcio que obtenga los datos
# Nuevos Imports
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import cr.ac.una.cgi.sdkuna.domain.DetalleGrupoRol;
import cr.ac.una.cgi.sdkuna.domain.GrupoRol;
import cr.ac.una.cgi.sdkuna.domain.UsuarioGrupoRol;
import cr.ac.una.cgi.sdkuna.domain.UsuarioRol;
import cr.ac.una.cgi.sdkuna.service.GrupoRolService;
import cr.ac.una.cgi.sdkuna.service.DetalleGrupoRolService;
import cr.ac.una.cgi.sigesa.pge.pge.domain.Persona;
import cr.ac.una.cgi.sigesa.pge.pge.service.PersonaService;

# Nuevos Autowired
@Autowired
PersonaService personaService;

@Autowired
GrupoRolService grupoRolService;

@Autowired
DetalleGrupoRolService detalleGrupoRolService;

#nuevos metodos
/**
     * Método que retorna la bitácora de EvaluacionOperativa pasada como parámetro
     * @param evaluacionOperativa
     * @bug BTPPI-457
     * @author Gustavo Matamoros González
     * @since 02/05/2024
     */
    @Override
    public void obtenerBitacoraEvaluacionOperativa(EvaluacionOperativa evaluacionOperativa) {
        
        String usuarioNombre =""; 
        String listaRoles = "";
        String listaUnidadesEjecutoras = "";

        Pattern pattern = Pattern.compile("unidad_(.*?)_acl", Pattern.CASE_INSENSITIVE);

        for (BitacoraEvaluacionPOA bitacora : evaluacionOperativa.getListaBitacoraEvaluacionPOA()) {
                 
            usuarioNombre ="";
            listaRoles = "";
            listaUnidadesEjecutoras = "";

            Usuario usuarioActual = usuarioService.find(bitacora.getUsuario().getId());

            // Nombre Usuario
            if(bitacora.getUsuario().getId()>3){
                Persona persona = personaService.findOneByIdentificacion(bitacora.getUsuario().getUsername());
                usuarioNombre = persona.getNombre();
            }else{
                usuarioNombre = bitacora.getUsuario().getNombreUsuario() + ' ' + bitacora.getUsuario().getNombreUsuario();
            } 

            // Roles Directos
            for(UsuarioRol usuarioRol : usuarioActual.getUsuarioRoles()){
                if (usuarioRol.getRol().getNombre().contains("PPI_PPO")) {
                    if(bitacora.getFecha().compareTo(usuarioRol.getFechaDesde()) >= 0 && bitacora.getFecha().compareTo(usuarioRol.getFechaHasta()) <= 0){
                        listaRoles += usuarioRol.getRolNombre()+ "|";
                    }
                }
            }

            // Grupo de Rol
            for(UsuarioGrupoRol usuarioGrupoRol : usuarioActual.getUsuarioGrupoRoles()){
                if(bitacora.getFecha().compareTo(usuarioGrupoRol.getFechaDesde()) >= 0 && bitacora.getFecha().compareTo(usuarioGrupoRol.getFechaHasta()) <= 0){
                    listaRoles += buscaRolACL(usuarioGrupoRol.getGrupoRol());
                }
            }

            // Eliminar roles repetidos
            String[] arrayTmp = listaRoles.split("\\|");
            Set<String> arrayListaRoles = new HashSet<>(Arrays.asList(arrayTmp));
            String[] arrayRolesSinRepetidos = arrayListaRoles.toArray(new String[arrayListaRoles.size()]);

            // Construir cadenas de roles y unidades Ejecutoras
            listaRoles = "";
            if(arrayRolesSinRepetidos.length >0){
                listaUnidadesEjecutoras += "<ul "+ "class="+ '"' + "ulUnidades" + '"'+ ">";
            }
            for (String rol : arrayRolesSinRepetidos){
                listaRoles += rol+"</br>";

                Matcher matcher = pattern.matcher(rol);
                if (matcher.find()) {
                    String codigoUnidadEjecutora = matcher.group(1);
                    UnidadEjecutora unidadEjecutoraActual = unidadEjecutoraService.findByCodigo(codigoUnidadEjecutora);

                    listaUnidadesEjecutoras += "<li>" + unidadEjecutoraActual.getNombre() + "</li>";    
                }
            }
            if(arrayRolesSinRepetidos.length >0){
                listaUnidadesEjecutoras += "</ul>";
            }

            // Cargamos los valores faltantes
            bitacora.setUsuarioNombreCompleto(usuarioNombre);
            bitacora.setUsuarioRoles(listaRoles);
            bitacora.setUsuarioUnidadEjecutora(listaUnidadesEjecutoras);

        }
    }

    /**
     * Método que retorna el rol ACL de Unidad según un Grupo Rol
     * @param grupoRol
     * @bug BTPPI-457
     * @author Gustavo Matamoros González
     * @since 02/05/2024
     */
    public String buscaRolACL(GrupoRol grupoRol){
        String rolesPPIPPO ="";
        List<DetalleGrupoRol> listaDetalleGrupoRoles = grupoRol.getDetallesGrupoRol();
        
        if(!listaDetalleGrupoRoles.isEmpty()){
        
            for(DetalleGrupoRol detalle : listaDetalleGrupoRoles){
                    Rol rol = detalle.getRol();                   
                                        
                    if(rol != null){
                        if(rol.getNombre().contains("PPI_PPO") && rol.getNombre().contains("ACL")) {
                            return rol.getNombre() + "|";
                        }
                    }                   
                    
                    if(detalle.getGrupo()!= null){
                        rolesPPIPPO += buscaRolACL(detalle.getGrupo());
                    }
            }
        }
        return rolesPPIPPO;
    }

EvaluacionOperativaBean.java

  • Ahora que tenemos un metodo desde el service que nos completa la información faltante
  • Vamos a en el bean del maestro mandar a cargar los datos con la funcion en el initDetails cuando id no sea null
  • Ahora para este caso espefico la evaluacion operativa bena no tenia un metodo initDetails por tanto lo creamos
# Agregamos el autowired que nos permite relaciona 2 Bean's
@Autowired
    BitacoraEvaluacionPOABean bitacoraEvaluacionPOABean;

// Inicializa los ddetalles
    public void initDetails() {

        // Si la entidad.id != null existe y puede tener detalles
        if(this.getEntity().getId() != null){
            
            // Cargamos los valores requeridos
            service.obtenerBitacoraEvaluacionOperativa(this.getEntity());
            
            // Cargamos los valores obtenidos en la lista de la entidad
            this.getEntity().setListaBitacoraEvaluacionPOA(bitacoraEvaluacionPOABean.setDetails(this.getEntity().getListaBitacoraEvaluacionPOA()));

            // Inicializamos el bean para que se muestre
            bitacoraEvaluacionPOABean.init();
        }
    }

// Sin comentarios
public void initDetails() {

        if(this.getEntity().getId() != null){
            service.obtenerBitacoraEvaluacionOperativa(this.getEntity());
            this.getEntity().setListaBitacoraEvaluacionPOA(bitacoraEvaluacionPOABean.setDetails(this.getEntity().getListaBitacoraEvaluacionPOA()));
            bitacoraEvaluacionPOABean.init();
        }
    }
  • Con esto ya cargamos los valores necesarios

evaluacionOperativaEditForm.xhtml

  • Ahora vamos a cambiar el diseño de como se presentan los datos
  • En este ya se cuenta con un tab view per la estructura es
<p:tabView id="evaluacionOperativaTabView" activeIndex="#{evaluacionOperativaBean.activeTab}">
<p:ajax event="tabChange" process="@this"/>

<p:tab 
                    id="BitacoraEvaluacionPOATab" 
                    title="#{i18n.evaluacionOperativa_bitacoraEvaluacionPOA_tab}">
<p:panelGrid columns="1" styleClass="una-panelgrid una-width-100">
</p:panelGrid>
</p:tab>

</p:tabView>
  • Luego dentro del panel grid agregamos el componente detailNoEditTableHorizontal
<p:panelGrid columns="1" styleClass="una-panelgrid una-width-100">
                        <components:detailNoEditTableHorizontal 
                            id="bitacoraDetailNoEditTableHorizontal"
                            header="#{i18n.evaluacionOperativa_bitacoraEvaluacionPOA_label}"
                            bean="#{bitacoraEvaluacionPOABean}"
                            masterBean="#{evaluacionOperativaBean}"
                            container="evaluacionOperativaTabView:"
                            showNewButton="false"
                            showActions="false" /> 
                    </p:panelGrid>
  • id=»bitacoraDetailNoEditTableHorizontal»: es un id
  • header=»#{i18n.evaluacionOperativa_bitacoraEvaluacionPOA_label}»: es el titulo
  • bean=»#{bitacoraEvaluacionPOABean}»: el bean que va a presentar (el que hicimos anteriormente)
  • masterBean=»#{evaluacionOperativaBean}»: bean principal el que estamos trabajando actualmente
  • container=»evaluacionOperativaTabView:»: es el ID del compmente princpal tabview
  • RESULTADO
<p:tabView id="evaluacionOperativaTabView" activeIndex="#{evaluacionOperativaBean.activeTab}">
                
                <p:ajax event="tabChange" process="@this"/>

<p:tab 
                    id="BitacoraEvaluacionPOATab" 
                    title="#{i18n.evaluacionOperativa_bitacoraEvaluacionPOA_tab}">
                    <p:panelGrid columns="1" styleClass="una-panelgrid una-width-100">
                        <components:detailNoEditTableHorizontal 
                            id="bitacoraDetailNoEditTableHorizontal"
                            header="#{i18n.evaluacionOperativa_bitacoraEvaluacionPOA_label}"
                            bean="#{bitacoraEvaluacionPOABean}"
                            masterBean="#{evaluacionOperativaBean}"
                            container="evaluacionOperativaTabView:"
                            showNewButton="false"
                            showActions="false" /> 
                    </p:panelGrid>
                </p:tab>

            </p:tabView>
  • Y como en esta pagina no se tenia el llamado a initDetails que creamos debemos agregar la linea dentro de preRender
<f:event type="preRenderView" listener="#{evaluacionOperativaBean.initDetails()}"/>
  • RESULATDO
<ui:define name="preRender">
        <f:event type="preRenderView" listener="#{evaluacionOperativaBean.find()}"/>
        <f:event type="preRenderView" listener="#{evaluacionOperativaBean.initProperties()}"/>
        <f:event type="preRenderView" listener="#{evaluacionOperativaBean.initDetails()}"/>
    </ui:define>