Backend (V) September 06, 2018

En esta ultima parte de la parte servidor hablare de como se crean los objetos que en el anterior articulo se devolvían.

Estos objetos eran del tipo _VentasAnoBean _y un ArrayList de _VentasSemanaBean. _Para conseguirlos se llamaban a sendas funciones en  la clase YagesBussines, que eran las que construían esos objetos.

Empezare describiendo la clase

@Component
public class YagesBussines {  
    @Autowired
    CalendarioRepositorioService calendarioRepositorio;

    @Autowired
    HistVentasRepository histVentasRepository;

    @Autowired
    DataSource dataSource;        
  
     @Autowired
    private JdbcOperations jdbc;

    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource);
    }
.....

Lo primero, marcamos nuestra clase como @Component para que Spring la cree y este disponible para inyectarla en otras clases. De otra manera nuestra clase YagesController  no podría usarla.

Ahora inyectamos los diferentes objetos que vamos a usar, incluyendo el objeto jdbc que es de tipo JdbcOperations . Para que Spring pueda crear ese objeto debemos tener la función jdbcTemplate, que, usando la variable  dataSource . devolverá un objeto tipo JdbcTemplate el cual implementa el interface  JdbcOperations para usarlo en la variable jdbc, ya que la función esta marcarla con la anotación @Bean y eso hará que Spring sepa de donde sacar un objeto tipo JdbcOperations.

public VentasAnoBean getVentasAno(int ano) {
        List<VentasMesBean> cal = getKilosPorMes(ano);
        VentasAnoBean ventasAno = new VentasAnoBean();
        cal.forEach((v) -> {
            ventasAno.addMes(v);
        });
       
        if (ventasAno.getKilosVentaAct() == 0) {
            throw new VentasNotFoundException(ano);
        }
        return ventasAno;
    }
    

La función getVentasAno, simplemente llama a la función getKilosPorMes, la cual devuelve una lista de objectos tipo VentasMesBean (List),  para después ir añadiéndolo al objeto tipo VentasAno.

Obsérvese que la función comprueba si el acumulado de kilos de venta,  a través de la función getKilosVentaAct(), es igual a cero. Si es cero, lanza una excepción del tipo VentasNotFoundException

Esa excepción se define en el paquete yages.yagesserver.bussines.

@ResponseStatus(HttpStatus.NOT_FOUND)
class VentasNotFoundException extends RuntimeException {
    private static final long serialVersionUID = 1L;

    public VentasNotFoundException(int ano) {
        this(ano, 0);
    }
    public VentasNotFoundException(int ano, int mes) {
        super("No encontradas ventas para año '" + ano + "'"+ (mes>0?" y Mes: "+mes:""));
    }
    public VentasNotFoundException(String msgError,int ano, int mes) {
        super("Error al buscar ventas para año '" + ano + "'"+ (mes>0?" y Mes: "+mes:"")+" \n Error: "+msgError);
    }
}

Lo destacable de esta clase es la anotación @ResponseStatus, la cual hará que cuando se lance, a través de una sentencia _throw, _sea capturada por Spring de tal manera que la respuesta a la peticion HTTP sea lo indicado, en este caso un HttpStatus.NOT_FOUND (el tipico 404: Página no encontrada). De esta manera si la consulta no devuelve ningún dato en vez de devolver un null, devolveremos un mensaje de error para que el cliente pueda tratarlo más fácilmente.

En otras palabras, cuando se lance una excepción del tipo VentasNotFoundException el cliente recibirá un error del tipo 404 (NOT FOUND).

La función getKilosPorMes, realiza una serie de consultas a través de jdbc. 

public List<VentasMesBean> getKilosPorMes(int ano) {                  
          List<VentasMesBean> cal =
                jdbc.query(
                "SELECT c.cal_ano,c.cal_mes , sum(hve_kilven) as kilosVenta,sum(hve_impven) as impVenta,sum(hve_impgan) as impGanancia "
                    + "FROM Calendario c,  histventas h "
                    + " where  c.cal_ano =  ?" 
                    + " and h.hve_fecini between c.cal_fecini and c.cal_fecfin "
                    + " and h.hve_fecfin between c.cal_fecini and c.cal_fecfin "
                    + " group by cal_ano,cal_mes "
                    + " order by c.cal_mes",  new Object[] { ano },
                (rs, rowNum) -> new VentasMesBean(rs.getInt("cal_ano"),rs.getInt("cal_mes"),rs.getDouble("kilosVenta"),rs.getDouble("impVenta"),rs.getDouble("impGanancia"))
                        );
             List<VentasMesBean> calAnt =
                jdbc.query(
                "SELECT c.cal_ano,c.cal_mes , sum(hve_kilven) as kilosVenta,sum(hve_impven) as impVenta,sum(hve_impgan) as impGanancia "
                    + "FROM Calendario c,  histventas h "
                    + " where  c.cal_ano =  ?" 
                   + " and h.hve_fecini between c.cal_fecini and c.cal_fecfin "
                    + " and h.hve_fecfin between c.cal_fecini and c.cal_fecfin "
                    + " group by cal_ano,cal_mes "
                    + " order by c.cal_mes",  new Object[] { ano - 1},
                  (rs, rowNum) -> new VentasMesBean(rs.getInt("cal_ano"),rs.getInt("cal_mes"),rs.getDouble("kilosVenta"),rs.getDouble("impVenta"),rs.getDouble("impGanancia"))
                        );
            
            
             cal.forEach((v) -> {
                 for (VentasMesBean vAnt : calAnt) {
                     if (v.getMes() == vAnt.getMes()) {
                         v.setKilosVentaAnt(vAnt.getKilosVentaAct());
                         v.setImpVentaAnt(vAnt.getImpVentaAct());
                         v.setGananAnt(vAnt.getGananAct());
                     }
                 }
            });
            return cal; 
    }

Obsérvese el uso intensivo de sentencias lamba.  Os recuerdo que tenéis un par de entradas sobre como este tema aquí y aquí

Esta función consulta primero todas las ventas del año mandado, metiéndolas en un objeto List, después hace la misma consulta sobre el año anterior al mandado, metiendo el resultado en otro objeto List. Después recorre la primera lista a, con la función forEach, y si existe el mismo mes en el segundo objeto, establece los parámetros del año anterior en el primer objeto VentasMesBean. Por ultimo devuelve la lista de objetos (List)

Esta función es la llamada cuando queremos buscar los datos de venta de un año y semana. Ofreciendo, semana por semana, los datos del periodo solicitado.

public ArrayList<VentasSemanaBean> getDatosSemana(int mes, int ano) {
        Calendario calAnterior = null;
        
        Optional<Calendario> calOpc = calendarioRepositorio.getCalendario(new CalendarioKey(mes, ano - 1));
        if (calOpc.isPresent()) {
            calAnterior = calOpc.get();
            try {
                ajustaFechas(calAnterior, ano - 1, mes);
            } catch (ParseException e) {
                throw new VentasNotFoundException(e.getMessage(), ano, mes);
            }
        }
......
    }

No es mi idea explicar paso a paso la lógica de esta función solo incidir en el uso de JPA, usando la clase CalendarioRepositorioService, que se limita a usar la clase generada automagicamente por Spring, la cual   implementara el interface CalendarioRepository.

El código por si estáis interesados en curiosear como se buscan los datos lo tenéis en mi pagina de GitHub

Y con esto termino las entradas, explicando la parte servidor de esta aplicación.

Espero que haya sido de utilidad y no os perdáis las siguientes sobre como hacer la parte cliente con Angular.

¡¡ Hasta pronto !!