Skip to content

4. TP 1 : Gestion basique d'une fiche de paie

4.1. Introduction

Pour appliquer ce qui a été vu précédemment, nous proposons maintenant un travail consistant à écrire un client Android pour tablette, permettant de simuler des calculs de feuille de salaire des employés d'une association.

L'application aura une architecture client / serveur :

Image

  • le serveur [1] est founi ;
  • il faut construire le client Android [2].

4.2. La base de données

4.2.1. Définition

Les données statiques utiles pour construire la fiche de paie seront placées dans une base de données que nous désignerons par la suite dbpam. Cette base de données a les tables suivantes :

Table EMPLOYES : rassemble des informations sur les différentes assistantes maternelles

Structure :

ID
clé primaire
VERSION
n° de version – augmente à chaque modification de la ligne
SS
numéro de sécurité sociale de l'employé - unique
NOM
nom de l'employé
PRENOM
son prénom
ADRESSE
son adresse
VILLE
sa ville
CODEPOSTAL
son code postal
INDEMNITE_ID
clé étrangère sur le champ [ID] de la table [INDEMNITES]

Son contenu pourrait être le suivant :

Image

Table COTISATIONS : rassemble des pourcentages nécessaires au calcul des cotisations sociales

Structure :

ID
clé primaire
VERSION
n° de version – augmente à chaque modification de la ligne
CSGRDS
pourcentage : contribution sociale généralisée + contribution au remboursement de la dette sociale
CSGD
pourcentage : contribution sociale généralisée déductible
SECU
pourcentage : sécurité sociale, veuvage, vieillesse
RETRAITE
pourcentage : retraite complémentaire + assurance chômage

Son contenu pourrait être le suivant :

Image

Les taux des cotisations sociales sont indépendants du salarié. La table précédente n'a qu'une ligne.

Table INDEMNITES : rassemble les éléments permettant le calcul du salaire à payer.
ID
clé primaire
VERSION
n° de version – augmente à chaque modification de la ligne
INDICE
indice de traitement - unique
BASEHEURE
prix net en euro d’une heure de garde
ENTRETIENJOUR
indemnité d’entretien en euro par jour de garde
REPASJOUR
indemnité de repas en euro par jour de garde
INDEMNITESCP
indemnité de congés payés. C'est un pourcentage à appliquer au salaire de base.

Son contenu pourrait être le suivant :

Image

On notera que les indemnités peuvent varier d'une assistante maternelle à une autre. Elles sont en effet associées à une assistante maternelle précise via l'indice de traitement de celle-ci. Ainsi Mme Marie Jouveinal qui a un indice de traitement de 2 (table EMPLOYES) a un salaire horaire de 2,1 euro (table INDEMNITES).

4.2.2. Génération

Le script [dbpam_hibernate.sql] de génération de la base de données est fourni :

  

Créez la base de données [dbpam_hibernate] (c'est le nom de la BD que le serveur web / jSON exploite) et faites en sorte que le login root sans mot de passe puisse y accéder. Vous pouvez procéder ainsi :

Lancez MySQL puis [PhpMyAdmin] :

 
  • [1-2] : importez le script [dbpam_hibernate.sql] puis exécutez-le ;

4.2.3. Modélisation Java de la base de données

Les éléments des tables [EMPLOYES], [INDEMNITES] et [COTISATIONS] sont modélisés par les classes suivantes :

[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 et setters
....
}
  • lignes 8-15 : ces champs correspondent aux colonnes de la table [EMPLOYES] ;
  • ligne 16 : le champ [indemniteId] correspond à la colonne [INDEMNITE_ID] qui est la clé étrangère de la table [EMPLOYES] ;
  • ligne 17 : l'indemnité de l'employé. Ce champ n'est pas toujours renseigné :
    • il ne l'est pas lorsqu'on demande l'URL [/employes],
    • il l'est lorsqu'on demande 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) {
        ...
    }

    // getters et setters
   ....
}
  • lignes 8-14 : les champs correspondent aux colonnes de la table [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 et setters
   ...
}
  • lignes 8-13 : les champs correspondent aux colonnes de la table [COTISATIONS] ;

4.3. Installation du serveur web / jSON

4.3.1. Installation

Le binaire Java du serveur web / jSON vous est fourni :

 

Pour lancer le serveur web / jSON, procédez de la façon suivante :

  • lancez le SGBD MySQL ;
  • assurez-vous que la BD [dbpam_hibernate] existe ;
  • ouvrez une fenêtre DOS ;
  • placez vous dans le dossier du jar ;
  • tapez la commande :
java -jar pam-server-01-all-1.0.jar

Cela suppose que le binaire [java.exe] est dans le PATH de votre machine. Si ce n'est pas le cas, tapez le chemin complet de [java.exe], par exemple :

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

