Skip to content

4. TP 1: Podstawowe zarządzanie kartą płacową

4.1. Introduction

Aby zastosować zdobytą wcześniej wiedzę, proponujemy teraz zadanie polegające na napisaniu aplikacji klienckiej na tablet z systemem Android, umożliwiającej symulację obliczeń listy płac pracowników stowarzyszenia.

Aplikacja będzie miała architekturę klient-serwer:

Image

  • serwer [1] jest już udostępniony;
  • należy stworzyć klienta na Androida [2].

4.2. Baza danych

4.2.1. Definicja

Dane statyczne potrzebne do sporządzenia listy płac zostaną umieszczone w bazie danych, którą w dalszej części nazwiemy dbpam. Baza ta zawiera następujące tabele:

Tabela EMPLOYES: zawiera informacje o poszczególnych opiekunkach do dzieci

Struktura:

ID
klucz główny
VERSION
numer wersji – zwiększa się przy każdej zmianie w wierszu
SS
numer ubezpieczenia społecznego pracownika – unikalny
NOM
imię i nazwisko pracownika
PRENOM
jego imię
ADRESSE
jego adres
VILLE
jego miasto
CODEPOSTAL
jego kod pocztowy
INDEMNITE_ID
klucz obcy w polu [ID] tabeli [INDEMNITES]

Jego zawartość mogłaby wyglądać następująco:

Image

Tabela COTISATIONS: zawiera wartości procentowe niezbędne do obliczenia składek na ubezpieczenie społeczne

Struktura:

ID
klucz główny
VERSION
numer wersji – zwiększa się przy każdej zmianie wiersza
CSGRDS
procent: ogólna składka społeczna + składka na spłatę długu społecznego
CSGD
procent: ogólna składka na ubezpieczenie społeczne podlegająca odliczeniu
SECU
procent: ubezpieczenie społeczne, renta wdowia, emerytura
RETRAITE
procent: dodatkowa emerytura + ubezpieczenie od bezrobocia

Jego treść mogłaby wyglądać następująco:

Image

Stawki składek na ubezpieczenie społeczne są niezależne od pracownika. Powyższa tabela zawiera tylko jeden wiersz.

Tabela INDEMNITES: zawiera elementy umożliwiające obliczenie wynagrodzenia do wypłaty.
ID
klucz główny
VERSION
numer wersji – zwiększa się przy każdej zmianie wiersza
INDICE
indeks przetwarzania – unikalny
BASEHEURE
Cena netto w euro za godzinę dyżuru
ENTRETIENJOUR
dodatek na utrzymanie w euro za dzień opieki
REPASJOUR
dodatek na posiłki w euro za dzień opieki
INDEMNITESCP
dodatek urlopowy. Jest to procent, który należy zastosować do wynagrodzenia podstawowego.

Jego treść mogłaby wyglądać następująco:

Image

Należy zauważyć, że dodatki mogą się różnić w zależności od opiekunki. Są one bowiem powiązane z konkretną opiekunką poprzez jej wskaźnik wynagrodzenia. Tak więc pani Marie Jouveinal, której wskaźnik wynagrodzenia wynosi 2 (tabela EMPLOYES), otrzymuje wynagrodzenie godzinowe w wysokości 2,1 euro (tabela INDEMNITES).

4.2.2. Generowanie

Dostarczono skrypt [dbpam_hibernate.sql] służący do generowania bazy danych:

  

Utwórz bazę danych [dbpam_hibernate] (jest to nazwa bazy danych BD, z której korzysta serwer WWW / jSON) i upewnij się, że login root bez hasła ma do niej dostęp. Można to zrobić w następujący sposób:

Uruchom MySQL, a następnie [PhpMyAdmin]:

 
  • [1-2]: zaimportuj skrypt [dbpam_hibernate.sql], a następnie go uruchom;

4.2.3. Modelowanie bazy danych w języku Java

Elementy tabel [EMPLOYES], [INDEMNITES] i [COTISATIONS] są modelowane przez następujące klasy:

[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) {
    ...
  }
   // metody pobierające i ustawiające
....
}
  • wiersze 8–15: pola te odpowiadają kolumnom tabeli [EMPLOYES];
  • wiersz 16: pole [indemniteId] odpowiada kolumnie [INDEMNITE_ID], która jest kluczem obcym tabeli [EMPLOYES];
  • wiersz 17: dodatek pracownika. Pole to nie zawsze jest wypełnione:
    • nie jest wypełnione, gdy żąda się URL [/employes],
    • jest wypełnione, gdy żądane są raporty URL i [/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) {
        ...
    }

     // metody pobierające i ustawiające
   ....
}
  • wiersze 8–14: pola odpowiadają kolumnom tabeli [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) {
        ...
    }
    // metody pobierające i ustawiające
   ...
}
  • wiersze 8–13: pola odpowiadają kolumnom tabeli [COTISATIONS];

4.3. Instalacja serwera WWW / jSON

4.3.1. Instalacja

Otrzymali Państwo plik binarny serwera WWW w języku Java / jSON:

 

Aby uruchomić serwer WWW / jSON, należy postępować w następujący sposób:

  • uruchom plik SGBD MySQL;
  • upewnij się, że istnieją pliki BD i [dbpam_hibernate];
  • otwórz okno DOS;
  • przejdź do folderu pliku jar;
  • wpisz polecenie:
