Skip to content

4. TP 1: Базове управління розрахунковим листом

4.1. Introduction

Щоб застосувати отримані раніше знання, ми пропонуємо завдання, яке полягає у написанні клієнтського додатка для планшета на базі Android, що дозволить моделювати розрахунок заробітної плати співробітників асоціації.

Додаток матиме архітектуру «клієнт-сервер»:

Image

  • сервер [1] вже надано;
  • необхідно створити клієнт для Android [2].

4.2. База даних

4.2.1. Визначення

Статичні дані, необхідні для формування розрахункового листка, будуть розміщені в базі даних, яку надалі позначатимемо як dbpam. Ця база даних містить такі таблиці:

Таблиця EMPLOYES: містить інформацію про різних нянь

Структура:

ID
первинний ключ
VERSION
номер версії — збільшується при кожній зміні рядка
SS
номер соціального страхування працівника — унікальний
NOM
ім'я працівника
PRENOM
його ім'я
ADRESSE
його адреса
VILLE
його місто
CODEPOSTAL
його поштовий індекс
INDEMNITE_ID
зовнішній ключ у полі [ID] таблиці [INDEMNITES]

Його вміст може бути таким:

Image

Таблиця COTISATIONS: містить відсотки, необхідні для розрахунку соціальних внесків

Структура:

ID
первинний ключ
VERSION
номер версії — збільшується при кожній зміні рядка
CSGRDS
відсоток: загальний соціальний внесок + внесок на погашення соціального боргу
CSGD
відсоток: загальний соціальний внесок, що підлягає вирахуванню
SECU
відсоток: соціальне страхування, пенсія у зв’язку з втратою годувальника, пенсія за віком
RETRAITE
відсоток: додаткова пенсія + страхування на випадок безробіття

Його зміст може бути таким:

Image

Ставки соціальних внесків не залежать від працівника. Попередня таблиця містить лише один рядок.

Таблиця INDEMNITES: містить дані, необхідні для розрахунку заробітної плати до виплати.
ID
первинний ключ
VERSION
номер версії — збільшується при кожній зміні рядка
INDICE
індекс обробки — унікальний
BASEHEURE
чиста вартість однієї години чергування в євро
ENTRETIENJOUR
добова надбавка за утримання в євро за день догляду
REPASJOUR
добові на харчування в євро за день догляду
INDEMNITESCP
допомога за оплачувану відпустку. Це відсоток, який застосовується до базової заробітної плати.

Його зміст може бути таким:

Image

Слід зауважити, що розмір надбавок може відрізнятися від однієї няні до іншої. Вони, а саме, прив’язані до конкретної няні через її індекс оплати праці. Так, пані Марі Жувенал, яка має індекс оплати праці 2 (таблиця EMPLOYES), отримує погодинну заробітну плату в розмірі 2,1 євро (таблиця INDEMNITES).

4.2.2. Генерація

Надається скрипт [dbpam_hibernate.sql] для формування бази даних:

  

Створіть базу даних [dbpam_hibernate] (це ім’я BD, яке використовує веб-сервер / jSON) і забезпечте доступ до неї для логіна root без пароля. Ви можете зробити це таким чином:

Запустіть MySQL, а потім [PhpMyAdmin]:

 
  • [1-2]: імпортуйте скрипт [dbpam_hibernate.sql], а потім запустіть його;

4.2.3. Моделювання бази даних на Java

Елементи таблиць [EMPLOYES], [INDEMNITES] та [COTISATIONS] моделюються за допомогою таких класів:

[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) {
    ...
  }
   // гетери та сеттери
....
}
  • рядки 8–15: ці поля відповідають стовпцям таблиці [EMPLOYES];
  • рядок 16: поле [indemniteId] відповідає стовпцю [INDEMNITE_ID], який є зовнішнім ключем таблиці [EMPLOYES];
  • рядок 17: компенсація працівника. Це поле не завжди заповнюється:
    • воно не заповнюється, коли запитується URL [/employes],
    • воно заповнюється при запиті 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) {
        ...
    }

     // геттери та сеттери
   ....
}
  • рядки 8–14: поля відповідають стовпцям таблиці [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 та setter
   ...
}
  • рядки 8–13: поля відповідають стовпцям таблиці [COTISATIONS];

4.3. Встановлення веб-сервера / jSON

4.3.1. Встановлення

Вам надано двійковий файл Java веб-сервера / jSON:

 

Щоб запустити веб-сервер / jSON, виконайте наступні дії:

  • запустіть SGBD MySQL;
  • переконайтеся, що BD [dbpam_hibernate] існують;
  • відкрийте вікно DOS;
  • перейдіть до папки з файлом jar;
  • введіть команду:
java -jar pam-server-01-all-1.0.jar

Це передбачає, що бінарний файл [java.exe] знаходиться у папці PATH на вашому комп’ютері. Якщо це не так, введіть повний шлях до [java.exe], наприклад:

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

