Skip to content

4. TP 1: Gestione di base di una busta paga

4.1. Introduction

Per mettere in pratica quanto visto in precedenza, proponiamo ora un esercizio che consiste nello scrivere un client Android per tablet, che consenta di simulare i calcoli delle buste paga dei dipendenti di un'associazione.

L'applicazione avrà un'architettura client/server:

Image

  • il server [1] è già disponibile;
  • è necessario sviluppare il client Android [2].

4.2. Il database

4.2.1. Definizione

I dati statici necessari per generare la busta paga saranno inseriti in un database che d'ora in poi chiameremo dbpam. Questo database contiene le seguenti tabelle:

Tabella EMPLOYES: raccoglie informazioni sulle diverse assistenti materne

Struttura:

ID
chiave primaria
VERSION
numero di versione – aumenta ad ogni modifica della riga
SS
numero di previdenza sociale del dipendente – univoco
NOM
nome del dipendente
PRENOM
il suo nome
ADRESSE
il suo indirizzo
VILLE
la sua città
CODEPOSTAL
il suo codice postale
INDEMNITE_ID
chiave esterna sul campo [ID] della tabella [INDEMNITES]

Il contenuto potrebbe essere il seguente:

Image

Tabella COTISATIONS: raccoglie le percentuali necessarie per il calcolo dei contributi sociali

Struttura:

ID
chiave primaria
VERSION
numero di versione – aumenta ad ogni modifica della riga
CSGRDS
percentuale: contributo sociale generalizzato + contributo al rimborso del debito sociale
CSGD
percentuale: contributo sociale generalizzato deducibile
SECU
percentuale: previdenza sociale, vedovanza, vecchiaia
RETRAITE
percentuale: pensione integrativa + assicurazione contro la disoccupazione

Il contenuto potrebbe essere il seguente:

Image

Le aliquote dei contributi sociali sono indipendenti dal dipendente. La tabella precedente contiene una sola riga.

Tabella INDEMNITES: raccoglie gli elementi necessari per il calcolo della retribuzione da corrispondere.
ID
chiave primaria
VERSION
numero di versione – aumenta ad ogni modifica della riga
INDICE
indice di elaborazione – univoco
BASEHEURE
prezzo netto in euro per un'ora di guardia
ENTRETIENJOUR
indennità di vitto e alloggio in euro per giorno di servizio
REPASJOUR
indennità pasto in euro per giorno di assistenza
INDEMNITESCP
indennità per ferie retribuite. Si tratta di una percentuale da applicare allo stipendio base.

Il contenuto potrebbe essere il seguente:

Image

Si noti che le indennità possono variare da un'assistente all'infanzia all'altra. Esse sono infatti associate a una specifica assistente all'infanzia tramite il suo indice di retribuzione. Pertanto, la signora Marie Jouveinal, che ha un indice di retribuzione pari a 2 (tabella EMPLOYES), ha una retribuzione oraria di 2,1 euro (tabella INDEMNITES).

4.2.2. Generazione

Viene fornito lo script [dbpam_hibernate.sql] per la generazione del database:

  

Creare il database [dbpam_hibernate] (è il nome del BD utilizzato dal server web / jSON) e assicurarsi che l'account root senza password possa accedervi. È possibile procedere come segue:

Avviare MySQL e poi [PhpMyAdmin]:

 
  • [1-2]: importare lo script [dbpam_hibernate.sql], quindi eseguirlo;

4.2.3. Modellazione Java del database

Gli elementi delle tabelle [EMPLOYES], [INDEMNITES] e [COTISATIONS] sono modellati dalle seguenti classi:

[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) {
    ...
  }
   // getter e setter
....
}
  • righe 8-15: questi campi corrispondono alle colonne della tabella [EMPLOYES];
  • riga 16: il campo [indemniteId] corrisponde alla colonna [INDEMNITE_ID], che è la chiave esterna della tabella [EMPLOYES];
  • riga 17: l’indennità del dipendente. Questo campo non è sempre compilato:
    • non lo è quando si richiede l'URL [/employes],
    • è compilato quando si richiede l'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) {
        ...
    }

     // getter e setter
   ....
}
  • righe 8-14: i campi corrispondono alle colonne della tabella [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) {
        ...
    }
    // getter e setter
   ...
}
  • righe 8-13: i campi corrispondono alle colonne della tabella [COTISATIONS];

4.3. Installazione del server web / jSON

4.3.1. Installazione

Il file binario Java del server web / jSON viene fornito:

 

Per avviare il server web / jSON, procedere come segue:

  • avviare il SGBD MySQL;
  • assicurarsi che BD e [dbpam_hibernate] esistano;
  • aprire una finestra DOS;
  • accedete alla cartella del file jar;
  • digitare il comando:
