Skip to content

4. TP 1: Maaş bordrosunun temel yönetimi

4.1. Introduction

Daha önce görülenleri uygulamak için, şimdi bir derneğin çalışanlarının maaş bordro hesaplamalarını simüle etmeyi sağlayan bir tablet Android istemcisi yazma ödevini öneriyoruz.

Uygulama, istemci/sunucu mimarisine sahip olacaktır:

Image

  • [1] sunucusu sağlanmıştır;
  • Android istemcisi [2]'i oluşturmanız gerekmektedir.

4.2. Veritabanı

4.2.1. Tanım

Maaş bordrosunu oluşturmak için gerekli statik veriler, bundan sonra dbpam olarak adlandıracağımız bir veritabanına yerleştirilecektir. Bu veritabanında aşağıdaki tablolar bulunmaktadır:

EMPLOYES tablosu: çeşitli çocuk bakıcıları hakkındaki bilgileri toplar

Yapı:

ID
birincil anahtar
VERSION
sürüm numarası – satır her değiştirildiğinde artar
SS
çalışanın sosyal güvenlik numarası – benzersiz
NOM
çalışanın adı
PRENOM
ad
ADRESSE
adresi
VILLE
şehri
CODEPOSTAL
posta kodu
INDEMNITE_ID
[INDEMNITES] tablosundaki [ID] alanında bulunan yabancı anahtar

İçeriği şu şekilde olabilir:

Image

COTISATIONS tablosu: sosyal güvenlik primlerinin hesaplanması için gerekli yüzdeleri içerir

Yapı:

ID
birincil anahtar
VERSION
sürüm numarası – satır her değiştirildiğinde artar
CSGRDS
yüzde: genel sosyal katkı payı + sosyal borç geri ödemesine katkı payı
CSGD
yüzde: indirilebilir genel sosyal katkı payı
SECU
yüzde: sosyal güvenlik, dul ve yaşlılık
RETRAITE
yüzde: ek emeklilik + işsizlik sigortası

İçeriği şu şekilde olabilir:

Image

Sosyal güvenlik prim oranları çalışandan bağımsızdır. Yukarıdaki tablo sadece bir satırdan oluşur.

INDEMNITES tablosu: ödenecek maaşın hesaplanmasına olanak tanıyan unsurları bir araya getirir.
ID
birincil anahtar
VERSION
sürüm numarası – satır her değiştirildiğinde artar
INDICE
işlem numarası – benzersiz
BASEHEURE
bir saatlik nöbetin net fiyatı (Euro)
ENTRETIENJOUR
bakım günü başına euro cinsinden bakım ücreti
REPASJOUR
bakım hizmeti verilen her gün için euro cinsinden yemek ödeneği
INDEMNITESCP
Ücretli izin ödeneği. Bu, temel maaşa uygulanacak bir yüzde oranıdır.

İçeriği şu şekilde olabilir:

Image

Ödeneklerin çocuk bakıcısından bakıcıya farklılık gösterebileceği unutulmamalıdır. Bu ödenekler, çocuk bakıcısının maaş endeksi aracılığıyla o bakıcıya özel olarak belirlenir. Örneğin, maaş endeksi 2 olan Bayan Marie Jouveinal’ın (EMPLOYES tablosu) saatlik ücreti 2,1 avrodur (INDEMNITES tablosu).

4.2.2. Oluşturma

Veritabanını oluşturmak için [dbpam_hibernate.sql] komut dosyası sağlanmıştır:

  

[dbpam_hibernate] veritabanını oluşturun (bu, web sunucusunun / jSON'in kullandığı BD'in adıdır) ve şifresiz root kullanıcı adının bu veritabanına erişebilmesini sağlayın. Bunun için şu adımları uygulayabilirsiniz:

MySQL'i ve ardından [PhpMyAdmin]'i başlatın:

 
  • [1-2]: [dbpam_hibernate.sql] komut dosyasını içe aktarın ve çalıştırın;

4.2.3. Veritabanının Java ile modellenmesi

