Skip to content

4. TP 1: Grundlegende Verwaltung einer Gehaltsabrechnung

4.1. Introduction

Um das bisher Gelernte anzuwenden, schlagen wir nun eine Aufgabe vor, bei der ein Android-Client für Tablets geschrieben werden soll, mit dem die Lohnabrechnungen der Mitarbeiter eines Vereins simuliert werden können.

Die Anwendung wird eine Client-Server-Architektur aufweisen:

Image

  • Der Server [1] wird bereitgestellt;
  • der Android-Client [2] muss erstellt werden.

4.2. Die Datenbank

4.2.1. Definition

Die statischen Daten, die für die Erstellung der Gehaltsabrechnung benötigt werden, werden in einer Datenbank gespeichert, die wir im Folgenden als dbpam“ bezeichnen. Diese Datenbank enthält die folgenden Tabellen:

Tabelle EMPLOYES: enthält Informationen zu den verschiedenen Tagesmüttern

Struktur:

ID
Primärschlüssel
VERSION
Versionsnummer – erhöht sich bei jeder Änderung der Zeile
SS
Sozialversicherungsnummer des Mitarbeiters – eindeutig
NOM
Name des Mitarbeiters
PRENOM
sein Vorname
ADRESSE
seine Adresse
VILLE
seine Stadt
CODEPOSTAL
seine Postleitzahl
INDEMNITE_ID
Fremdschlüssel im Feld [ID] der Tabelle [INDEMNITES]

Sein Inhalt könnte wie folgt lauten:

Image

Tabelle COTISATIONS: enthält die für die Berechnung der Sozialbeiträge erforderlichen Prozentsätze

Struktur:

ID
Primärschlüssel
VERSION
Versionsnummer – erhöht sich bei jeder Änderung der Zeile
CSGRDS
Prozentsatz: allgemeiner Sozialbeitrag + Beitrag zur Tilgung der Sozialschuld
CSGD
Prozentsatz: abzugsfähiger allgemeiner Sozialbeitrag
SECU
Prozentsatz: Sozialversicherung, Witwenrente, Altersrente
RETRAITE
Prozentsatz: Zusatzrente + Arbeitslosenversicherung

Der Inhalt könnte wie folgt lauten:

Image

Die Sozialversicherungsbeiträge sind unabhängig vom Arbeitnehmer. Die vorstehende Tabelle enthält nur eine Zeile.

Tabelle INDEMNITES: enthält die Elemente, die zur Berechnung des zu zahlenden Gehalts erforderlich sind.
ID
Primärschlüssel
VERSION
Versionsnummer – erhöht sich bei jeder Änderung der Zeile
INDICE
Verarbeitungsindex – eindeutig
BASEHEURE
Nettopreis in Euro für eine Stunde Bereitschaftsdienst
ENTRETIENJOUR
Verpflegungszuschlag in Euro pro Betreuungs-Tag
REPASJOUR
Verpflegungszulage in Euro pro Betreuungs-Tag
INDEMNITESCP
Urlaubsgeld. Dies ist ein Prozentsatz, der auf das Grundgehalt anzuwenden ist.

Der Inhalt könnte wie folgt lauten:

Image

Es ist zu beachten, dass die Zulagen von einer Tagesmutter zur anderen variieren können. Sie sind nämlich über den Gehaltsindex der jeweiligen Tagesmutter mit dieser verknüpft. So hat Frau Marie Jouveinal, die einen Gehaltsindex von 2 hat (Tabelle EMPLOYES), einen Stundenlohn von 2,1 Euro (Tabelle INDEMNITES).

4.2.2. Generierung

Das Skript [dbpam_hibernate.sql] zur Generierung der Datenbank wird bereitgestellt:

  

Erstellen Sie die Datenbank [dbpam_hibernate] (dies ist der Name der Datenbank BD, die vom Webserver / jSON genutzt wird) und stellen Sie sicher, dass der Benutzer root ohne Passwort darauf zugreifen kann. Gehen Sie dazu wie folgt vor:

Starten Sie MySQL und anschließend [PhpMyAdmin]:

 
  • [1-2]: Importieren Sie das Skript [dbpam_hibernate.sql] und führen Sie es anschließend aus;

4.2.3. Java-Modellierung der Datenbank

Die Elemente der Tabellen [EMPLOYES], [INDEMNITES] und [COTISATIONS] werden durch die folgenden Klassen modelliert:

[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 und Setter
....
}
  • Zeilen 8–15: Diese Felder entsprechen den Spalten der Tabelle [EMPLOYES];
  • Zeile 16: Das Feld [indemniteId] entspricht der Spalte [INDEMNITE_ID], die den Fremdschlüssel der Tabelle [EMPLOYES] darstellt;
  • Zeile 17: die Vergütung des Mitarbeiters. Dieses Feld ist nicht immer ausgefüllt:
    • Er ist nicht ausgefüllt, wenn man die Felder URL und [/employes] abfragt,
    • es ist ausgefüllt, wenn die Tabellen URL und [/salaire] abgefragt werden;

[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 und Setter
   ....
}
  • Zeilen 8–14: Die Felder entsprechen den Spalten der Tabelle [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 und Setter
   ...
}
  • Zeilen 8–13: Die Felder entsprechen den Spalten der Tabelle [COTISATIONS];

4.3. Installation des Webservers / jSON

4.3.1. Installation

Die Java-Binärdatei des Webservers / jSON wird Ihnen zur Verfügung gestellt:

 

Um den Webserver / jSON zu starten, gehen Sie wie folgt vor:

  • Starten Sie SGBD MySQL;
  • stellen Sie sicher, dass die Dateien BD und [dbpam_hibernate] vorhanden sind;
  • Öffnen Sie ein DOS-Fenster;
  • wechseln Sie in den Ordner der JAR-Datei;
  • Geben Sie den folgenden Befehl ein:
java -jar pam-server-01-all-1.0.jar

Dies setzt voraus, dass sich die Binärdatei [java.exe] im Verzeichnis PATH auf Ihrem Rechner befindet. Ist dies nicht der Fall, geben Sie den vollständigen Pfad zu [java.exe] ein, zum Beispiel:

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