Des logs s'affichent :

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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/**] 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'
  • ligne 16 : l'URL [/salaire/{SS}/{ht}/{jt}] est découverte ;
  • ligne 17 : l'URL [/employes] est découverte ;

4.3.2. Les URL du service web/jSON

Le service web / jSON est implémenté par Spring MVC et expose deux 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) {

Le service web accepte les deux URL suivantes :

  • ligne 1 : /employes : pour avoir la liste des employés ;
  • ligne 4 : /salaire/SS/ht/jt : pour avoir la feuille de salaire de l'employé de n° [SS] ayant travaillé [ht] heures pendant [jt] jours ;

Voici des copies d'écran montrant cela.

On demande les employés :

Image

On coupe la base, on relance le serveur et on demande les employés :

Image

On demande un salaire :

Image

On demande le salaire d'une personne inexistante :

Image

4.3.3. Les réponses jSON du service web/jSON

  

Les URL du service web / jSON envoient des réponses de type [Response<T>] :


package client.android.dao.service;

import java.util.List;

public class Response<T> {

    // ----------------- propriétés
    // statut de l'opération
    private int status;
    // les éventuels messages d'status
    private List<String> messages;
    // le corps de la réponse
    private T body;

    // constructeurs
    public Response() {

    }

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

    // getters et setters
...
}
  • l'URL [/employes] renvoie un type Response<List<Employe>> ;
  • l'URL [/salaire] renvoie un type Response<FeuilleSalaire> ;

La classe [FeuilleSalaire] est la suivante :


package pam.entities;

import java.io.Serializable;

public class FeuilleSalaire implements Serializable {

    private static final long serialVersionUID = 1L;
    // champs privés
    private Employe employe;
    private Cotisation cotisation;
    private ElementsSalaire elementsSalaire;

    // constructeurs
    public FeuilleSalaire() {
    }

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

    // getters et setters
   ...
}
  • ligne 9 : la classe [Employe] a été présentée paragraphe 4.2.3;
  • ligne 10 : la classe [Cotisation] a été présentée paragraphe 4.2.3 ;

La classe [ElementsSalaire] (ligne 11) est la suivante :


package pam.entities;

import java.io.Serializable;

public class ElementsSalaire implements Serializable {

    private static final long serialVersionUID = 1L;
    // champs privés
    private double salaireBase;
    private double cotisationsSociales;
    private double indemnitesEntretien;
    private double indemnitesRepas;
    private double salaireNet;

    // constructeurs
    public ElementsSalaire() {

    }

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

    // getters et setters
    ...
}

4.4. Tests du client Android

Le binaire exécutable du client Android terminé vous est donné :

  

Avec la souris déposez le binaire [pam-client.apk] ci-dessus sur un émulateur de tablette [GenyMotion]. Il va alors être enregistré puis exécuté. Lancez également le serveur web / jSON si ce n'est déjà fait. Le client Android a pour objet de récupérer les informations renvoyées par le serveur web / jSON et de les mettre en forme. Les différentes vues du client Android sont les suivantes :

Il faut tout d'abord se connecter au service web / jSON :

Image

  • en [1], on donne l'URL du service web / jSON. Avec l'émulateur, mettez l'une des adresses IP du PC (mais pas 127.0.0.1). Avec une tablette, mettez l'adresse wifi de la machine du serveur web / jSON et inhibez le pare-feu du poste serveur s'il en a un car il risque de bloquer les appels entrants ;
  • en [2], on se connecte ;

On arrive alors à la page de simulation :

Image

  • en [3], on choisit un employé ;
  • en [4], on indique un nombre d'heures ;
  • en [5], on indique un nombre de jours ;
  • en [6], on demande la simulation ;

La page de simulation obtenue est la suivante :

Image

  • en [7], la simulation obtenue ;
  • en [8], on l'enregistre ;

Image

  • en [9], la liste des simulations ;
  • en [10], on retire une simulation ;

Image

  • en [11], il n'y a plus de simulations ;
  • en [12], on retourne au formulaire de simulation ;

Image

  • en [13], on retrouve le formulaire ;
  • en [14], on revient vers la page de configuration;

Image

  • en [15], on retrouve le formulaire de connexion initial.

4.5. Travail à faire

Le squelette du client Android présenté précédemment vous est donné. Il a été construit à partir du projet [client-android-skel] décrit au paragraphe 2.

  

Le projet est exécutable et a déjà les vues nécessaires. Il y a simplement du code à rajouter pour que l'application fasse ce qu'elle a à faire. La procédure à suivre est la suivante :

  • exécutez la version complète pour appréhender le travail à faire ;
  • exécutez la version allégée et étudiez le code de celle-ci. Il respecte les méthodes de conception utilisées dans les pages précédentes ;
  • ajoutez le code manquant ;