Skip to content

4. Aufgabe 1: Grundlegende Verwaltung von Gehaltsabrechnungen

4.1. Einführung

Um das bisher Gelernte anzuwenden, schlagen wir nun eine Aufgabe vor, bei der ein Android-Client für Tablets entwickelt wird, der die Gehaltsabrechnung für die Mitarbeiter eines Vereins simuliert.

Die Anwendung wird eine Client-Server-Architektur aufweisen:

Image

  • Der Server [1] wird bereitgestellt;
  • Sie müssen den Android-Client [2] erstellen.

4.2. Die Datenbank

4.2.1. Definition

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

Tabelle EMPLOYEES: enthält Informationen zu den verschiedenen Kinderbetreuungsanbietern

Struktur:

ID
Primärschlüssel
VERSION
Versionsnummer – wird bei jeder Änderung der Zeile erhöht
SS
Sozialversicherungsnummer des Mitarbeiters – eindeutig
NAME
Nachname des Mitarbeiters
VORNAME
Vorname
ADRESSE
ihre Adresse
STADT
seine/ihre Stadt
POSTLEITZAHL
seine/ihre Postleitzahl
INDEMNITY_ID
Fremdschlüssel auf das Feld [ID] der Tabelle [INDEMNITES]

Der Inhalt könnte wie folgt aussehen:

Image

Tabelle COTISATIONS: enthält die Prozentsätze, die zur Berechnung der Sozialversicherungsbeiträge benötigt werden

Struktur:

ID
Primärschlüssel
VERSION
Versionsnummer – wird bei jeder Änderung der Zeile erhöht
CSGRDS
Prozentsatz: Allgemeiner Sozialbeitrag + Beitrag zur Tilgung der Sozialschulden
CSGD
Prozentsatz: abzugsfähiger allgemeiner Sozialbeitrag
SECU
Prozentsatz: Sozialversicherung, Witwenrente, Altersrente
RENTE
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 „ALLOWANCES“: enthält die Elemente, die zur Berechnung des zu zahlenden Gehalts verwendet werden.
ID
Primärschlüssel
VERSION
Versionsnummer – wird bei jeder Änderung der Zeile erhöht
INDEX
Verarbeitungsindex – eindeutig
STUNDENSATZ
Nettopreis in Euro für eine Stunde Bereitschaftsdienst
TÄGLICHE WARTUNG
Tagespauschale in Euro pro Pflegetag
MAHLZEIT PRO TAG
Verpflegungszuschuss in Euro pro Pflegetag
URLAUBSGELD
Bezahlter Urlaubsgeldzuschlag. Dies ist ein Prozentsatz, der auf das Grundgehalt angewendet wird.

Der Inhalt könnte wie folgt lauten:

Image

Beachten Sie, dass die Zulagen von Kinderbetreuungsanbieter zu Kinderbetreuungsanbieter variieren können. Sie sind über die jeweilige Gehaltsstufe an einen bestimmten Kinderbetreuungsanbieter gebunden. Beispielsweise hat Frau Marie Jouveinal, die der Gehaltsstufe 2 angehört (Tabelle „EMPLOYEES“), einen Stundenlohn von 2,1 Euro (Tabelle „INDEMNITES“).

4.2.2. Generierung

Das Skript zur Datenbankgenerierung [dbpam_hibernate.sql] wird bereitgestellt:

  

Erstellen Sie die Datenbank [dbpam_hibernate] (dies ist der Name der vom Webserver/jSON verwendeten Datenbank) und stellen Sie sicher, dass der Root-Benutzer (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 [EMPLOYEES], [ALLOWANCES] und [CONTRIBUTIONS] werden durch die folgenden Klassen modelliert:

[ Mitarbeiter]


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 and setters
....
}
  • Zeilen 8–15: Diese Felder entsprechen den Spalten in der Tabelle [EMPLOYEES];
  • Zeile 16: Das Feld [indemniteId] entspricht der Spalte [INDEMNITE_ID], die den Fremdschlüssel der Tabelle [EMPLOYEES] darstellt;
  • Zeile 17: die Zulage des Mitarbeiters. Dieses Feld ist nicht immer ausgefüllt:
    • Es wird nicht ausgefüllt, wenn die URL [/employees] aufgerufen wird,
    • es wird bei der Abfrage der URL [/salary] ausgefüllt;

[ 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 and setters
   ....
}
  • Zeilen 8–14: Die Felder entsprechen den Spalten der Tabelle [INDEMNITES];

[ Beitrag]


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

4.3. Webserver / JSON-Installation

4.3.1. Installation

Die Java-Binärdatei für den Web-/JSON-Server wird bereitgestellt:

 

Um den Web/JSON-Server zu starten, gehen Sie wie folgt vor:

  • Starten Sie das MySQL-DBMS;
  • Stellen Sie sicher, dass die Datenbank [dbpam_hibernate] vorhanden ist;
  • Öffnen Sie ein Eingabeaufforderungsfenster;
  • Navigieren Sie zum Jar-Ordner;
  • Geben Sie den folgenden Befehl ein:
java -jar pam-server-01-all-1.0.jar

