Skip to content

4. TP 1: Gestão básica de uma folha de pagamento

4.1. Introduction

Para aplicar o que foi abordado anteriormente, propomos agora um trabalho que consiste em desenvolver uma aplicação Android para tablet, que permita simular os cálculos das folhas de pagamento dos funcionários de uma associação.

A aplicação terá uma arquitetura cliente/servidor:

Image

  • O servidor [1] já está disponível;
  • é necessário criar o cliente Android [2].

4.2. A base de dados

4.2.1. Definição

Os dados estáticos necessários para criar a folha de pagamento serão colocados numa base de dados a que, doravante, nos referiremos como dbpam. Esta base de dados contém as seguintes tabelas:

Tabela EMPLOYES: reúne informações sobre as diferentes amas

Estrutura:

ID
chave primária
VERSION
n.º de versão – aumenta a cada alteração da linha
SS
número de segurança social do colaborador – único
NOM
nome do funcionário
PRENOM
o seu nome próprio
ADRESSE
a sua morada
VILLE
a sua cidade
CODEPOSTAL
o seu código postal
INDEMNITE_ID
chave estrangeira no campo [ID] da tabela [INDEMNITES]

O seu conteúdo poderia ser o seguinte:

Image

Tabela COTISATIONS: reúne as percentagens necessárias para o cálculo das contribuições sociais

Estrutura:

ID
chave primária
VERSION
n.º de versão – aumenta a cada alteração da linha
CSGRDS
percentagem: contribuição social generalizada + contribuição para o reembolso da dívida social
CSGD
percentagem: contribuição social generalizada dedutível
SECU
percentagem: segurança social, pensão de viuvez, velhice
RETRAITE
percentagem: pensão complementar + seguro de desemprego

O seu conteúdo poderá ser o seguinte:

Image

As taxas das contribuições sociais são independentes do trabalhador. A tabela anterior tem apenas uma linha.

Tabela INDEMNITES: reúne os elementos que permitem o cálculo do salário a pagar.
ID
chave primária
VERSION
n.º de versão – aumenta a cada alteração da linha
INDICE
índice de processamento – único
BASEHEURE
preço líquido em euros por hora de serviço de guarda
ENTRETIENJOUR
Subsídio de manutenição em euros por dia de guarda
REPASJOUR
Subsídio de refeição em euros por dia de serviço
INDEMNITESCP
Subsídio de férias pagas. Trata-se de uma percentagem a aplicar ao salário base.

O seu conteúdo poderia ser o seguinte:

Image

Note-se que os subsídios podem variar de uma ama a outra. Estão, de facto, associados a uma ama específica através do seu índice salarial. Assim, a Sra. Marie Jouveinal, que tem um índice salarial de 2 (tabela EMPLOYES), tem um salário por hora de 2,1 euros (tabela INDEMNITES).

4.2.2. Geração

É fornecido o script [dbpam_hibernate.sql] para a geração da base de dados:

  

Crie a base de dados [dbpam_hibernate] (este é o nome da base de dados BD que o servidor web / jSON utiliza) e certifique-se de que o login root, sem palavra-passe, tenha acesso à mesma. Pode proceder da seguinte forma:

Inicie o MySQL e, em seguida, o [PhpMyAdmin]:

 
  • [1-2]: importe o script [dbpam_hibernate.sql] e, em seguida, execute-o;

4.2.3. Modelação Java da base de dados

Os elementos das tabelas [EMPLOYES], [INDEMNITES] e [COTISATIONS] são modelados pelas seguintes classes:

[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) {
    ...
  }
   // getters e setters
....
}
  • linhas 8-15: estes campos correspondem às colunas da tabela [EMPLOYES];
  • linha 16: o campo [indemniteId] corresponde à coluna [INDEMNITE_ID], que é a chave estrangeira da tabela [EMPLOYES];
  • linha 17: a indemnização do funcionário. Este campo nem sempre é preenchido:
    • não o está quando se solicita o URL [/employes],
    • mas está preenchido quando se solicita o 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) {
        ...
    }

     // getters e setters
   ....
}
  • linhas 8-14: os campos correspondem às colunas da tabela [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) {
        ...
    }
    // getters e setters
   ...
}
  • linhas 8-13: os campos correspondem às colunas da tabela [COTISATIONS];

4.3. Instalação do servidor web / jSON

4.3.1. Instalação

O binário Java do servidor web / jSON é-lhe fornecido:

 

Para iniciar o servidor web / jSON, proceda da seguinte forma:

  • inicie o SGBD MySQL;
  • certifique-se de que o BD [dbpam_hibernate] existe;
  • abra uma janela DOS;
  • navegue até à pasta do ficheiro .jar;
  • digite o comando:
java -jar pam-server-01-all-1.0.jar

Isto pressupõe que o ficheiro binário [java.exe] se encontra no PATH do seu computador. Se não for esse o caso, introduza o caminho completo para o [java.exe], por exemplo:

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

São apresentados registos:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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/**] para o manipulador do tipo [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 [/**] para o manipulador do tipo [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'
  • linha 16: o URL [/salaire/{SS}/{ht}/{jt}] é detetado;
  • linha 17: é detetado o URL [/employes];

4.3.2. Os URL do serviço web/jSON

O serviço web / jSON é implementado pelo Spring MVC e expõe dois 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) {

O serviço web aceita as duas seguintes URL:

  • linha 1: /employes: para obter a lista de funcionários;
  • linha 4: /salário/SS/ht/jt: para obter a folha de salário do funcionário com o n.º [SS] que trabalhou [ht] horas durante [jt] dias;

Eis algumas capturas de ecrã que ilustram isto.

Solicitam-se os funcionários:

Image

Desligamos a base de dados, reiniciamos o servidor e consultamos os funcionários:

Image

Solicita-se um salário:

Image

Solicita-se o salário de uma pessoa inexistente:

Image

4.3.3. As respostas jSON do serviço web/jSON

  

Os URL do serviço web / jSON enviam respostas do tipo [Response<T>]:


package client.android.dao.service;

import java.util.List;

public class Response<T> {

    // ----------------- propriedades
    // estado da operação
    private int status;
    // eventuais mensagens de estado
    private List<String> messages;
    // o corpo da resposta
    private T body;

    // construtores
    public Response() {

    }

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

    // getters e setters
...
}
  • o URL [/employes] devolve um tipo Response<List<Empregado>>;
  • o URL e o [/salaire] devolvem um tipo Response<FeuilleSalaire>;

A classe [FeuilleSalaire] é a seguinte:


package pam.entities;

import java.io.Serializable;

public class FeuilleSalaire implements Serializable {

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

    // construtores
    public FeuilleSalaire() {
    }

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

    // getters e setters
   ...
}
  • linha 9: a classe [Employe] foi apresentada no parágrafo 4.2.3;
  • linha 10: a classe [Cotisation] foi apresentada no parágrafo 4.2.3;

A classe [ElementsSalaire] (linha 11) é a seguinte:


package pam.entities;

import java.io.Serializable;

public class ElementsSalaire implements Serializable {

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

    // construtores
    public ElementsSalaire() {

    }

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

    // getters e setters
    ...
}

4.4. Testes do cliente Android

É-lhe fornecido o ficheiro binário executável do cliente Android finalizado:

  

Com o rato, arraste o ficheiro executável [pam-client.apk] acima para um emulador de tablet [GenyMotion]. Este será então guardado e, em seguida, executado. Inicie também o servidor web / jSON, caso ainda não o tenha feito. O cliente Android tem como objetivo recuperar as informações enviadas pelo servidor web / jSON e formatá-las. As diferentes vistas do cliente Android são as seguintes:

Em primeiro lugar, é necessário estabelecer ligação ao serviço web / jSON:

Image

  • em [1], introduz-se o URL do serviço web / jSON. Com o emulador, introduza um dos endereços IP do PC (mas não 127.0.0.1). Num tablet, introduza o endereço Wi-Fi do servidor web / jSON e desative a firewall do servidor, caso exista, pois esta pode bloquear as ligações de entrada;
  • no [2], faz-se a ligação;

Chega-se então à página de simulação:

Image

  • em [3], seleciona-se um funcionário;
  • em [4], indica-se um número de horas;
  • em [5], indica-se o número de dias;
  • em [6], solicita-se a simulação;

A página de simulação obtida é a seguinte:

Image

  • em [7], a simulação obtida;
  • em [8], guarda-se o resultado;

Image

  • em [9], a lista de simulações;
  • em [10], remove-se uma simulação;

Image

  • em [11], já não há simulações;
  • em [12], regressa-se ao formulário de simulação;

Image

  • em [13], volta-se ao formulário;
  • em [14], regressa-se à página de configuração;

Image

  • em [15], volta-se ao formulário de início de sessão inicial.

4.5. Trabalho a realizar

O esqueleto do cliente Android apresentado anteriormente é-lhe fornecido. Foi construído a partir do projeto [client-android-skel] descrito no parágrafo 2.

  

O projeto é executável e já possui as vistas necessárias. Basta adicionar código para que a aplicação execute as tarefas necessárias. O procedimento a seguir é o seguinte:

  • execute a versão completa para compreender o trabalho a realizar;
  • execute a versão simplificada e analise o seu código. Este respeita os métodos de conceção utilizados nas páginas anteriores;
  • adicione o código que falta;