Skip to content

4. TP 1: Gestión básica de una nómina

4.1. Introduction

Para poner en práctica lo visto anteriormente, proponemos ahora un trabajo que consiste en escribir una aplicación cliente para tableta Android que permita simular los cálculos de las nóminas de los empleados de una asociación.

La aplicación tendrá una arquitectura cliente/servidor:

Image

  • El servidor [1] ya está disponible;
  • hay que desarrollar el cliente Android [2].

4.2. La base de datos

4.2.1. Definición

Los datos estáticos necesarios para generar la nómina se almacenarán en una base de datos a la que, en adelante, nos referiremos c e dbpam. Esta base de datos contiene las siguientes tablas:

Tabla EMPLOYES: recopila información sobre las diferentes cuidadoras infantiles

Estructura:

ID
clave primaria
VERSION
N.º de versión: aumenta con cada modificación de la línea
SS
Número de la Seguridad Social del empleado: único
NOM
nombre del empleado
PRENOM
su nombre
ADRESSE
su dirección
VILLE
su ciudad
CODEPOSTAL
su código postal
INDEMNITE_ID
clave externa en el campo [ID] de la tabla [INDEMNITES]

Su contenido podría ser el siguiente:

Image

Tabla COTISATIONS: recoge los porcentajes necesarios para el cálculo de las cotizaciones sociales

Estructura:

ID
clave primaria
VERSION
N.º de versión: aumenta con cada modificación de la línea
CSGRDS
porcentaje: cotización social generalizada + contribución al reembolso de la deuda social
CSGD
porcentaje: contribución social generalizada deducible
SECU
porcentaje: Seguridad Social, viudedad, vejez
RETRAITE
porcentaje: pensión complementaria + seguro de desempleo

Su contenido podría ser el siguiente:

Image

Los tipos de las cotizaciones sociales son independientes del empleado. La tabla anterior solo tiene una línea.

Tabla INDEMNITES: recoge los datos necesarios para calcular el salario a pagar.
ID
clave primaria
VERSION
N.º de versión: aumenta con cada modificación de la línea
INDICE
Índice de procesamiento: único
BASEHEURE
Precio neto en euros por hora de guardia
ENTRETIENJOUR
Indemnización por manutención en euros por día de custodia
REPASJOUR
Indemnización por comida en euros por día de servicio
INDEMNITESCP
Indemnización por vacaciones pagadas. Se trata de un porcentaje que se aplica al salario base.

Su contenido podría ser el siguiente:

Image

Cabe señalar que las indemnizaciones pueden variar de una cuidadora a otra. De hecho, están vinculadas a una cuidadora concreta a través de su índice salarial. Así, la Sra. Marie Jouveinal, que tiene un índice salarial de 2 (tabla EMPLOYES), tiene un salario por hora de 2,1 euros (tabla INDEMNITES).

4.2.2. Generación

Se proporciona el script [dbpam_hibernate.sql] para la generación de la base de datos:

  

Crea la base de datos [dbpam_hibernate] (este es el nombre de la base de datos BD que utiliza el servidor web / jSON) y asegúrese de que el usuario root, sin contraseña, pueda acceder a ella. Puede hacerlo de la siguiente manera:

Inicie MySQL y, a continuación, [PhpMyAdmin]:

 
  • [1-2]: importe el script [dbpam_hibernate.sql] y, a continuación, ejecútelo;

4.2.3. Modelado Java de la base de datos

Los elementos de las tablas [EMPLOYES], [INDEMNITES] y [COTISATIONS] se modelan mediante las siguientes clases:

[Employe]


package pam.entities;

import java.io.Serializable;

public class Employe implements Serializable {

  private static final long serialVersionUID = 1L;
  private Long id;
  private int version;
  private String SS;
  private String nom;
  private String prenom;
  private String adresse;
  private String ville;
  private String codePostal;
  private int idIndemnite;
  private Indemnite indemnite;

  public Employe() {
  }

  public Employe(String SS, String nom, String prenom, String adresse, String ville, String codePostal, Indemnite indemnite) {
    ...
  }
   // getters y setters
....
}
  • líneas 8-15: estos campos se corresponden con las columnas de la tabla [EMPLOYES];
  • línea 16: el campo [indemniteId] corresponde a la columna [INDEMNITE_ID], que es la clave externa de la tabla [EMPLOYES];
  • línea 17: la indemnización del empleado. Este campo no siempre está rellenado:
    • no lo está cuando se solicita el URL [/employes],
    • sí lo está cuando se solicitan los campos URL y [/salaire];

[Indemnite]


package pam.entities;

import java.io.Serializable;

public class Indemnite implements Serializable {

