Skip to content

4. TP 1: Gerenciamento básico de uma folha de pagamento

4.1. Introduction

Para aplicar o que foi visto anteriormente, propomos agora um trabalho que consiste em desenvolver um aplicativo Android para tablet, permitindo simular cálculos da folha de pagamento dos funcionários de uma associação.

O aplicativo terá uma arquitetura cliente/servidor:

Image

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

4.2. O banco de dados

4.2.1. Definição

Os dados estáticos necessários para gerar a folha de pagamento serão armazenados em um banco de dados que, doravante, designaremos como dbpam. Esse banco de dados possui as seguintes tabelas:

Tabela EMPLOYES: reúne informações sobre as diferentes babás

Estrutura:

ID
chave primária
VERSION
número de versão – aumenta a cada alteração na linha
SS
número de previdência social do funcionário – único
NOM
nome do funcionário
PRENOM
nome próprio
ADRESSE
seu endereço
VILLE
sua cidade
CODEPOSTAL
seu CEP
INDEMNITE_ID
chave estrangeira no campo [ID] da tabela [INDEMNITES]

Seu conteúdo poderia ser o seguinte:

Image

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

Estrutura:

ID
chave primária
VERSION
número de versão – aumenta a cada modificação da linha
CSGRDS
porcentagem: contribuição social generalizada + contribuição para o pagamento da dívida social
CSGD
porcentagem: contribuição social generalizada dedutível
SECU
porcentagem: previdência social, pensão de viuvez, aposentadoria
RETRAITE
porcentagem: aposentadoria complementar + seguro-desemprego

Seu conteúdo poderia ser o seguinte:

Image

As alíquotas das contribuições sociais são independentes do empregado. A tabela anterior possui apenas uma linha.

Tabela INDEMNITES: reúne os elementos que permitem o cálculo do salário a ser pago.
ID
chave primária
VERSION
número da versão – aumenta a cada modificação da linha
INDICE
índice de processamento – único
BASEHEURE
preço líquido em euros por hora de plantão
ENTRETIENJOUR
subsídio de manutenção em euros por dia de plantão
REPASJOUR
Subsídio de alimentação em euros por dia de plantão
INDEMNITESCP
Subsídio de férias remuneradas. Trata-se de uma porcentagem a ser aplicada ao salário-base.

Seu conteúdo poderia ser o seguinte:

Image

Observe-se que os subsídios podem variar de uma babá para outra. Na verdade, eles estão associados a uma babá específica por meio do índice salarial dela. Assim, a Sra. Marie Jouveinal, que possui um índice salarial de 2 (tabela EMPLOYES), tem um salário por hora de 2,1 euros (tabela INDEMNITES).

4.2.2. Geração

O script [dbpam_hibernate.sql] para geração do banco de dados é fornecido:

  

Crie o banco de dados [dbpam_hibernate] (esse é o nome do BD que o servidor web / jSON utiliza) e certifique-se de que o login root, sem senha, tenha acesso a ela. Você 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. Modelagem Java do banco 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: esses 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 indenização do funcionário. Esse campo nem sempre é preenchido:
    • não é preenchido 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 é fornecido a você:

 

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

  • execute o SGBD MySQL;
  • verifique se o BD [dbpam_hibernate] existe;
  • abra uma janela do DOS;
  • navegue até a pasta do arquivo .jar;
  • digite o comando:
java -jar pam-server-01-all-1.0.jar

Isso pressupõe que o binário [java.exe] esteja no diretório PATH do seu computador. Caso contrário, digite 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

Os registros são exibidos:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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}] é detectado;
  • linha 17: o URL [/employes] é detectado;

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: /empregados: para obter a lista de funcionários;
  • linha 4: /salário/SS/ht/jt: para obter a folha de pagamento do funcionário com o nº [SS] que trabalhou [ht] horas durante [jt] dias;

Aqui estão algumas capturas de tela que ilustram isso.

Solicita-se os funcionários:

Image

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

Image

Solicitamos um salário:

Image

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

Image

4.3.3. 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
    // status da operação
    private int status;
    // eventuais mensagens de status
    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 e o [/employes] retornam um tipo Response<List<Empregado>>;
  • o URL e o [/salaire] retornam 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

O binário executável do cliente Android finalizado é fornecido a você:

  

Com o mouse, arraste o arquivo binário [pam-client.apk] acima para um emulador de tablet [GenyMotion]. Ele será então salvo e executado. Inicie também o servidor web / jSON, caso ainda não tenha feito isso. O cliente Android tem como objetivo recuperar as informações enviadas pelo servidor web / jSON e formatá-las. As diferentes telas do cliente Android são as seguintes:

Primeiro, é necessário conectar-se ao serviço web / jSON:

Image

  • em [1], fornece-se o URL do serviço web / jSON. Com o emulador, insira um dos endereços IP ou PC (mas não 127.0.0.1). Em um tablet, insira o endereço Wi-Fi do servidor web / jSON e desative o firewall do servidor, caso haja um, pois ele pode bloquear as conexões de entrada;
  • no [2], faça o login;

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

Image

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

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

Image

  • em [7], a simulação obtida;
  • em [8], ela é salva;

Image

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

Image

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

Image

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

Image

  • em [15], voltamos ao formulário de login inicial.

4.5. Tarefa a ser realizada

A estrutura do cliente Android apresentada anteriormente já está à sua disposição. Ela foi criada a partir do projeto [client-android-skel] descrito no parágrafo 2.

  

O projeto é executável e já possui as visualizações necessárias. Basta adicionar código para que o aplicativo execute as tarefas necessárias. O procedimento a ser seguido é o seguinte:

  • execute a versão completa para compreender o trabalho a ser feito;
  • execute a versão simplificada e analise o código dela. Ele segue os métodos de projeto utilizados nas páginas anteriores;
  • adicione o código que falta;