Es werden Protokolle angezeigt:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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/**] auf den Handler vom Typ [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 vom Typ [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'
  • Zeile 16: URL [/salaire/{SS}/{ht}/{jt}] wurde gefunden;
  • Zeile 17: URL [/employes] wird erkannt;

4.3.2. Die URL des Webdienstes/jSON

Der Webdienst / jSON wird von Spring MVC implementiert und stellt zwei URL bereit:


@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) {

Der Webdienst akzeptiert die folgenden beiden URL:

  • Zeile 1: /employes: um die Liste der Mitarbeiter abzurufen;
  • Zeile 4: /salaire/SS/ht/jt: um die Gehaltsabrechnung des Mitarbeiters mit der Nummer [SS] abzurufen, der [ht] Stunden an [jt] Tagen gearbeitet hat;

Hier sind einige Screenshots, die dies veranschaulichen.

Gesucht werden die Mitarbeiter:

Image

Die Datenbank wird heruntergefahren, der Server neu gestartet und die Mitarbeiter abgefragt:

Image

Man fragt nach einem Gehalt:

Image

Man fragt nach dem Gehalt einer nicht existierenden Person:

Image

4.3.3. Die Antworten jSON des Webdienstes/jSON

  

Die Webdienst-Antworten URL / jSON senden Antworten vom Typ [Response<T>]:


package client.android.dao.service;

import java.util.List;

public class Response<T> {

    // ----------------- Eigenschaften
    // Status der Transaktion
    private int status;
    // allfällige Statusmeldungen
    private List<String> messages;
    // der Antworttext
    private T body;

    // Konstruktoren
    public Response() {

    }

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

    // Getter und Setter
...
}
  • URL und [/employes] geben einen Typ „Response<List<Employe>>“ zurück;
  • URL und [/salaire] geben den Typ „Response<FeuilleSalaire>“ zurück;

Die Klasse [FeuilleSalaire] sieht wie folgt aus:


package pam.entities;

import java.io.Serializable;

public class FeuilleSalaire implements Serializable {

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

    // Konstruktoren
    public FeuilleSalaire() {
    }

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

    // Getter und Setter
   ...
}
  • Zeile 9: Die Klasse [Employe] wurde in Abschnitt 4.2.3 vorgestellt;
  • Zeile 10: Die Klasse [Cotisation] wurde in Abschnitt 4.2.3 vorgestellt;

Die Klasse [ElementsSalaire] (Zeile 11) lautet wie folgt:


package pam.entities;

import java.io.Serializable;

public class ElementsSalaire implements Serializable {

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

    // Konstruktoren
    public ElementsSalaire() {

    }

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

    // Getter und Setter
    ...
}

4.4. Tests des Android-Clients

Die fertige ausführbare Binärdatei des Android-Clients wird Ihnen wie folgt bereitgestellt:

  

Ziehen Sie die oben genannte Binärdatei [pam-client.apk] mit der Maus auf einen Tablet-Emulator [GenyMotion]. Sie wird dann gespeichert und anschließend ausgeführt. Starten Sie außerdem den Webserver / jSON, falls dies noch nicht geschehen ist. Der Android-Client dient dazu, die vom Webserver / jSON zurückgegebenen Informationen abzurufen und aufzubereiten. Der Android-Client verfügt über folgende Ansichten:

Zunächst muss eine Verbindung zum Webdienst / jSON hergestellt werden:

Image

  • In [1] wird die URL des Webdienstes /jSON angegeben. Geben Sie im Emulator eine der Adressen IP oder PC ein (jedoch nicht 127.0.0.1). Geben Sie auf einem Tablet die WLAN-Adresse des Webserver-Rechners / jSON ein und deaktivieren Sie die Firewall des Server-Rechners, falls vorhanden, da diese eingehende Verbindungen blockieren könnte;
  • Unter [2] stellen Sie eine Verbindung her;

Man gelangt dann zur Simulationsseite:

Image

  • In [3] wählen Sie einen Mitarbeiter aus;
  • In [4] gibt man eine Stundenzahl an;
  • unter [5] gibt man die Anzahl der Tage ein;
  • in [6] wird die Simulation gestartet;

Die daraufhin angezeigte Simulationsseite sieht wie folgt aus:

Image

  • in [7] wird die erzeugte Simulation angezeigt;
  • In [8] wird sie gespeichert;

Image

  • in [9], die Liste der Simulationen;
  • in [10] wird eine Simulation entfernt;

Image

  • in [11], es sind keine Simulationen mehr vorhanden;
  • in [12] kehrt man zum Simulationsformular zurück;

Image

  • in [13] gelangt man wieder zum Formular;
  • in [14] kehrt man zur Konfigurationsseite zurück;

Image

  • mit [15] gelangt man zurück zum ursprünglichen Anmeldeformular.

4.5. Zu erledigende Aufgabe

Das zuvor vorgestellte Grundgerüst des Android-Clients wird Ihnen zur Verfügung gestellt. Es wurde auf der Grundlage des in Abschnitt 2 beschriebenen Projekts [client-android-skel] erstellt.

  

Das Projekt ist lauffähig und verfügt bereits über die erforderlichen Ansichten. Es muss lediglich Code hinzugefügt werden, damit die Anwendung ihre Aufgaben erfüllen kann. Gehen Sie wie folgt vor:

  • Führen Sie die Vollversion aus, um sich einen Überblick über die anstehenden Aufgaben zu verschaffen;
  • Führen Sie die vereinfachte Version aus und studieren Sie deren Code. Er entspricht den auf den vorherigen Seiten verwendeten Entwurfsmethoden;
  • fügen Sie den fehlenden Code hinzu;