    private static final long serialVersionUID = 1L;
    private Long id;
    private int version;
    private int indice;
    private double baseHeure;
    private double entretienJour;
    private double repasJour;
    private double indemnitesCp;

    public Indemnite() {
    }

    public Indemnite(int indice, double baseHeure, double entretienJour, double repasJour, double indemnitesCP) {
        ...
    }

     // getters y setters
   ....
}
  • líneas 8-14: los campos se corresponden con las columnas de la tabla [INDEMNITES];

[Cotisation]


package pam.entities;

import java.io.Serializable;

public class Cotisation implements Serializable {

    private static final long serialVersionUID = 1L;
    private Long id;
    private int version;
    private double csgrds;
    private double csgd;
    private double secu;
    private double retraite;

    public Cotisation() {
    }

    public Cotisation(double csgrds, double csgd, double secu, double retraite) {
        ...
    }
    // getters y setters
   ...
}
  • líneas 8-13: los campos se corresponden con las columnas de la tabla [COTISATIONS];

4.3. Instalación del servidor web / jSON

4.3.1. Instalación

Se le proporciona el binario Java del servidor web / jSON:

 

Para iniciar el servidor web / jSON, siga estos pasos:

  • ejecute el archivo SGBD MySQL;
  • asegúrese de que existen los archivos BD y [dbpam_hibernate];
  • abre una ventana DOS;
  • ve a la carpeta del archivo .jar;
  • escribe el comando:
java -jar pam-server-01-all-1.0.jar

Esto supone que el binario [java.exe] se encuentra en el directorio PATH de tu equipo. Si no es así, escribe la ruta completa de [java.exe], por ejemplo:

D:\Programs\devjava\java\jdk1.8\bin\java -jar pam-server-01-all-1.0.jar

Aparecerán los siguientes registros:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.1.1.RELEASE)