З’являться записи журналу:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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/**] на обробник типу [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 [/**] обробник типу [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'
  • рядок 16: виявлено URL [/salaire/{SS}/{ht}/{jt}];
  • рядок 17: виявлено URL [/employes];

4.3.2. URL веб-сервісу/jSON

Веб-сервіс / jSON реалізовано за допомогою Spring MVC і він надає два 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) {

Веб-сервіс приймає такі два запити:

  • рядок 1: /employes: для отримання списку співробітників;
  • рядок 4: /salaire/SS/ht/jt: для отримання відомості про заробітну плату співробітника з номером [SS], який відпрацював [ht] годин протягом [jt] днів;

Ось скріншоти, що це ілюструють.

Потрібні дані про таких працівників:

Image

Виконуємо очищення бази даних, перезапускаємо сервер і запитуємо співробітників:

Image

Запитуємо інформацію про зарплату:

Image

Запитуємо зарплату неіснуючої особи:

Image

4.3.3. Відповіді jSON від веб-сервісу/jSON

  

URL веб-сервісу / jSON надсилають відповіді типу [Response<T>]:


package client.android.dao.service;

import java.util.List;

public class Response<T> {

    // ----------------- властивості
    // статус операції
    private int status;
    // можливі повідомлення про стан
    private List<String> messages;
    // тіло відповіді
    private T body;

    // конструктори
    public Response() {

    }

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

    // методи getter та setter
...
}
  • URL [/employes] повертає тип Response<List<Employe>>;
  • URL та [/salaire] повертають тип Response<FeuilleSalaire>;

Клас [FeuilleSalaire] має такий вигляд:


package pam.entities;

import java.io.Serializable;

public class FeuilleSalaire implements Serializable {

    private static final long serialVersionUID = 1L;
    // приватні поля
    private Employe employe;
    private Cotisation cotisation;
    private ElementsSalaire elementsSalaire;

    // конструктори
    public FeuilleSalaire() {
    }

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

    // методи getter та setter
   ...
}
  • рядок 9: клас [Employe] було представлено в розділі 4.2.3;
  • рядок 10: клас [Cotisation] було представлено в розділі 4.2.3;

Клас [ElementsSalaire] (рядок 11) має такий вигляд:


package pam.entities;

import java.io.Serializable;

public class ElementsSalaire implements Serializable {

    private static final long serialVersionUID = 1L;
    // приватні поля
    private double salaireBase;
    private double cotisationsSociales;
    private double indemnitesEntretien;
    private double indemnitesRepas;
    private double salaireNet;

    // конструктори
    public ElementsSalaire() {

    }

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

    // методи getter та setter
    ...
}

4.4. Тестування клієнта Android

Ось готовий виконуваний файл клієнта для Android:

  

Перетягніть мишкою вищевказаний файл [pam-client.apk] на емулятор планшета [GenyMotion]. Він буде збережений, а потім запущений. Також запустіть веб-сервер / jSON, якщо це ще не зроблено. Клієнт для Android призначений для отримання інформації, що надсилається веб-сервером / jSON, та її форматування. Клієнт для Android має такі екрани:

Спочатку потрібно підключитися до веб-сервісу / jSON:

Image

  • у [1] вказується URL веб-сервісу / jSON. У емуляторі вкажіть одну з адрес IP або PC (але не 127.0.0.1). На планшеті введіть Wi-Fi-адресу комп’ютера, на якому працює веб-сервер / jSON, і вимкніть брандмауер сервера, якщо він є, оскільки він може блокувати вхідні запити;
  • на [2] ввійдіть у систему;

Після цього відкриється сторінка симуляції:

Image

  • у [3] вибираємо співробітника;
  • у [4] вкажіть кількість годин;
  • на сторінці [5] вказуємо кількість днів;
  • у [6] запускаємо симуляцію;

Отримана сторінка моделювання виглядає так:

Image

  • у [7] — отримане моделювання;
  • у [8] — її зберігають;

Image

  • у [9] — список моделювань;
  • у [10] видаляється симуляція;

Image

  • у [11], симуляцій більше немає;
  • у [12] — повернення до форми моделювання;

Image

  • у [13] знову з'являється форма;
  • у [14] — повернення на сторінку налаштувань;

Image

  • перейшовши за посиланням [15], ми повертаємося до початкової форми входу.

4.5. Завдання

Скелет клієнта для Android, представлений раніше, надано вам. Він створено на основі проєкту [client-android-skel], описаного в розділі 2.

  

Проєкт є готовим до запуску і вже містить необхідні екрани. Потрібно лише додати код, щоб додаток виконував свої функції. Послідовність дій така:

  • запустіть повну версію, щоб зрозуміти, що потрібно зробити;
  • запустіть спрощену версію та вивчіть її код. Він відповідає методам проектування, описаним на попередніх сторінках;
  • додайте відсутній код;