Skip to content

4. Tarea 1: Gestión básica de nóminas

4.1. Introducción

Para aplicar lo que hemos tratado anteriormente, proponemos ahora un encargo consistente en el desarrollo de un cliente Android para tabletas, diseñado para simular el cálculo de las nóminas de los empleados de una asociación.

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

Image

  • el servidor [1];
  • debes construir el cliente Android [2].

4.2. La base de datos

4.2.1. Definición

Los datos estáticos necesarios para construir el recibo de salarios se almacenarán en una base de datos a la que nos referiremos en adelante como dbpam. Esta base de datos contiene las siguientes tablas:

EMPLOYEES tabla: contiene información sobre los distintos proveedores de servicios de guardería

Estructura:

ID
clave primaria
VERSION
número de versión - aumenta con cada modificación de la fila
SS
Número de la Seguridad Social del empleado - único
NAME
Apellido del empleado
FIRST NAME
nombre de pila
ADDRESS
su dirección
CITY
su ciudad
ZIP CODE
su ZIP código
INDEMNITY_ID
Clave foránea en el campo [ID] de la tabla [INDEMNITES]

Su contenido podría ser el siguiente:

Image

COTISATIONS tabla: contiene los porcentajes necesarios para calcular las cotizaciones a la seguridad social

Estructura:

ID
clave primaria
VERSION
número de versión - aumenta con cada modificación de la fila
CSGRDS
Porcentaje: Contribución social general + Contribución al reembolso de la deuda social
CSGD
porcentaje: cotización social general deducible
SECU
porcentaje: seguridad social, viudedad, vejez
PENSION
porcentaje: pensión complementaria + seguro de desempleo

Su contenido podría ser el siguiente:

Image

Los tipos de cotización a la Seguridad Social son independientes del trabajador. La tabla anterior sólo tiene una fila.

ALLOWANCES tabla: contiene los elementos utilizados para calcular el salario a pagar.
ID
clave primaria
VERSION
número de versión - aumenta con cada modificación de la fila
INDEX
Índice de tratamiento - único
HOURLY RATE
Precio neto en euros por una hora de guardia
DAILY MAINTENANCE
indemnización diaria en euros por día de asistencia
MEAL PER DAY
Subsidio de comida en euros por día de asistencia
VACATION PAY
Indemnización por vacaciones pagadas. Se trata de un porcentaje aplicado al salario base.

Su contenido podría ser el siguiente:

Image

Tenga en cuenta que los complementos pueden variar de un cuidador a otro. Están vinculadas a un cuidador concreto a través de su categoría salarial. Por ejemplo, la Sra. Marie Jouveinal, que tiene un grado salarial 2 (tabla EMPLOYEES), tiene un salario por hora de 2,1 euros (tabla INDEMNITES).

4.2.2. Generación

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

  

Cree la base de datos [dbpam_hibernate] (este es el nombre de la base de datos utilizada por el servidor web/jSON) y asegúrese de que el archivo raíz (sin contraseña) puede acceder a ella. Puede hacerlo de la siguiente manera:

Inicie MySQL, luego [PhpMyAdmin]:

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

4.2.3. Modelado Java de la base de datos

Los elementos de las tablas [EMPLOYEES], [ALLOWANCES] y [CONTRIBUTIONS] se modelan mediante las siguientes clases:

[ Empleado]


package pam.entities;
 
import java.io.Serializable;
 
public class Employee implements Serializable {
 
  private static final long serialVersionUID = 1L;
  private Long id;
  private int version;
  private String SS;
  private String lastName;
  private String firstName;
  private String address;
  private String city;
  private String zipCode;
  private int allowanceID;
  private Indemnity indemnity;
 
  public Employee() {
  }
 
  public Employee(String lastName, String firstName, String middleName, String address, String city, String zipCode, Indemnity indemnity) {
    ...
  }
  // getters and setters
....
}
  • líneas 8-15: estos campos corresponden a las columnas de la tabla [EMPLOYEES];
  • línea 16: el campo [indemniteId] corresponde a la columna [INDEMNITE_ID], que es la clave externa de la tabla [EMPLOYEES];
  • línea 17: indemnización del empleado. Este campo no siempre se rellena:
    • no se rellena al solicitar el URL [/empleados],
    • es al solicitar el URL [/salario];

[ 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 index;
    private double baseTime;
    private double room, daily cleaning;
    private double meal per day;
    private double severancePay;
 
    public Indemnity() {
    }
 
    public Allowance(int index, double baseHour, double dailyMaintenance, double dailyMeal, double CPAllowance) {
        ...
    }
 
    // getters and setters
   ....
}
  • líneas 8-14: los campos corresponden a las columnas de la tabla [INDEMNITES];

[ Contribución]


package pam.entities;
 
import java.io.Serializable;
 
public class Contribution implements Serializable {
 
    private static final long serialVersionUID = 1L;
    private Long id;
    private int version;
    private double csgrds;
    private double csgd;
    private double socialsecurity;
    private double pension;
 
    public Contribution() {
    }
 
    public Contribution(double csgrds, double csgd, double secu, double retirement) {
        ...
    }
   // getters and setters
   ...
}
  • líneas 8-13: los campos corresponden a las columnas de la tabla [COTISATIONS];

4.3. Servidor web / JSON instalación

4.3.1. Instalación

Se proporciona el binario Java para el servidor web/jSON:

 

Para iniciar el servidor web/JSON, proceda del siguiente modo:

  • Inicie el MySQL DBMS;
  • asegúrese de que la base de datos [dbpam_hibernate] existe;
  • Abra una ventana de símbolo del sistema;
  • Vaya a la carpeta jar;
  • teclea el comando:
java -jar pam-server-01-all-1.0.jar

Esto supone que el ejecutable [java.exe] está en el PATH de su máquina. Si no es así, escriba la ruta completa a [java.exe], por ejemplo:

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

Se muestran 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 "{[/salary/{SS}/{ht}/{jt}],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public pam.restapi.PayrollResponse pam.restapi.PamController.getPayroll(java.lang.String, double, int)
2014-10-22 16:45:33.805  INFO 1868 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/employees],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public pam.restapi.EmployeesResponse pam.restapi.PamController.getEmployees()
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/**] onto handler of type [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 of type [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: el URL [/salary/{SS}/{ht}/{jt}] está resuelto;
  • línea 17: el URL [/empleados];

4.3.2. Servicio web/JSON URLs

El servicio web / JSON está implementado por Spring MVC y expone dos URLs:


@RequestMapping(value = "/employees", method = RequestMethod.GET, content-type = "application/json; charset=UTF-8")
public EmployeesResponse getEmployees() {
...
@RequestMapping(value = "/salary/{SS}/{ht}/{jt}", method = RequestMethod.GET, response-type = "application/json; charset=UTF-8")
public PaystubResponse getPaystub(@PathVariable("SS") String SS, @PathVariable("ht") double ht, @PathVariable("jt") int jt) {

El servicio web acepta los dos URLs siguientes:

  • línea 1: /empleados: para recuperar la lista de empleados;
  • Línea 4: /salario/SS/ht/jt: para recuperar el recibo de sueldo del empleado #[SS] que trabajó [ht] horas durante [jt] días;

Aquí tienes algunas capturas de pantalla que lo ilustran.

Preguntamos a los empleados:

Image

Hacemos una copia de seguridad de la base de datos, reiniciamos el servidor y consultamos a los empleados:

Image

Consultamos un salario:

Image

Solicitamos el salario de una persona inexistente:

Image

4.3.3. Las respuestas JSON del servicio web/JSON

  

El servicio web/jSON URLs devuelve respuestas de tipo [Response<T>]:


package client.android.dao.service;
 
import java.util.List;
 
public class Response<T> {
 
    // ----------------- properties
    // operation status
    private int status;
    // any status messages
    private List<String> messages;
    // response body
    private T body;
 
    // constructors
    public Response() {
 
    }
 
    public Response(int status, List<String> messages, T body) {
        this.status = status;
        this.messages = messages;
        this.body = body;
    }

    // getters and setters
...
}
  • El URL [/empleados] devuelve un Respuesta<Lista<Empleado>>;
  • El URL [/salario] devuelve un Respuesta<PayStub> tipo;

La clase [PayrollSheet] es la siguiente:


package pam.entities;
 
import java.io.Serializable;
 
public class Payroll implements Serializable {
 
    private static final long serialVersionUID = 1L;
    // private fields
    private Employee employee;
    private Contribution contribution;
    private PayrollElements payrollElements;
 
    // constructors
    public Payroll() {
    }
 
    public Payroll(Employee employee, Contribution contribution, SalaryElements salaryElements) {
        ...
    }
 
    // getters and setters
   ...
}
  • línea 9: la clase [Empleado] se introdujo en la sección 4.2.3;
  • línea 10: la clase [Contribution] se introdujo en la sección 4.2.3;

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


package pam.entities;
 
import java.io.Serializable;
 
public class SalaryElements implements Serializable {
 
    private static final long serialVersionUID = 1L;
    // private fields
    private double baseSalary;
    private double socialSecurityContributions;
    private double maintenanceAllowance;
    private double mealAllowance;
    private double netSalary;
 
    // constructors
    public ElementsSalary() {
 
    }
 
    public ElementsSalary(double baseSalary, double socialContributions, double maintenanceAllowance, double mealAllowance, double netSalary) {
        ...
    }
 
    // getters and setters
    ...
}

4.4. Pruebas de clientes Android

A continuación se proporciona el binario ejecutable para el cliente Android terminado:

  

Utilice el ratón para arrastrar el archivo [pam-client.apk] anterior a un emulador de tableta [GenyMotion]. A continuación, se guardará y se ejecutará. Inicia también el servidor web/JSON si aún no lo has hecho. El propósito del cliente Android es recuperar la información devuelta por el servidor web/JSON y formatearla. Las diferentes vistas del cliente Android son las siguientes:

En primer lugar, debe conectarse al servicio web / JSON:

Image

  • en [1], introduzca el URL del servicio web/JSON. Con el emulador, introduzca una de las direcciones PC del IP (pero no 127.0.0.1). Con una tableta, introduce la dirección Wi-Fi del equipo servidor web/JSON y desactiva el cortafuegos del servidor si lo tiene, ya que puede bloquear las llamadas entrantes;
  • En [2], inicia sesión;

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

Image

  • En [3], seleccione un empleado;
  • En [4], introduzca un número de horas;
  • En [5], introduzca el número de días;
  • En [6], ejecuta la simulación;

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

Image

  • en [7], los resultados de la simulación;
  • en [8], guárdelo;

Image

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

Image

  • en [11], no hay más simulaciones;
  • En [12], se vuelve al formulario de simulación;

Image

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

Image

  • en [15], se vuelve al formulario de acceso inicial.

4.5. Trabajo por hacer

Te proporcionamos el esqueleto de cliente Android presentado anteriormente. Se construyó a partir del proyecto [client-android-skel] descrito en la Sección 2.

  

El proyecto es ejecutable y ya contiene las vistas necesarias. Simplemente hay que añadir código para que la aplicación haga lo que se supone que debe hacer. El procedimiento es el siguiente:

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