Skip to content

4. Assignment 1: Basic Paystub Management

4.1. Introduction

To apply what we’ve covered previously, we now propose an assignment involving the development of an Android client for tablets, designed to simulate payroll calculations for the employees of an association.

The application will have a client/server architecture:

Image

  • the server [1] is provided;
  • you must build the Android client [2].

4.2. The database

4.2.1. Definition

The static data needed to build the pay stub will be stored in a database that we will refer to hereafter as dbpam. This database contains the following tables:

EMPLOYEES table: contains information about the various child care providers

Structure:

ID
primary key
VERSION
version number – increments with each modification of the row
SS
Employee's Social Security number – unique
NAME
Employee's last name
FIRST NAME
first name
ADDRESS
their address
CITY
his/her city
ZIP CODE
his/her ZIP code
INDEMNITY_ID
Foreign key on the [ID] field of the [INDEMNITES] table

Its content could be as follows:

Image

COTISATIONS table: contains the percentages needed to calculate social security contributions

Structure:

ID
primary key
VERSION
version number – increments with each modification of the row
CSGRDS
Percentage: General Social Contribution + Contribution to Social Debt Repayment
CSGD
percentage: deductible general social contribution
SECU
percentage: social security, widowhood, old age
PENSION
percentage: supplemental pension + unemployment insurance

Its content could be as follows:

Image

Social security contribution rates are independent of the employee. The previous table has only one row.

ALLOWANCES table: contains the elements used to calculate the salary to be paid.
ID
primary key
VERSION
version number – increments with each modification of the row
INDEX
Processing index – unique
HOURLY RATE
Net price in euros for one hour of on-call duty
DAILY MAINTENANCE
daily allowance in euros per day of care
MEAL PER DAY
Meal allowance in euros per day of care
VACATION PAY
Paid vacation allowance. This is a percentage applied to the base salary.

Its content could be as follows:

Image

Note that allowances may vary from one child care provider to another. They are linked to a specific child care provider via their pay grade. For example, Ms. Marie Jouveinal, who has a pay grade of 2 (EMPLOYEES table), has an hourly wage of 2.1 euros (INDEMNITES table).

4.2.2. Generation

The database generation script [dbpam_hibernate.sql] is provided:

  

Create the [dbpam_hibernate] database (this is the name of the database used by the web server/jSON) and ensure that the root login (without a password) can access it. You can do this as follows:

Start MySQL, then [PhpMyAdmin]:

 
  • [1-2]: Import the [dbpam_hibernate.sql] script and then execute it;

4.2.3. Java modeling of the database

The elements of the [EMPLOYEES], [ALLOWANCES], and [CONTRIBUTIONS] tables are modeled by the following classes:

[ Employee]


package pam.entities;

import java.io.Serializable;

public class Employee implements Serializable {

  private static final long serialVersionUID = 1L;
  private Long id;
  private int version;
  private String SS;
  private String lastName;
  private String firstName;
  private String address;
  private String city;
  private String zipCode;
  private int allowanceID;
  private Indemnity indemnity;

  public Employee() {
  }

  public Employee(String lastName, String firstName, String middleName, String address, String city, String zipCode, Indemnity indemnity) {
    ...
  }
  // getters and setters
....
}
  • lines 8–15: these fields correspond to the columns in the [EMPLOYEES] table;
  • line 16: the [indemniteId] field corresponds to the [INDEMNITE_ID] column, which is the foreign key of the [EMPLOYEES] table;
  • line 17: the employee's allowance. This field is not always populated:
    • it is not filled in when requesting the URL [/employees],
    • it is when requesting the URL [/salary];

[ 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 index;
    private double baseTime;
    private double room, daily cleaning;
    private double meal per day;
    private double severancePay;

    public Indemnity() {
    }

    public Allowance(int index, double baseHour, double dailyMaintenance, double dailyMeal, double CPAllowance) {
        ...
    }

    // getters and setters
   ....
}
  • lines 8-14: the fields correspond to the columns of the [INDEMNITES] table;

[ Contribution]


package pam.entities;

import java.io.Serializable;

public class Contribution implements Serializable {

    private static final long serialVersionUID = 1L;
    private Long id;
    private int version;
    private double csgrds;
    private double csgd;
    private double socialsecurity;
    private double pension;

    public Contribution() {
    }

    public Contribution(double csgrds, double csgd, double secu, double retirement) {
        ...
    }
   // getters and setters
   ...
}
  • lines 8-13: the fields correspond to the columns of the [COTISATIONS] table;

4.3. Web server / JSON installation

4.3.1. Installation

The Java binary for the web/jSON server is provided:

 

To start the web/JSON server, proceed as follows:

  • Start the MySQL DBMS;
  • make sure the database [dbpam_hibernate] exists;
  • Open a command prompt window;
  • Navigate to the jar folder;
  • type the command:
