Skip to content

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

4.1. Introduction

Para poner en práctica lo visto anteriormente, proponemos ahora un ejercicio que consiste en desarrollar una aplicación cliente para tabletas 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 de 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 cuenta con 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 fila
SS
número de seguro 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: recopila 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: contribución social generalizada + contribución al pago 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

Las tasas de las cotizaciones sociales son independientes del empleado. La tabla anterior solo tiene una fila.

Tabla INDEMNITES: reúne los elementos que permiten 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 una hora de guardia
ENTRETIENJOUR
indemnización de manutención en euros por día de custodia
REPASJOUR
indemnización por comida en euros por día de cuidado
INDEMNITESCP
Indemnización por vacaciones pagadas. Es 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 específica 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 luego [PhpMyAdmin]:

 
  • [1-2]: importa el script [dbpam_hibernate.sql] y luego ejecútalo;

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 corresponden a 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á completado:
    • no lo está cuando se solicita el URL [/employes],
    • sí lo está cuando se solicita el URL [/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 corresponden a 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 corresponden a 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 SGBD MySQL;
  • asegúrese de que existan BD y [dbpam_hibernate];
  • abre una ventana DOS;
  • navega hasta la carpeta del archivo .jar;
  • escriba 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 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 manejador 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 acepta las siguientes dos URL:

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

Aquí hay algunas capturas de pantalla que lo muestran.

Se solicitan los empleados:

Image

Se desconecta 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<Empleado>>;
  • 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 presentó en el párrafo 4.2.3;
  • línea 10: la clase [Cotisation] se presentó en el párrafo 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 de Android

Se te proporciona el binario ejecutable del cliente de Android ya terminado:

  

Con el ratón, arrastre el archivo binario [pam-client.apk] que aparece arriba y suéltelo en un emulador de tableta [GenyMotion]. Se guardará y luego se ejecutará. Inicie también el servidor web / jSON si aún no lo ha 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 proporciona el URL del servicio web / jSON. Con el emulador, ingresa una de las direcciones IP o PC (pero no 127.0.0.1). Con una tableta, ingresa la dirección Wi-Fi de la máquina del servidor web / jSON y desactiva el firewall del servidor, si lo tiene, ya que podría bloquear las llamadas entrantes;
  • en [2], nos conectamos;

A continuación, se llega 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 que se obtiene 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 regresa al formulario de simulación;

Image

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

Image

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

4.5. Tarea a realizar

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

  

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

  • ejecuta la versión completa para comprender el trabajo que hay que hacer;
  • ejecute la versión simplificada y estudie su código. Este respeta los métodos de diseño utilizados en las páginas anteriores;
  • agregue el código que falta;