Dies setzt voraus, dass sich die ausführbare Datei [java.exe] im PATH Ihres Computers 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/**] 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'
  • Zeile 16: Die URL [/salary/{SS}/{ht}/{jt}] wird aufgelöst;
  • Zeile 17: Die URL [/employees] wurde ermittelt;

4.3.2. Webservice-/JSON-URLs

Der Webservice/JSON wird von Spring MVC implementiert und stellt zwei URLs 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 Webservice akzeptiert die folgenden zwei URLs:

  • Zeile 1: /employees: zum Abrufen der Liste der Mitarbeiter;
  • Zeile 4: /salary/SS/ht/jt: zum Abrufen der Gehaltsabrechnung für den Mitarbeiter Nr. [SS], der [ht] Stunden an [jt] Tagen gearbeitet hat;

Hier sind einige Screenshots, die dies veranschaulichen.

Wir fragen die Mitarbeiter ab:

Image

Wir sichern die Datenbank, starten den Server neu und fragen die Mitarbeiter ab:

Image

Wir fragen ein Gehalt ab:

Image

Wir fragen das Gehalt einer nicht existierenden Person ab:

Image

4.3.3. Die JSON-Antworten vom Webservice/JSON

  

Die Webservice-/JSON-URLs geben Antworten vom Typ [Response<T>] zurück:


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;
    // the body of the reply
    private T body;
 
    // manufacturers
    public Response() {
 
    }
 
    public Response(int status, List<String> messages, T body) {
        this.status = status;
        this.messages = messages;
        this.body = body;
    }
 
    // getters and setters
...
}
  • Die URL [/employees] gibt eine Response<List<Employee>> zurück;
  • Die URL [/salary] gibt einen Response<PayStub>-Typ zurück;

Die Klasse [PayrollSheet] sieht wie folgt aus:


package pam.entities;
 
import java.io.Serializable;
 
public class FeuilleSalaire implements Serializable {
 
    private static final long serialVersionUID = 1L;
    // private fields
    private Employe employe;
    private Cotisation cotisation;
    private ElementsSalaire elementsSalaire;
 
    // manufacturers
    public FeuilleSalaire() {
    }
 
    public FeuilleSalaire(Employe employe, Cotisation cotisation, ElementsSalaire elementsSalaire) {
        ...
    }
 
    // getters and setters
   ...
}
  • Zeile 9: Die Klasse [Employee] wurde in Abschnitt 4.2.3 vorgestellt;
  • Zeile 10: Die Klasse [Contribution] wurde in Abschnitt 4.2.3 vorgestellt;

Die Klasse [SalaryElements] (Zeile 11) sieht wie folgt aus:


package pam.entities;
 
import java.io.Serializable;
 
public class ElementsSalaire implements Serializable {
 
    private static final long serialVersionUID = 1L;
    // private fields
    private double salaireBase;
    private double cotisationsSociales;
    private double indemnitesEntretien;
    private double indemnitesRepas;
    private double salaireNet;
 
    // manufacturers
    public ElementsSalaire() {
 
    }
 
    public ElementsSalaire(double salaireBase, double cotisationsSociales, double indemnitesEntretien, double indemnitesRepas, double salaireNet) {
        ...
    }
 
    // getters and setters
    ...
}

4.4. Android-Client-Tests

Die ausführbare Binärdatei für den fertigen Android-Client finden Sie unten:

  

Ziehe die oben angezeigte Datei [pam-client.apk] mit der Maus auf den Tablet-Emulator [GenyMotion]. Sie wird dann gespeichert und ausgeführt. Starte außerdem den Web-/JSON-Server, falls du dies noch nicht getan hast. Der Android-Client dient dazu, die vom Web-/JSON-Server zurückgegebenen Informationen abzurufen und zu formatieren. Die verschiedenen Ansichten des Android-Clients sind wie folgt:

Zunächst müssen Sie eine Verbindung zum Webdienst / JSON herstellen:

Image

  • Geben Sie in [1] die URL des Web-/JSON-Dienstes ein. Geben Sie beim Emulator eine der IP-Adressen des PCs ein (jedoch nicht 127.0.0.1). Geben Sie bei einem Tablet die WLAN-Adresse des Web-/JSON-Server-Rechners ein und deaktivieren Sie die Firewall des Servers, falls vorhanden, da diese eingehende Verbindungen blockieren könnte;
  • Melden Sie sich unter [2] an;

Sie werden dann zur Simulationsseite weitergeleitet:

Image

  • Wählen Sie unter [3] einen Mitarbeiter aus;
  • Geben Sie in [4] eine Stundenzahl ein;
  • Geben Sie in [5] die Anzahl der Tage ein;
  • Führen Sie in [6] die Simulation durch;

Die resultierende Simulationsseite sieht wie folgt aus:

Image

  • in [7] die Simulationsergebnisse;
  • in [8] speichern;

Image

  • in [9] die Liste der Simulationen;
  • in [10] wird eine Simulation gelöscht;

Image

  • in [11] sind keine Simulationen mehr vorhanden;
  • In [12] kehren Sie zum Simulationsformular zurück;

Image

  • in [13] kehren Sie zum Formular zurück;
  • in [14] kehren Sie zur Konfigurationsseite zurück;

Image

  • über [15] gelangen Sie zurück zum Anmeldeformular.

4.5. Zu erledigende Aufgaben

Das zuvor vorgestellte Android-Client-Skelett wird Ihnen zur Verfügung gestellt. Es wurde aus dem in Abschnitt 2 beschriebenen Projekt [client-android-skel] erstellt.

  

Das Projekt ist lauffähig und enthält bereits die erforderlichen Ansichten. Sie müssen lediglich Code hinzufügen, damit die Anwendung die gewünschte Funktion erfüllt. Gehen Sie dazu wie folgt vor:

  • Führen Sie die Vollversion aus, um die durchzuführenden Aufgaben zu verstehen;
  • Führen Sie die Light-Version aus und studieren Sie deren Code. Er folgt den auf den vorherigen Seiten verwendeten Entwurfsmethoden;
  • Fügen Sie den fehlenden Code hinzu;