java -jar pam-server-01-all-1.0.jar

This assumes that the [java.exe] executable is in your machine's PATH. If this is not the case, type the full path to [java.exe], for example:

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

Logs are displayed:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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 "{[/salary/{SS}/{ht}/{jt}],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public pam.restapi.PayrollResponse pam.restapi.PamController.getPayroll(java.lang.String, double, int)
2014-10-22 16:45:33.805  INFO 1868 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/employees],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public pam.restapi.EmployeesResponse pam.restapi.PamController.getEmployees()
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'
  • line 16: the URL [/salary/{SS}/{ht}/{jt}] is resolved;
  • line 17: the URL [/employees] is discovered;

4.3.2. Web service/JSON URLs

The web service / JSON is implemented by Spring MVC and exposes two URLs:


@RequestMapping(value = "/employees", method = RequestMethod.GET, content-type = "application/json; charset=UTF-8")
public EmployeesResponse getEmployees() {
...
@RequestMapping(value = "/salary/{SS}/{ht}/{jt}", method = RequestMethod.GET, response-type = "application/json; charset=UTF-8")
public PaystubResponse getPaystub(@PathVariable("SS") String SS, @PathVariable("ht") double ht, @PathVariable("jt") int jt) {

The web service accepts the following two URLs:

  • line 1: /employees: to retrieve the list of employees;
  • Line 4: /salary/SS/ht/jt: to retrieve the pay stub for employee #[SS] who worked [ht] hours over [jt] days;

Here are some screenshots illustrating this.

We query the employees:

Image

We back up the database, restart the server, and query the employees:

Image

We query a salary:

Image

We request the salary of a non-existent person:

Image

4.3.3. The JSON responses from the web service/JSON

  

The web service/jSON URLs return responses of type [Response<T>]:


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;
    // response body
    private T body;

    // constructors
    public Response() {

    }

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

    // getters and setters
...
}
  • The URL [/employees] returns a Response<List<Employee>>;
  • The URL [/salary] returns a Response<PayStub> type;

The [PayrollSheet] class is as follows:


package pam.entities;

import java.io.Serializable;

public class Payroll implements Serializable {

    private static final long serialVersionUID = 1L;
    // private fields
    private Employee employee;
    private Contribution contribution;
    private PayrollElements payrollElements;

    // constructors
    public Payroll() {
    }

    public Payroll(Employee employee, Contribution contribution, SalaryElements salaryElements) {
        ...
    }

    // getters and setters
   ...
}
  • line 9: the [Employee] class was introduced in section 4.2.3;
  • line 10: the [Contribution] class was introduced in section 4.2.3;

The [SalaryElements] class (line 11) is as follows:


package pam.entities;

import java.io.Serializable;

public class SalaryElements implements Serializable {

    private static final long serialVersionUID = 1L;
    // private fields
    private double baseSalary;
    private double socialSecurityContributions;
    private double maintenanceAllowance;
    private double mealAllowance;
    private double netSalary;

    // constructors
    public ElementsSalary() {

    }

    public ElementsSalary(double baseSalary, double socialContributions, double maintenanceAllowance, double mealAllowance, double netSalary) {
        ...
    }

    // getters and setters
    ...
}

4.4. Android client tests

The executable binary for the finished Android client is provided below:

  

Use your mouse to drag the [pam-client.apk] file above onto a tablet emulator [GenyMotion]. It will then be saved and executed. Also launch the web/JSON server if you haven’t already done so. The purpose of the Android client is to retrieve the information returned by the web/JSON server and format it. The different views of the Android client are as follows:

First, you must connect to the web service / JSON:

Image

  • in [1], enter the URL of the web/JSON service. With the emulator, enter one of the PC’s IP addresses (but not 127.0.0.1). With a tablet, enter the Wi-Fi address of the web/JSON server machine and disable the server’s firewall if it has one, as it may block incoming calls;
  • In [2], log in;

You will then be taken to the simulation page:

Image

  • In [3], select an employee;
  • In [4], enter a number of hours;
  • In [5], enter the number of days;
  • In [6], run the simulation;

The resulting simulation page is as follows:

Image

  • in [7], the simulation results;
  • in [8], save it;

Image

  • in [9], the list of simulations;
  • in [10], a simulation is deleted;

Image

  • in [11], there are no more simulations;
  • In [12], you return to the simulation form;

Image

  • in [13], you return to the form;
  • in [14], you return to the configuration page;

Image

  • in [15], you return to the initial login form.

4.5. Work to be done

The Android client skeleton presented earlier is provided to you. It was built from the [client-android-skel] project described in Section 2.

  

The project is executable and already contains the necessary views. You simply need to add code so that the application does what it is supposed to do. The procedure is as follows:

  • run the full version to understand the work to be done;
  • run the lightweight version and study its code. It follows the design methods used in the previous pages;
  • add the missing code;