java -jar pam-server-01-all-1.0.jar

Ciò presuppone che il file binario [java.exe] si trovi nella cartella PATH del proprio computer. In caso contrario, digitare il percorso completo di [java.exe], ad esempio:

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

Vengono visualizzati i log:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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/**] su handler di 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 gestore di 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'
  • riga 16: viene individuato il file URL [/salaire/{SS}/{ht}/{jt}];
  • riga 17: viene individuato URL [/employes];

4.3.2. I URL del servizio web/jSON

Il servizio web / jSON è implementato da Spring MVC ed espone due 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) {

Il servizio web accetta le seguenti due URL:

  • riga 1: /employes: per ottenere l'elenco dei dipendenti;
  • riga 4: /stipendio/SS/ht/jt: per ottenere la busta paga del dipendente n. [SS] che ha lavorato [ht] ore per [jt] giorni;

Ecco alcune schermate che lo dimostrano.

Si richiedono i dipendenti:

Image

Si esegue il cut-off del database, si riavvia il server e si richiamano i dipendenti:

Image

Si richiede uno stipendio:

Image

Si richiede lo stipendio di una persona inesistente:

Image

4.3.3. Le risposte jSON del servizio web/jSON

  

I URL del servizio web / jSON inviano risposte del tipo [Response<T>]:


package client.android.dao.service;

import java.util.List;

public class Response<T> {

    // ----------------- proprietà
    // stato dell'operazione
    private int status;
    // eventuali messaggi di stato
    private List<String> messages;
    // il corpo della risposta
    private T body;

    // costruttori
    public Response() {

    }

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

    // getter e setter
...
}
  • URL e [/employes] restituiscono un tipo Response<List<Employe>>;
  • URL e [/salaire] restituiscono un tipo Response<FeuilleSalaire>;

La classe [FeuilleSalaire] è la seguente:


package pam.entities;

import java.io.Serializable;

public class FeuilleSalaire implements Serializable {

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

    // costruttori
    public FeuilleSalaire() {
    }

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

    // getter e setter
   ...
}
  • riga 9: la classe [Employe] è stata presentata al paragrafo 4.2.3;
  • riga 10: la classe [Cotisation] è stata presentata nel paragrafo 4.2.3;

La classe [ElementsSalaire] (riga 11) è la seguente:


package pam.entities;

import java.io.Serializable;

public class ElementsSalaire implements Serializable {

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

    // costruttori
    public ElementsSalaire() {

    }

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

    // getter e setter
    ...
}

4.4. Test del client Android

Ecco il file binario eseguibile del client Android completato:

  

Con il mouse, trascinate il file binario [pam-client.apk] sopra indicato su un emulatore di tablet [GenyMotion]. Verrà quindi salvato ed eseguito. Avviate anche il server web / jSON, se non l'avete già fatto. Il client Android ha lo scopo di recuperare le informazioni restituite dal server web / jSON e di formattarle. Le diverse schermate del client Android sono le seguenti:

Per prima cosa è necessario connettersi al servizio web / jSON:

Image

  • in [1], si inserisce l’URL del servizio web / jSON. Con l'emulatore, inserite uno degli indirizzi IP o PC (ma non 127.0.0.1). Con un tablet, inserite l'indirizzo Wi-Fi del computer che ospita il server web / jSON e disattivate il firewall del server, se presente, poiché potrebbe bloccare le chiamate in entrata;
  • su [2], ci si connette;

Si accede quindi alla pagina di simulazione:

Image

  • in [3], si seleziona un dipendente;
  • in [4], si indica un numero di ore;
  • in [5], si indica un numero di giorni;
  • in [6], si avvia la simulazione;

La pagina di simulazione ottenuta è la seguente:

Image

  • in [7], la simulazione ottenuta;
  • in [8], la si salva;

Image

  • in [9], l'elenco delle simulazioni;
  • in [10], si rimuove una simulazione;

Image

  • in [11], non ci sono più simulazioni;
  • in [12], si torna al modulo di simulazione;

Image

  • in [13], si torna al modulo;
  • in [14], si torna alla pagina di configurazione;

Image

  • da [15], si torna al modulo di accesso iniziale.

4.5. Lavoro da svolgere

Lo scheletro dell’app Android presentato in precedenza vi viene fornito. È stato realizzato a partire dal progetto [client-android-skel] descritto al paragrafo 2.

  

Il progetto è eseguibile e dispone già delle viste necessarie. È sufficiente aggiungere del codice affinché l’applicazione svolga le operazioni previste. La procedura da seguire è la seguente:

  • eseguite la versione completa per comprendere il lavoro da svolgere;
  • eseguire la versione ridotta e studiarne il codice. Esso rispetta i metodi di progettazione utilizzati nelle pagine precedenti;
  • aggiungere il codice mancante;