[EMPLOYES], [INDEMNITES] ve [COTISATIONS] tablolarındaki öğeler aşağıdaki sınıflarla modellenmiştir:

[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) {
    ...
  }
   // getter ve setter'lar
....
}
  • 8-15. satırlar: bu alanlar, [EMPLOYES] tablosunun sütunlarına karşılık gelir;
  • 16. satır: [indemniteId] alanı, [INDEMNITE_ID] sütununa karşılık gelir; bu sütun, [EMPLOYES] tablosunun yabancı anahtarıdır;
  • 17. satır: çalışanın tazminatı. Bu alan her zaman doldurulmaz:
    • URL [/employes] talep edildiğinde doldurulmaz,
    • URL ve [/salaire] talep edildiğinde doldurulur;

[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) {
        ...
    }

     // getter ve setter'lar
   ....
}
  • 8-14. satırlar: alanlar, [INDEMNITES] tablosunun sütunlarına karşılık gelir;

[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 ve setter'lar
   ...
}
  • satır 8-13: alanlar, [COTISATIONS] tablosundaki sütunlara karşılık gelir;

4.3. Web sunucusunun kurulumu / jSON

4.3.1. Kurulum

Web sunucusunun Java ikili dosyası / jSON size sağlanmıştır:

 

Web sunucusunu / jSON başlatmak için aşağıdaki adımları izleyin:

  • SGBD MySQL dosyasını çalıştırın;
  • BD ve [dbpam_hibernate] dosyalarının mevcut olduğundan emin olun;
  • bir DOS penceresi açın;
  • jar dosyasının bulunduğu klasöre gidin;
  • şu komutu yazın:
java -jar pam-server-01-all-1.0.jar

Bu, [java.exe] ikili dosyasının bilgisayarınızdaki PATH klasöründe olduğunu varsayar. Aksi takdirde, [java.exe] dosyasının tam yolunu yazın, örneğin:

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

Günlükler görüntülenir:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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] türündeki işleyiciye
2014-10-22 16:45:33.839  INFO 1868 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] QZXW2HTML türündeki işleyici BW2NsYXNzIG9yZy5zcHJpbmdmcmFtZXdvcmsud2ViLnNlcnZsZXQucmVzb3VyY2UuUmVzb3VyY2VIdHRwUmVxdWVzdEhhbmRsZXJdZQX
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. satır: URL [/salaire/{SS}/{ht}/{jt}] bulundu;
  • 17. satır: URL ve [/employes] tespit edildi;

4.3.2. Web hizmeti/jSON'in URL'i

Web hizmeti / jSON, Spring MVC tarafından uygulanır ve iki URL sunar:


@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) {

Web hizmeti aşağıdaki iki URL'i kabul eder:

  • 1. satır: /employes: çalışan listesini almak için;
  • 4. satır: /maaş/SS/ht/jt: [SS] numaralı çalışanın, [ht] saat boyunca [jt] gün çalışmış olduğu maaş bordrosunu almak için;

Bunu gösteren ekran görüntüleri aşağıdadır.

Çalışanlardan istenen bilgiler:

Image

Veritabanını kapatıp sunucuyu yeniden başlattıktan sonra çalışanları sorguladığımızda:

Image

Maaş sorgulanıyor:

Image

Var olmayan bir kişinin maaşını soruyoruz:

Image

4.3.3. Web hizmetinden gelen yanıtlar: jSON / jSON

  

Web hizmetinden gelen URL / jSON, [Response<T>] türünde yanıtlar gönderir:


package client.android.dao.service;

import java.util.List;

public class Response<T> {

    // ----------------- özellikler
    // işlem durumu
    private int status;
    // olası durum mesajları
    private List<String> messages;
    // yanıtın gövdesi
    private T body;

    // yapıcılar
    public Response() {

    }

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

    // getter ve setter'lar
...
}
  • URL ve [/employes], Response<List<Employe>> türünde yanıtlar döndürür;
  • URL ve [/salaire], Response<FeuilleSalaire> türünde yanıt döndürür;

[FeuilleSalaire] sınıfı şu şekildedir:


package pam.entities;

import java.io.Serializable;

public class FeuilleSalaire implements Serializable {

    private static final long serialVersionUID = 1L;
    // özel alanlar
    private Employe employe;
    private Cotisation cotisation;
    private ElementsSalaire elementsSalaire;

    // yapıcılar
    public FeuilleSalaire() {
    }

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

    // getter ve setter'lar
   ...
}
  • 9. satır: [Employe] sınıfı 4.2.3. paragrafta tanıtılmıştır;
  • 10. satır: [Cotisation] sınıfı 4.2.3. paragrafta sunulmuştur;

[ElementsSalaire] sınıfı (satır 11) şu şekildedir:


package pam.entities;

import java.io.Serializable;

public class ElementsSalaire implements Serializable {

    private static final long serialVersionUID = 1L;
    // özel alanlar
    private double salaireBase;
    private double cotisationsSociales;
    private double indemnitesEntretien;
    private double indemnitesRepas;
    private double salaireNet;

    // yapıcılar
    public ElementsSalaire() {

    }

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

    // getter ve setter'lar
    ...
}

4.4. Android istemcisi testleri

Tamamlanmış Android istemcisinin yürütülebilir dosyası aşağıda verilmiştir:

  

Fare ile yukarıdaki [pam-client.apk] dosyasını bir tablet emülatörüne ([GenyMotion]) sürükleyip bırakın. Dosya kaydedilip çalıştırılacaktır. Henüz yapmadıysanız, web sunucusunu (jSON) da başlatın. Android istemcisinin amacı, / jSON web sunucusu tarafından geri gönderilen bilgileri almak ve bunları biçimlendirmektir. Android istemcisinin farklı görünümleri şunlardır:

Öncelikle / jSON web hizmetine bağlanmanız gerekir:

Image

  • [1] adresinde, /jSON web hizmetinin URL adresi verilir. Emülatörde, IP veya PC adreslerinden birini girin (ancak 127.0.0.1 adresini girmeyin). Bir tablet kullanıyorsanız, web sunucusunun cihazının Wi-Fi adresini / jSON girin ve sunucu cihazında varsa güvenlik duvarını devre dışı bırakın, çünkü gelen çağrıları engelleyebilir;
  • [2] adresinden bağlanın;

Böylece simülasyon sayfasına ulaşılır:

Image

  • [3]'te bir çalışan seçin;
  • [4]'te saat sayısını belirtin;
  • [5] adresinde, gün sayısını girin;
  • [6]'te simülasyon talebinde bulunulur;

Elde edilen simülasyon sayfası şöyledir:

Image

  • [7]'te, elde edilen simülasyon;
  • [8]'te, simülasyon kaydedilir;

Image

  • [9]'te simülasyon listesi;
  • [10]'te, bir simülasyon silinir;

Image

  • [11]'e, artık simülasyon kalmamıştır;
  • [12]'te, simülasyon formuna geri dönülür;

Image

  • [13]'te, forma geri dönülür;
  • [14]'te, yapılandırma sayfasına geri dönülür;

Image

  • [15]'e geldiğinizde, ilk oturum açma formuna geri dönersiniz.

4.5. Yapılması gerekenler

Daha önce sunulan Android istemcisinin iskeleti size verilmiştir. Bu iskelet, 2. paragrafta açıklanan [client-android-skel] projesinden oluşturulmuştur.

  

Proje çalıştırılabilir durumdadır ve gerekli görünümler zaten mevcuttur. Uygulamanın yapması gerekeni yerine getirebilmesi için sadece biraz kod eklenmesi gerekmektedir. İzlenecek prosedür şöyledir:

  • Yapılması gereken işi kavramak için tam sürümü çalıştırın;
  • hafifletilmiş sürümü çalıştırın ve kodunu inceleyin. Bu kod, önceki sayfalarda kullanılan tasarım yöntemlerine uygundur;
  • eksik kodu ekleyin;