2014-10-22 16:45:23.347  INFO 1868 --- [           main] pam.boot.BootWeb                         : Starting BootWeb on Gportpers3 with PID 1868 (D:\Temp\14-10-22\pam\server-pam.jar started by ST in D:\Temp\14-10-22\pam)
2014-10-22 16:45:23.414  INFO 1868 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@689ab9e2: startup date [Wed Oct 22 16:45:23 CEST 2014]; root of context hierarchy
...
...
2014-10-22 16:45:31.147  INFO 1868 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
2014-10-22 16:45:31.484  INFO 1868 --- [           main] o.h.h.i.ast.ASTQueryTranslatorFactory    : HHH000397: Using ASTQueryTranslatorFactory
2014-10-22 16:45:33.564  INFO 1868 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-10-22 16:45:33.804  INFO 1868 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/salaire/{SS}/{ht}/{jt}],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public pam.restapi.FeuilleSalaireResponse pam.restapi.PamController.getFeuilleSalaire(java.lang.String,double,int)
2014-10-22 16:45:33.805  INFO 1868 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/employes],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public pam.restapi.EmployesResponse pam.restapi.PamController.getEmployes()
2014-10-22 16:45:33.807  INFO 1868 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2014-10-22 16:45:33.807  INFO 1868 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[text/html],custom=[]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
2014-10-22 16:45:33.839  INFO 1868 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] al controlador de tipo [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-10-22 16:45:33.839  INFO 1868 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler de tipo [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-10-22 16:45:34.384  INFO 1868 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2014-10-22 16:45:34.535  INFO 1868 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080/http
2014-10-22 16:45:34.538  INFO 1868 --- [           main] pam.boot.BootWeb                         : Started BootWeb in 11.916 seconds (JVM running for 12.725)
2014-10-22 16:45:39.329  INFO 1868 --- [       Thread-2] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@689ab9e2: startup date [Wed Oct 22 16:45:23 CEST 2014]; root of context hierarchy
2014-10-22 16:45:39.331  INFO 1868 --- [       Thread-2] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown
2014-10-22 16:45:39.333  INFO 1868 --- [       Thread-2] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  • línea 16: se detecta el archivo URL [/salaire/{SS}/{ht}/{jt}];
  • línea 17: se detecta el URL [/employes];

4.3.2. Los URL del servicio web/jSON

El servicio web / jSON está implementado por Spring MVC y expone dos URL:


@RequestMapping(value = "/employes", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
public EmployesResponse getEmployes() {
...
@RequestMapping(value = "/salaire/{SS}/{ht}/{jt}", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
public FeuilleSalaireResponse getFeuilleSalaire(@PathVariable("SS") String SS, @PathVariable("ht") double ht, @PathVariable("jt") int jt) {

El servicio web admite las dos URL siguientes:

  • línea 1: /empleados: para obtener la lista de empleados;
  • línea 4: /salaire/SS/ht/jt: para obtener la nómina del empleado con el n.º [SS] que ha trabajado [ht] horas durante [jt] días;

Aquí hay unas capturas de pantalla que lo muestran.

Se solicitan los empleados:

Image

Se apaga la base de datos, se reinicia el servidor y se consultan los empleados:

Image

Se consulta un salario:

Image

Se solicita el salario de una persona que no existe:

Image

4.3.3. Las respuestas jSON del servicio web/jSON

  

Los URL del servicio web / jSON envían respuestas del tipo [Response<T>]:


package client.android.dao.service;

import java.util.List;

public class Response<T> {

    // ----------------- propiedades
    // estado de la operación
    private int status;
    // posibles mensajes de estado
    private List<String> messages;
    // el cuerpo de la respuesta
    private T body;

    // constructores
    public Response() {

    }

    public Response(int status, List<String> messages, T body) {
        this.status = status;
        this.messages = messages;
        this.body = body;
    }

    // getters y setters
...
}
  • el URL [/employes] devuelve un tipo Response<List<Employe>>;
  • URL y [/salaire] devuelven un tipo Response<FeuilleSalaire>;

La clase [FeuilleSalaire] es la siguiente:


package pam.entities;

import java.io.Serializable;

public class FeuilleSalaire implements Serializable {

    private static final long serialVersionUID = 1L;
    // campos privados
    private Employe employe;
    private Cotisation cotisation;
    private ElementsSalaire elementsSalaire;

    // constructores
    public FeuilleSalaire() {
    }

    public FeuilleSalaire(Employe employe, Cotisation cotisation, ElementsSalaire elementsSalaire) {
        ...
    }

    // getters y setters
   ...
}
  • línea 9: la clase [Employe] se ha presentado en el apartado 4.2.3;
  • línea 10: la clase [Cotisation] se ha presentado en el apartado 4.2.3;

La clase [ElementsSalaire] (línea 11) es la siguiente:


package pam.entities;

import java.io.Serializable;

public class ElementsSalaire implements Serializable {

    private static final long serialVersionUID = 1L;
    // campos privados
    private double salaireBase;
    private double cotisationsSociales;
    private double indemnitesEntretien;
    private double indemnitesRepas;
    private double salaireNet;

    // constructores
    public ElementsSalaire() {

    }

    public ElementsSalaire(double salaireBase, double cotisationsSociales, double indemnitesEntretien, double indemnitesRepas, double salaireNet) {
        ...
    }

    // getters y setters
    ...
}

4.4. Pruebas del cliente Android

A continuación se muestra el binario ejecutable del cliente de Android ya compilado:

  

Arrastra con el ratón el archivo binario [pam-client.apk] que aparece arriba hasta un emulador de tableta [GenyMotion]. Se guardará y, a continuación, se ejecutará. Inicia también el servidor web / jSON si aún no lo has hecho. El cliente de Android tiene como objetivo recuperar la información enviada por el servidor web / jSON y darle formato. Las diferentes vistas del cliente de Android son las siguientes:

En primer lugar, hay que conectarse al servicio web / jSON:

Image

  • en [1], se introduce el URL del servicio web / jSON. Con el emulador, introduce una de las direcciones IP o PC (pero no 127.0.0.1). Si utilizas una tableta, introduce la dirección Wi-Fi del equipo del servidor web / jSON y desactiva el cortafuegos del servidor, si lo tiene, ya que podría bloquear las conexiones entrantes;
  • En [2], nos conectamos;

A continuación, se accede a la página de simulación:

Image

  • En [3], se selecciona un empleado;
  • en [4], se indica un número de horas;
  • En [5], se indica un número de días;
  • en [6], se solicita la simulación;

La página de simulación obtenida es la siguiente:

Image

  • en [7], la simulación obtenida;
  • en [8], se guarda;

Image

  • en [9], la lista de simulaciones;
  • en [10], se elimina una simulación;

Image

  • en [11], ya no hay simulaciones;
  • en [12], se vuelve al formulario de simulación;

Image

  • en [13], se vuelve a mostrar el formulario;
  • en [14], se vuelve a la página de configuración;

Image

  • en [15], volvemos al formulario de inicio de sesión inicial.

4.5. Tarea a realizar

Se le proporciona el esqueleto del cliente para Android presentado anteriormente. Se ha creado a partir del proyecto [client-android-skel] descrito en el apartado 2.

  

El proyecto es ejecutable y ya cuenta con las vistas necesarias. Solo hay que añadir código para que la aplicación haga lo que tiene que hacer. El procedimiento a seguir es el siguiente:

  • ejecuta la versión completa para familiarizarte con el trabajo que hay que realizar;
  • ejecuta la versión simplificada y estudia su código. Este respeta los métodos de diseño utilizados en las páginas anteriores;
  • añade el código que falta;