java -jar pam-server-01-all-1.0.jar

Zakłada się, że plik binarny [java.exe] znajduje się w katalogu PATH na Twoim komputerze. Jeśli tak nie jest, wpisz pełną ścieżkę do pliku [java.exe], na przykład:

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

Wyświetlą się logi:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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/**] do obsługi typu [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 [/**] do obsługi typu [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'
  • wiersz 16: wykryto plik URL [/salaire/{SS}/{ht}/{jt}];
  • wiersz 17: wykryto URL [/employes];

4.3.2. URL serwisu internetowego/jSON

Usługa internetowa / jSON jest zaimplementowana przez Spring MVC i udostępnia dwa elementy 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) {

Usługa internetowa akceptuje następujące dwa URL:

  • wiersz 1: /employes: w celu uzyskania listy pracowników;
  • wiersz 4: /salaire/SS/ht/jt: aby uzyskać listę płac pracownika o numerze [SS], który przepracował [ht] godzin w ciągu [jt] dni;

Oto zrzuty ekranu ilustrujące to.

Wymagane są dane pracowników:

Image

Wyłączamy bazę danych, ponownie uruchamiamy serwer i sprawdzamy pracowników:

Image

Wprowadzamy wynagrodzenie:

Image

Żądamy wynagrodzenia dla osoby, która nie istnieje:

Image

4.3.3. Odpowiedzi serwisu internetowego: jSON / jSON

  

Odpowiedzi URL z serwisu internetowego / jSON wysyłają odpowiedzi typu [Response<T>]:


package client.android.dao.service;

import java.util.List;

public class Response<T> {

    // ----------------- właściwości
    // status operacji
    private int status;
    // ewentualne komunikaty o statusie
    private List<String> messages;
    // treść odpowiedzi
    private T body;

    // konstruktory
    public Response() {

    }

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

    // metody pobierające i ustawiające
...
}
  • URL [/employes] zwraca typ Response<List<Employe>>;
  • URL i [/salaire] zwracają typ Response<FeuilleSalaire>;

Klasa [FeuilleSalaire] ma następującą postać:


package pam.entities;

import java.io.Serializable;

public class FeuilleSalaire implements Serializable {

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

    // konstruktory
    public FeuilleSalaire() {
    }

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

    // metody pobierające i ustawiające
   ...
}
  • wiersz 9: klasa [Employe] została przedstawiona w paragrafie 4.2.3;
  • wiersz 10: klasa [Cotisation] została przedstawiona w paragrafie 4.2.3;

Klasa [ElementsSalaire] (wiersz 11) ma następujący wygląd:


package pam.entities;

import java.io.Serializable;

public class ElementsSalaire implements Serializable {

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

    // konstruktory
    public ElementsSalaire() {

    }

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

    // metody pobierające i ustawiające
    ...
}

4.4. Testy klienta na systemie Android

Oto gotowy plik wykonywalny klienta dla systemu Android:

  

Za pomocą myszki przeciągnij powyższy plik wykonywalny [pam-client.apk] na emulator tabletu [GenyMotion]. Zostanie on wówczas zapisany, a następnie uruchomiony. Uruchom również serwer WWW / jSON, jeśli jeszcze tego nie zrobiłeś. Zadaniem klienta na Androida jest pobieranie informacji zwracanych przez serwer WWW / jSON oraz ich formatowanie. Poszczególne widoki klienta na Androida są następujące:

Najpierw należy połączyć się z usługą internetową / jSON:

Image

  • w [1] podajemy adres URL usługi internetowej / jSON. W emulatorze należy wprowadzić jeden z adresów IP lub PC (ale nie 127.0.0.1). Na tablecie wprowadź adres Wi-Fi urządzenia serwera WWW / jSON i wyłącz zaporę sieciową serwera, jeśli taka istnieje, ponieważ może ona blokować połączenia przychodzące;
  • w [2] nawiązujemy połączenie;

Następnie pojawia się strona symulacji:

Image

  • w [3] wybieramy pracownika;
  • w [4] należy podać liczbę godzin;
  • w [5] należy podać liczbę dni;
  • w [6] uruchamia się symulację;

Wyświetla się następująca strona symulacji:

Image

  • w [7] – uzyskana symulacja;
  • w [8] zapisuje się ją;

Image

  • w [9] – lista symulacji;
  • w [10] usuwa się symulację;

Image

  • w [11] nie ma już żadnych symulacji;
  • w [12], powrót do formularza symulacji;

Image

  • w [13] ponownie wyświetla się formularz;
  • w [14] następuje powrót do strony konfiguracji;

Image

  • po wprowadzeniu kodu [15] ponownie wyświetla się początkowy formularz logowania.

4.5. Zadanie do wykonania

Podano wam szkielet klienta dla systemu Android przedstawiony powyżej. Został on stworzony na podstawie projektu [client-android-skel] opisanego w akapicie 2.

  

Projekt jest gotowy do uruchomienia i zawiera już niezbędne widoki. Wystarczy dodać kod, aby aplikacja działała zgodnie z przeznaczeniem. Należy postępować w następujący sposób:

  • uruchomcie pełną wersję, aby zapoznać się z zadaniem;
  • uruchom wersję uproszczoną i zapoznaj się z jej kodem. Jest on zgodny z metodami projektowania opisanymi na poprzednich stronach;
  • dodaj brakujący kod;