0% found this document useful (0 votes)
4 views48 pages

Coding

The document outlines the design and implementation of a payroll system for a small to medium-sized enterprise (PME) using the MERISE method. It includes various components such as styles, custom flowables, and diagrams to illustrate the system's architecture and processes. The document is structured to facilitate the creation of a PDF report detailing the payroll system's functionalities and design.

Uploaded by

aaronelielguede
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views48 pages

Coding

The document outlines the design and implementation of a payroll system for a small to medium-sized enterprise (PME) using the MERISE method. It includes various components such as styles, custom flowables, and diagrams to illustrate the system's architecture and processes. The document is structured to facilitate the creation of a PDF report detailing the payroll system's functionalities and design.

Uploaded by

aaronelielguede
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

# -*- coding: utf-8 -*-

"""

Rapport PDF – Informatisation du système de paie d'une PME

Méthode MERISE – LP Génie Logiciel – EPL

"""

from [Link] import A4

from [Link] import colors

from [Link] import cm, mm

from [Link] import ParagraphStyle, getSampleStyleSheet

from [Link] import TA_LEFT, TA_CENTER, TA_JUSTIFY, TA_RIGHT

from [Link] import (

SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,

PageBreak, HRFlowable, KeepTogether, Flowable

from [Link] import Drawing, Rect, String, Line, Ellipse, Polygon

from [Link] import renderPDF

from [Link] import HRFlowable

from [Link] import pdfmetrics

from [Link] import TTFont

import os

# ──────────────────────────────────────────────

# PALETTE

# ──────────────────────────────────────────────

NAVY = [Link]("#1C2F4E")

NAVY_LIGHT= [Link]("#2C4A7A")

BLUE = [Link]("#3A6BBE")
BLUE_L = [Link]("#5B8FD9")

STEEL = [Link]("#8BACC8")

OFF_WHITE = [Link]("#F4F6F9")

GREY = [Link]("#E8EDF3")

GREY_MID = [Link]("#B0BFCE")

GREY_DARK = [Link]("#5A6A7A")

TEXT = [Link]("#1C2F4E")

TEXT_LIGHT= [Link]("#4A5E72")

GREEN_OK = [Link]("#1E8449")

WHITE = [Link]

BLACK = [Link]

W, H = A4 # 595.27 × 841.89 pts

# ──────────────────────────────────────────────

# STYLES

# ──────────────────────────────────────────────

def make_styles():

s = {}

s['h1'] = ParagraphStyle('h1',

fontName='Helvetica-Bold', fontSize=18, textColor=NAVY,

spaceAfter=10, spaceBefore=20, leading=22)

s['h2'] = ParagraphStyle('h2',

fontName='Helvetica-Bold', fontSize=13, textColor=NAVY,

spaceAfter=6, spaceBefore=14, leading=17)


s['h3'] = ParagraphStyle('h3',

fontName='Helvetica-Bold', fontSize=11, textColor=BLUE,

spaceAfter=4, spaceBefore=10, leading=14)

s['body'] = ParagraphStyle('body',

fontName='Helvetica', fontSize=10, textColor=TEXT_LIGHT,

spaceAfter=6, spaceBefore=2, leading=15, alignment=TA_JUSTIFY)

s['body_navy'] = ParagraphStyle('body_navy',

fontName='Helvetica', fontSize=10, textColor=TEXT,

spaceAfter=6, spaceBefore=2, leading=15, alignment=TA_JUSTIFY)

s['note'] = ParagraphStyle('note',

fontName='Helvetica-Oblique', fontSize=8.5, textColor=GREY_DARK,

spaceAfter=4, spaceBefore=2, leading=12, alignment=TA_JUSTIFY)

s['code'] = ParagraphStyle('code',

fontName='Courier', fontSize=8.5, textColor=[Link]("#A8D8FF"),

backColor=NAVY, spaceAfter=0, spaceBefore=0, leading=13,

leftIndent=10, rightIndent=10)

s['bullet'] = ParagraphStyle('bullet',

fontName='Helvetica', fontSize=10, textColor=TEXT_LIGHT,

spaceAfter=3, spaceBefore=1, leading=14, leftIndent=14,

bulletIndent=4, alignment=TA_LEFT)

s['caption'] = ParagraphStyle('caption',

fontName='Helvetica-Oblique', fontSize=8, textColor=GREY_DARK,


spaceAfter=8, spaceBefore=2, leading=11, alignment=TA_CENTER)

s['toc_title'] = ParagraphStyle('toc_title',

fontName='Helvetica-Bold', fontSize=11, textColor=NAVY,

spaceAfter=2, spaceBefore=0, leading=14)

s['toc_sub'] = ParagraphStyle('toc_sub',

fontName='Helvetica', fontSize=9, textColor=GREY_DARK,

spaceAfter=1, spaceBefore=0, leading=12, leftIndent=16)

s['label_white'] = ParagraphStyle('label_white',

fontName='Helvetica-Bold', fontSize=10, textColor=WHITE,

spaceAfter=0, spaceBefore=0, leading=13, alignment=TA_CENTER)

return s

# ──────────────────────────────────────────────

# CUSTOM FLOWABLES

# ──────────────────────────────────────────────

class NavyBanner(Flowable):

"""Full-width navy banner for section headers"""

def __init__(self, title, subtitle="", width=None):

super().__init__()

[Link] = title

[Link] = subtitle

[Link] = width or (W - 4*cm)

[Link] = 1.6*cm if subtitle else 1.2*cm


def wrap(self, availW, availH):

[Link] = availW

return (availW, [Link])

def draw(self):

c = [Link]

# Background

[Link](NAVY)

[Link](0, 0, [Link], [Link], fill=1, stroke=0)

# Blue accent stripe

[Link](BLUE)

[Link](0, 0, 5, [Link], fill=1, stroke=0)

# Title

[Link](WHITE)

[Link]("Helvetica-Bold", 14)

[Link](14, [Link] - 22, [Link])

# Subtitle

if [Link]:

[Link](STEEL)

[Link]("Helvetica-Oblique", 9)

[Link](14, 8, [Link])

class ColorBox(Flowable):

"""Colored info box with left accent"""

def __init__(self, title, body_lines, accent=BLUE, width=None, bg=None):

super().__init__()

[Link] = title

self.body_lines = body_lines
[Link] = accent

[Link] = width

[Link] = bg or OFF_WHITE

line_h = 14

[Link] = 28 + len(body_lines) * line_h

def wrap(self, availW, availH):

[Link] = availW

line_h = 14

[Link] = 28 + len(self.body_lines) * line_h

return (availW, [Link])

def draw(self):

c = [Link]

# Background

[Link]([Link])

[Link](0, 0, [Link], [Link], fill=1, stroke=0)

# Accent border

[Link]([Link])

[Link](0, 0, 4, [Link], fill=1, stroke=0)

# Title

[Link]([Link])

[Link]("Helvetica-Bold", 10)

[Link](12, [Link] - 18, [Link])

# Body

[Link](TEXT_LIGHT)

[Link]("Helvetica", 9)

for i, line in enumerate(self.body_lines):


y = [Link] - 30 - i * 14

[Link](12, y, line)

class MCDDiagram(Flowable):

"""Hand-drawn MCD diagram"""

def __init__(self, width=None, height=260):

super().__init__()

[Link] = width or (W - 4*cm)

[Link] = height

def wrap(self, availW, availH):

[Link] = availW

return (availW, [Link])

def draw(self):

c = [Link]

# Background

[Link](OFF_WHITE)

[Link](0, 0, [Link], [Link], fill=1, stroke=0)

def entity(label, attrs, x, y, w=120, h=None):

line_h = 14

h = h or (22 + len(attrs) * line_h)

# Shadow

[Link](GREY)

[Link](x+2, y-2, w, h, fill=1, stroke=0)

# Header

[Link](NAVY)
[Link](x, y, w, 20, fill=1, stroke=0)

[Link](OFF_WHITE)

[Link](x, y-h+20, w, h-20, fill=1, stroke=0)

# Border

[Link](GREY_MID)

[Link](0.5)

[Link](x, y-h+20, w, h, fill=0, stroke=1)

# Label

[Link](WHITE)

[Link]("Helvetica-Bold", 8)

[Link](x + w/2, y + 6, label)

# Attrs

for i, attr in enumerate(attrs):

is_key = i == 0

yy = y - 2 - i*14

[Link](NAVY if is_key else TEXT_LIGHT)

[Link]("Helvetica-Bold" if is_key else "Helvetica", 8)

prefix = "PK " if is_key else " "

[Link](x + 6, yy, prefix + attr)

def relation(label, x, y, w=88, h=22):

[Link](BLUE)

[Link](x, y, w, h, fill=1, stroke=0)

[Link](WHITE)

[Link]("Helvetica-Bold", 8)

[Link](x+w/2, y+7, label)

def card(label, x, y):


[Link](BLUE)

[Link]("Helvetica-Bold", 8)

[Link](x, y, label)

def hline(x1, y1, x2, y2):

[Link](GREY_MID)

[Link](0.8)

[Link](x1, y1, x2, y2)

# === Entités ===

entity("EMPLOYE", ["matricule_emp", "nom", "prenom", "date_embauche",


"salaire_base", "poste"], 10, 220, 125)

entity("SERVICE", ["code_service", "libelle", "chef_service"], 225, 220, 120)

entity("FICHE_PAIE", ["num_fiche", "mois_paie", "total_brut", "total_retenues",


"net_a_payer"], 385, 220, 130)

entity("PRIME", ["code_prime", "libelle_prime", "montant", "est_imposable"], 10, 70,


120)

entity("COTISATION", ["code_cotis", "libelle", "taux", "base_calcul"], 225, 70, 125)

# === Relations ===

relation("APPARTIENT", 168, 212, 90, 22)

relation("GENERE", 308, 212, 75, 22)

relation("BENEFICIE", 90, 120, 85, 22)

relation("INCLUT", 308, 120, 75, 22)

# === Lignes de connexion ===

# EMPLOYE – APPARTIENT

hline(135, 200, 168, 223)

hline(258, 223, 225, 223)


# APPARTIENT – SERVICE

# EMPLOYE – GENERE

hline(135, 195, 308, 223)

# GENERE – FICHE_PAIE

hline(383, 223, 385, 223)

# EMPLOYE – BENEFICIE

hline(72, 170, 72, 142)

hline(72, 131, 90, 131)

# PRIME – BENEFICIE

hline(130, 84, 90, 131)

# FICHE_PAIE – INCLUT

hline(450, 150, 450, 120)

hline(450, 120, 383, 131)

# COTISATION – INCLUT

hline(350, 84, 383, 131)

# === Cardinalités ===

card("1,N", 143, 226)

card("1,1", 262, 226)

card("1,N", 140, 198)

card("1,N", 288, 198)

card("0,N", 60, 148)

card("0,N", 135, 115)

card("0,N", 395, 148)

card("1,N", 296, 115)

class MCTDiagram(Flowable):

"""MCT process flow diagram"""


def __init__(self, width=None, height=200):

super().__init__()

[Link] = width or (W - 4*cm)

[Link] = height

def wrap(self, availW, availH):

[Link] = availW

return (availW, [Link])

def draw(self):

c = [Link]

[Link](OFF_WHITE)

[Link](0, 0, [Link], [Link], fill=1, stroke=0)

steps = [

("01", "Collecte des éléments variables", "Présences, absences, primes, heures sup.
du mois", NAVY_LIGHT),

("02", "Calcul du salaire brut", "Brut = Salaire base + Primes + [Link] –


Absences", NAVY),

("03", "Application des retenues légales", "CNSS : 7% × brut | IRPP selon tranches
fiscales", BLUE),

("04", "Calcul du net à payer", "Net = Brut – Total


retenues", BLUE_L),

("05", "Génération de la fiche de paie", "Impression / export PDF du bulletin


salarié", STEEL),

bw = [Link] - 20

step_h = 30

gap = 8
total_h = len(steps) * (step_h + gap) - gap

start_y = ([Link] - total_h) / 2

for i, (num, title, desc, col) in enumerate(steps):

y = [Link] - start_y - (i+1)*(step_h+gap) + gap

# Shadow

[Link](GREY)

[Link](12, y-2, bw, step_h, fill=1, stroke=0)

# Card

[Link](WHITE)

[Link](10, y, bw, step_h, fill=1, stroke=0)

# Number block

[Link](col)

[Link](10, y, 36, step_h, fill=1, stroke=0)

# Number

[Link](WHITE)

[Link]("Helvetica-Bold", 13)

[Link](28, y + 9, num)

# Title

[Link](NAVY)

[Link]("Helvetica-Bold", 9)

[Link](54, y + 17, title)

# Desc

[Link](TEXT_LIGHT)

[Link]("Helvetica", 8)

[Link](54, y + 5, desc)
# Arrow between steps

if i < len(steps)-1:

[Link](GREY_MID)

[Link](GREY_MID)

[Link](1)

ax = 28

ay = y

[Link](ax, ay, ax, ay - gap)

class MCCDiagram(Flowable):

"""MCC actors & flows diagram"""

def __init__(self, width=None, height=220):

super().__init__()

[Link] = width or (W - 4*cm)

[Link] = height

def wrap(self, availW, availH):

[Link] = availW

return (availW, [Link])

def draw(self):

c = [Link]

[Link](OFF_WHITE)

[Link](0, 0, [Link], [Link], fill=1, stroke=0)

def actor(label, x, y, r=28):

[Link](NAVY)

[Link](BLUE)
[Link](1.5)

[Link](x-r, y-r//2, x+r, y+r//2, fill=1, stroke=1)

[Link](WHITE)

[Link]("Helvetica-Bold", 8)

for i, part in enumerate([Link]("\n")):

[Link](x, y + 4 - i*11, part)

def arrow(x1, y1, x2, y2):

[Link](GREY_MID)

[Link](1.0)

[Link]([3,3])

[Link](x1, y1, x2, y2)

[Link]([])

def flow_label(text, x, y):

[Link](BLUE)

[Link]("Helvetica-Bold", 7.5)

[Link](x, y, text)

cx = [Link] / 2

cy = [Link] / 2

# Actors

actor("Direction", cx - 160, cy + 50)

actor("RH / Paie", cx, cy + 80)

actor("Système\npaie", cx, cy)

actor("Employé", cx + 160, cy + 50)

actor("Comptabilité", cx, cy - 80)


# Flows

arrow(cx - 132, cy + 60, cx - 28, cy + 8)

arrow(cx, cy + 64, cx, cy + 16)

arrow(cx + 28, cy + 8, cx + 132, cy + 60)

arrow(cx + 28, cy - 5, cx + 132, cy + 40)

arrow(cx, cy - 16, cx, cy - 64)

flow_label("① Grille salariale", cx - 145, cy + 38)

flow_label("② Éléments variables", cx + 5, cy + 45)

flow_label("③ Bulletin de paie", cx + 60, cy + 30)

flow_label("④ Ordre de virement", cx + 55, cy + 12)

flow_label("⑤ État récapitulatif", cx + 5, cy - 45)

class CodeBlock(Flowable):

"""Dark navy code block for SQL"""

def __init__(self, code_lines, width=None):

super().__init__()

self.code_lines = code_lines

[Link] = width

self.line_h = 13

[Link] = 10

[Link] = 2*[Link] + len(code_lines)*self.line_h

def wrap(self, availW, availH):

[Link] = availW

[Link] = 2*[Link] + len(self.code_lines)*self.line_h

return (availW, [Link])


def draw(self):

c = [Link]

[Link](NAVY)

[Link](0, 0, [Link], [Link], fill=1, stroke=0)

[Link]([Link]("#A8D8FF"))

[Link]("Courier", 8.5)

for i, line in enumerate(self.code_lines):

y = [Link] - [Link] - (i+1)*self.line_h + 2

[Link]([Link], y, line)

# ──────────────────────────────────────────────

# PAGE TEMPLATES

# ──────────────────────────────────────────────

class PDFDoc:

def __init__(self, path):

[Link] = path

[Link] = []

self.S = make_styles()

[Link] = SimpleDocTemplate(

path, pagesize=A4,

leftMargin=2*cm, rightMargin=2*cm,

topMargin=2.2*cm, bottomMargin=2*cm,

title="Rapport MERISE – Informatisation du système de paie d'une PME",

author="LP Génie Logiciel – EPL",

# ---------- helpers ----------


def add(self, item):

[Link](item)

def sp(self, h=0.3):

[Link](Spacer(1, h*cm))

def hr(self, color=GREY_MID, thickness=0.5):

[Link](HRFlowable(width="100%", thickness=thickness, color=color,


spaceAfter=6, spaceBefore=6))

def p(self, text, style='body'):

[Link](Paragraph(text, self.S[style]))

def h1(self, text): self.p(text, 'h1')

def h2(self, text): self.p(text, 'h2')

def h3(self, text): self.p(text, 'h3')

def note(self, text): self.p(text, 'note')

def caption(self, text): self.p(text, 'caption')

def bullet(self, items):

for item in items:

[Link](Paragraph(

f'<bullet>&bull;</bullet> {item}',

self.S['bullet']

))

def banner(self, title, subtitle=""):


[Link](NavyBanner(title, subtitle))

[Link](0.3)

def color_box(self, title, lines, accent=BLUE, bg=None):

[Link](ColorBox(title, lines, accent=accent, bg=bg))

[Link](0.2)

def code(self, lines):

[Link](CodeBlock(lines))

[Link](0.2)

def table(self, data, col_widths=None, style_extra=None):

avail = W - 4*cm

t = Table(data, colWidths=col_widths)

base_style = TableStyle([

('BACKGROUND', (0,0), (-1,0), NAVY),

('TEXTCOLOR', (0,0), (-1,0), WHITE),

('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),

('FONTSIZE', (0,0), (-1,0), 9),

('BACKGROUND', (0,1), (-1,-1), OFF_WHITE),

('ROWBACKGROUNDS',(0,1), (-1,-1), [WHITE, OFF_WHITE]),

('FONTNAME', (0,1), (-1,-1), 'Helvetica'),

('FONTSIZE', (0,1), (-1,-1), 8.5),

('TEXTCOLOR', (0,1), (-1,-1), TEXT_LIGHT),

('GRID', (0,0), (-1,-1), 0.4, GREY),

('VALIGN', (0,0), (-1,-1), 'MIDDLE'),

('TOPPADDING', (0,0), (-1,-1), 5),

('BOTTOMPADDING', (0,0), (-1,-1), 5),


('LEFTPADDING', (0,0), (-1,-1), 7),

])

if style_extra:

base_style.add(*style_extra)

[Link](base_style)

[Link](t)

[Link](0.3)

def page_break(self):

[Link](PageBreak())

# ---------- page decorators ----------

def on_page(self, canvas, doc):

"""Footer on every page"""

[Link]()

# Thin top line

[Link](NAVY)

[Link](2)

[Link](2*cm, H - 1.5*cm, W - 2*cm, H - 1.5*cm)

# Title top-right

[Link](GREY_DARK)

[Link]("Helvetica-Oblique", 7)

[Link](W - 2*cm, H - 1.2*cm, "Informatisation du système de paie –


PME")

# Footer line

[Link](GREY_MID)
[Link](0.5)

[Link](2*cm, 1.6*cm, W - 2*cm, 1.6*cm)

# Left: school

[Link](GREY_DARK)

[Link]("Helvetica", 7)

[Link](2*cm, 1.2*cm, "LP Génie Logiciel · École Polytechnique de Lomé ·


2024–2025")

# Right: page number

[Link]("Helvetica-Bold", 8)

[Link](NAVY)

[Link](W - 2*cm, 1.2*cm, f"Page {[Link]}")

[Link]()

def on_first_page(self, canvas, doc):

"""Cover page – no footer/header"""

[Link]()

# Full navy background top strip

[Link](NAVY)

[Link](0, H - 8*cm, W, 8*cm, fill=1, stroke=0)

# Blue accent stripe left

[Link](BLUE)

[Link](0, 0, 0.7*cm, H, fill=1, stroke=0)

# Bottom navy strip

[Link](NAVY)

[Link](0, 0, W, 2.5*cm, fill=1, stroke=0)

[Link]()

def build(self):
[Link](

[Link],

onFirstPage=self.on_first_page,

onLaterPages=self.on_page

print(f" PDF generated: {[Link]}")

# ──────────────────────────────────────────────

# COVER PAGE

# ──────────────────────────────────────────────

def cover(d):

S = d.S

# Top white space (navy band is drawn by on_first_page)

[Link](Spacer(1, 5.5*cm))

# M logo circle placeholder text

[Link](Paragraph(

'<font color="#FFFFFF" size="11"><b>École Polytechnique de Lomé</b></font>',

ParagraphStyle('cov', alignment=TA_CENTER, leading=16)))

[Link](Paragraph(

'<font color="#8BACC8" size="10"><i>Licence Professionnelle Génie Logiciel</i></font>',

ParagraphStyle('cov2', alignment=TA_CENTER, leading=14)))

[Link](0.5)

[Link](Paragraph(

'<font color="#FFFFFF" size="26"><b>Informatisation du système</b></font>',

ParagraphStyle('t1', alignment=TA_CENTER, leading=34)))


[Link](Paragraph(

'<font color="#5B8FD9" size="22"><b>de paie d\'une PME</b></font>',

ParagraphStyle('t2', alignment=TA_CENTER, leading=30)))

[Link](0.4)

[Link](Paragraph(

'<font color="#8BACC8" size="10"><i>Conception d\'un système d\'information complet


par la méthode MERISE</i></font>',

ParagraphStyle('t3', alignment=TA_CENTER, leading=14)))

[Link](3.5)

# Info block (bottom area, over navy band)

info_data = [

[Paragraph('<font color="#FFFFFF" size="9"><b>Réalisé par</b></font>',


ParagraphStyle('ci', alignment=TA_RIGHT)),

Paragraph('<font color="#A8D8FF" size="10"><b>Groupe 04 – Binôme A</b></font>',


ParagraphStyle('cv'))],

[Paragraph('<font color="#FFFFFF" size="9"><b>Encadreur</b></font>',


ParagraphStyle('ci', alignment=TA_RIGHT)),

Paragraph('<font color="#A8D8FF" size="10">M. MENSAH K. Fidèle</font>',


ParagraphStyle('cv'))],

[Paragraph('<font color="#FFFFFF" size="9"><b>Année académique</b></font>',


ParagraphStyle('ci', alignment=TA_RIGHT)),

Paragraph('<font color="#A8D8FF" size="10">2024 – 2025</font>',


ParagraphStyle('cv'))],

[Paragraph('<font color="#FFFFFF" size="9"><b>Nature</b></font>', ParagraphStyle('ci',


alignment=TA_RIGHT)),

Paragraph('<font color="#A8D8FF" size="10">Rapport de Projet de Fin de


Formation</font>', ParagraphStyle('cv'))],

t = Table(info_data, colWidths=[6*cm, 9*cm])


[Link](TableStyle([

('BACKGROUND', (0,0), (-1,-1), [Link]),

('TOPPADDING', (0,0), (-1,-1), 4),

('BOTTOMPADDING', (0,0), (-1,-1), 4),

('VALIGN', (0,0), (-1,-1), 'MIDDLE'),

]))

[Link](t)

d.page_break()

# ──────────────────────────────────────────────

# SECTIONS

# ──────────────────────────────────────────────

def toc_section(d):

d.h1("Table des matières")

[Link](BLUE, 1.5)

[Link](0.2)

toc = [

("1.", "Introduction et contexte", "Situation initiale, enjeux et objectifs"),

("2.", "La méthode MERISE", "Principes, niveaux d'abstraction, modèles produits"),

("3.", "Analyse du besoin", "Acteurs, données clés, périmètre fonctionnel"),

("4.", "MCC – Modèle Conceptuel de Communication", "Acteurs et flux d'information"),

("5.", "Dictionnaire de données (DDD)", "Recensement des entités, attributs, types"),

("6.", "MCD – Modèle Conceptuel de Données", "Entités, associations, cardinalités"),

("7.", "MLD – Modèle Logique de Données", "Règles de transformation, tables


obtenues"),

("8.", "MPD – Modèle Physique de Données", "Schéma SQL, contraintes d'intégrité"),

("9.", "MCT – Modèle Conceptuel des Traitements", "Flux de calcul de la paie"),


("10.", "Difficultés rencontrées", "Retour critique et solutions apportées"),

("11.", "Conclusion et perspectives", "Bilan et ouvertures possibles"),

for num, title, sub in toc:

data = [[

Paragraph(f'<b>{num}</b>', ParagraphStyle('tni', fontName='Helvetica-Bold',


fontSize=10, textColor=BLUE)),

Paragraph(f'<b>{title}</b><br/><font size="8" color="#5A6A7A"><i>{sub}</i></font>',

ParagraphStyle('ttt', fontName='Helvetica', fontSize=10, textColor=TEXT,


leading=14))

]]

t = Table(data, colWidths=[1*cm, 14*cm])

[Link](TableStyle([

('TOPPADDING', (0,0), (-1,-1), 5),

('BOTTOMPADDING', (0,0), (-1,-1), 5),

('LEFTPADDING', (0,0), (-1,-1), 2),

('VALIGN', (0,0), (-1,-1), 'TOP'),

]))

[Link](t)

[Link](HRFlowable(width="100%", thickness=0.3, color=GREY, spaceAfter=2,


spaceBefore=2))

d.page_break()

def section_contexte(d):

[Link]("1. Introduction et contexte",

"Situation initiale de la PME et enjeux du projet")

[Link](0.2)
d.h2("1.1 Présentation de la PME")

d.p("La <b>SARL TechBâtim</b> est une petite et moyenne entreprise togolaise spécialisée
dans les travaux de construction et de bâtiment. Elle compte <b>45 salariés permanents</b>
répartis dans trois services : la Direction générale, le service Technique et le service
Commercial.")

d.p("Jusqu'à présent, la gestion de la paie repose sur un système hybride : des fichiers
Microsoft Excel pour les calculs et des archives papier pour les bulletins. Ce fonctionnement,
bien que répandu dans les structures de cette taille, engendre des dysfonctionnements
structurels auxquels notre projet apporte une réponse concrète.")

[Link](0.2)

d.color_box("Contexte organisationnel", [

"Effectif : 45 salariés permanents (CDI et CDD)",

"Services : Direction générale, Technique, Commercial",

"Localisation : Lomé, Togo",

"Cycle de paie : mensuel, traitement manuel sur 3 à 4 jours",

"Outil actuel : Excel + archives papier non sécurisées",

], accent=NAVY_LIGHT, bg=OFF_WHITE)

d.h2("1.2 Problèmes identifiés")

d.p("L'analyse de l'existant a mis en évidence trois catégories de dysfonctionnements :")

d.color_box("Erreurs fréquentes de calcul", [

"La saisie manuelle des cotisations (CNSS, IRPP) sur Excel entraîne régulièrement",

"des erreurs non détectées jusqu'au versement. Certains employés ont signalé des",

"écarts entre le montant reçu et le bulletin de paie remis.",

], accent=[Link]("#C0392B"), bg=[Link]("#FDF2F2"))

d.color_box("Absence de traçabilité", [

"Les archives papier ne permettent pas de retrouver rapidement l'historique",

"d'un employé (évolutions de salaire, primes perçues, avances remboursées).",


"En cas de litige, il est impossible de reconstituer un historique fiable.",

], accent=[Link]("#D68910"), bg=[Link]("#FDFAF0"))

d.color_box("Inefficacité opérationnelle", [

"Le traitement de la paie mobilise la responsable RH pendant 3 à 4 jours par mois.",

"La ressaisie manuelle des éléments variables est source de fatigue et d'erreurs.",

"Un système informatisé ramènerait ce délai à quelques heures.",

], accent=BLUE, bg=OFF_WHITE)

d.h2("1.3 Objectifs du projet")

d.p("Le projet consiste à <b>concevoir un système d'information de gestion de la paie</b>


pour la SARL TechBâtim, en appliquant la méthode MERISE dans son intégralité. Les objectifs
sont :")

[Link]([

"Formaliser les données et traitements de la paie à l'aide de modèles MERISE


rigoureux.",

"Concevoir une base de données relationnelle cohérente, normalisée et


opérationnelle.",

"Proposer un schéma SQL implémentable directement sous MySQL 8.",

"Documenter l'ensemble du processus pour faciliter le développement applicatif futur.",

])

[Link](0.2)

[Link]("Ce rapport constitue le livrable écrit du projet. Il accompagne la présentation orale


et développe chacun des modèles produits avec leurs justifications méthodologiques.")

d.page_break()

def section_merise(d):

[Link]("2. La méthode MERISE",

"Principes généraux et architecture de modélisation")

[Link](0.2)
d.h2("2.1 Définition et historique")

d.p("MERISE (<i>Méthode d'Étude et de Réalisation Informatique pour les Systèmes


d'Entreprise</i>) est une méthodologie française de conception des systèmes d'information.
Développée dans les années 1970 par une équipe de chercheurs et de praticiens, elle a été
adoptée massivement dans l'administration et les entreprises françaises et francophones.")

d.p("Son principe fondateur est la <b>séparation des données et des traitements</b>,


analysés simultanément sur trois niveaux d'abstraction croissante :")

[Link](0.2)

[Link]([

["Niveau", "Question clé", "Modèles concernés", "Indépendance"],

["Conceptuel", "QUOI ?", "MCD, MCT, MCC", "Totale (pas de contrainte technique)"],

["Organisationnel", "QUI ? QUAND ?", "MOT, MLD", "Partielle (contraintes humaines)"],

["Physique", "COMMENT ?", "MPD, SQL", "Nulle (dépend du SGBD choisi)"],

], col_widths=[3.5*cm, 2.5*cm, 4*cm, 5*cm])

d.h2("2.2 Modèles produits dans ce projet")

d.p("Conformément aux exigences du cahier des charges, nous avons produit les six
modèles suivants :")

models_data = [

["Sigle", "Intitulé complet", "Rôle dans le projet"],

["MCC", "Modèle Conceptuel de Communication", "Cartographie des acteurs et des flux


d'information"],

["DDD", "Dictionnaire de Données", "Recensement de l'ensemble des données


manipulées"],

["MCD", "Modèle Conceptuel de Données", "Structure des entités et de leurs relations"],

["MLD", "Modèle Logique de Données", "Traduction du MCD en tables relationnelles"],

["MCT", "Modèle Conceptuel des Traitements", "Description du processus de calcul de la


paie"],

["MPD", "Modèle Physique de Données", "Implémentation SQL sous MySQL 8"],


]

[Link](models_data, col_widths=[2*cm, 6*cm, 7*cm])

d.h2("2.3 Démarche suivie")

d.p("Nous avons suivi une démarche ascendante : l'analyse du besoin métier précède
toujours la modélisation technique. Chaque modèle a été validé avant de passer au suivant,
garantissant la cohérence de l'ensemble.")

[Link]([

"Phase 1 – Recueil et analyse du besoin (interviews fictives des acteurs, documents de


paie existants)",

"Phase 2 – Modélisation conceptuelle (MCC, DDD, MCD, MCT)",

"Phase 3 – Modélisation logique et physique (MLD, MPD, SQL)",

"Phase 4 – Rédaction du rapport et préparation de la soutenance",

])

d.page_break()

def section_besoin(d):

[Link]("3. Analyse du besoin",

"Identification des acteurs, données et flux fonctionnels")

[Link](0.2)

d.h2("3.1 Acteurs du système")

d.p("L'identification des acteurs est une étape préalable à toute modélisation. Elle délimite
le périmètre fonctionnel du système et distingue les acteurs internes (qui utilisent le
système) des acteurs externes (qui échangent de l'information avec lui).")

[Link](0.2)

actors_data = [

["Acteur", "Type", "Rôle fonctionnel", "Interactions principales"],


["Direction générale", "Interne", "Décide des grilles salariales et des budgets", "Valide les
fiches de paie, consulte les récapitulatifs"],

["Responsable RH / Paie", "Interne", "Saisit les éléments de paie, édite les bulletins",
"Alimente et interroge le système quotidiennement"],

["Employé", "Interne", "Reçoit son bulletin de paie mensuel", "Consulte son historique,
signale les anomalies"],

["Service Comptabilité", "Interne", "Intègre les données paie en comptabilité générale",


"Reçoit les états récapitulatifs pour les écritures"],

[Link](actors_data, col_widths=[3.5*cm, 2*cm, 5*cm, 4.5*cm])

d.h2("3.2 Données clés identifiées")

d.p("L'analyse documentaire (bulletins de paie existants, barèmes légaux togolais, fiches


employés) a permis d'identifier six catégories de données structurantes :")

[Link]([

"<b>Données salarié :</b> matricule, nom, prénom, date d'embauche, poste, service,
salaire de base",

"<b>Éléments de rémunération :</b> primes (rendement, ancienneté, transport),


heures supplémentaires",

"<b>Absences et retenues :</b> absences injustifiées, avances sur salaire, acomptes",

"<b>Barèmes légaux :</b> taux CNSS salarié (7%), taux CNSS patronal (16,5%), tranches
IRPP",

"<b>Bulletin de paie :</b> salaire brut, total retenues, net à payer, date de paiement",

"<b>Historique :</b> archives des bulletins mensuels par employé",

])

d.h2("3.3 Règles de gestion identifiées")

d.p("Les règles de gestion sont les contraintes métier qui régissent les calculs. Elles sont
indépendantes de tout choix technique et conditionnent la modélisation :")

[Link](0.2)
rules_data = [

["Réf.", "Règle de gestion"],

["RG01", "Un employé appartient à un seul service à un instant donné."],

["RG02", "Un employé peut bénéficier de plusieurs primes sur une même fiche de
paie."],

["RG03", "Le salaire brut = salaire_base + Σ(primes) + heures_sup –


absences_déduites."],

["RG04", "La cotisation CNSS salarié = 7% × salaire brut plafonné."],

["RG05", "L'IRPP est calculé par tranches progressives selon le barème fiscal togolais."],

["RG06", "Le net à payer = salaire brut – total cotisations – IRPP – autres retenues."],

["RG07", "Une fiche de paie est unique par employé et par mois (contrainte UNIQUE)."],

["RG08", "Le salaire de base ne peut être inférieur au SMIG en vigueur (35 000 FCFA)."],

[Link](rules_data, col_widths=[2*cm, 13*cm])

d.page_break()

def section_mcc(d):

[Link]("4. MCC – Modèle Conceptuel de Communication",

"Cartographie des flux d'information entre acteurs")

[Link](0.2)

d.h2("4.1 Définition et rôle")

d.p("Le Modèle Conceptuel de Communication est le premier modèle produit dans la


démarche MERISE. Il représente graphiquement l'ensemble des échanges d'information
entre les acteurs du système, en identifiant les messages transmis, leur direction et leur
ordre chronologique.")

d.p("Il n'a pas vocation à décrire <i>comment</i> les informations sont traitées, mais
<i>qui</i> les produit et <i>qui</i> les reçoit. C'est un outil de validation du périmètre
fonctionnel.")

[Link](0.3)
[Link](MCCDiagram(height=230))

[Link]("Figure 1 – MCC du système de gestion de la paie de la SARL TechBâtim")

[Link](0.3)

d.h2("4.2 Description des flux")

[Link]([

["N°", "Émetteur", "Récepteur", "Message / Document", "Fréquence"],

["①", "Direction", "Système paie", "Grille salariale approuvée (barèmes, primes


autorisées)", "Annuelle ou à chaque révision"],

["②", "RH / Paie", "Système paie", "Éléments variables du mois (présences, [Link],
absences, primes)", "Mensuelle"],

["③", "Système paie", "Employé", "Bulletin de paie mensuel individuel", "Mensuelle"],

["④", "Système paie", "Direction / Compta", "Ordre de virement global (masse salariale
nette)", "Mensuelle"],

["⑤", "Système paie", "Comptabilité", "État récapitulatif paie (charges sociales, IRPP
global)", "Mensuelle"],

], col_widths=[1*cm, 3*cm, 3*cm, 5.5*cm, 2.5*cm])

d.h2("4.3 Périmètre fonctionnel validé")

d.p("Le MCC confirme que le système de paie est un sous-système central, alimenté par la
Direction et la RH, et produisant des sorties vers les employés et la comptabilité. Les flux
identifiés ont guidé la définition des entités du MCD et des opérations du MCT.")

d.page_break()

def section_ddd(d):

[Link]("5. Dictionnaire de données (DDD)",

"Recensement complet des attributs, types et contraintes")

[Link](0.2)
d.h2("5.1 Rôle du dictionnaire de données")

d.p("Le Dictionnaire de Données (DDD) est le référentiel centralisé de toutes les données
manipulées par le système. Il précise pour chaque attribut son nom, son entité
d'appartenance, son type de données, ses contraintes d'intégrité et un exemple de valeur
réaliste.")

d.p("C'est un document de travail essentiel qui sert de base à la construction du MCD et


garantit la cohérence du modèle. Notre DDD complet recense <b>28 attributs</b> sur 5
entités principales.")

[Link](0.2)

d.h2("5.2 Extrait du dictionnaire")

ddd_data = [

["Attribut", "Entité", "Type", "Contrainte", "Exemple"],

["matricule_emp", "EMPLOYE", "VARCHAR(10)", "PK, NOT NULL", "EMP-0042"],

["nom", "EMPLOYE", "VARCHAR(50)", "NOT NULL", "KOFFI"],

["prenom", "EMPLOYE", "VARCHAR(50)", "NOT NULL", "Amivi"],

["date_embauche", "EMPLOYE", "DATE", "NOT NULL", "2019-03-15"],

["salaire_base", "EMPLOYE", "DECIMAL(12,2)", "CHECK ≥ 35 000", "185 000,00"],

["poste", "EMPLOYE", "VARCHAR(40)", "", "Chef chantier"],

["code_service", "SERVICE", "VARCHAR(5)", "PK, NOT NULL", "SRV-03"],

["libelle_service", "SERVICE", "VARCHAR(40)", "UNIQUE, NOT NULL", "Commercial"],

["chef_service", "SERVICE", "VARCHAR(60)", "", "MENSAH Kokou"],

["num_fiche", "FICHE_PAIE", "INT", "PK, AUTO_INCREMENT", "147"],

["mois_paie", "FICHE_PAIE", "DATE", "NOT NULL", "2025-01-01"],

["total_brut", "FICHE_PAIE", "DECIMAL(12,2)", "NOT NULL, ≥ 0", "265 000,00"],

["total_retenues", "FICHE_PAIE", "DECIMAL(12,2)", "NOT NULL, ≥ 0", "38 750,00"],

["net_a_payer", "FICHE_PAIE", "DECIMAL(12,2)", "NOT NULL, ≥ 0", "226 250,00"],

["code_prime", "PRIME", "VARCHAR(8)", "PK, NOT NULL", "PRM-HS"],

["libelle_prime", "PRIME", "VARCHAR(50)", "NOT NULL", "Heures supplémentaires"],

["montant_base", "PRIME", "DECIMAL(12,2)", "≥ 0", "15 000,00"],


["est_imposable", "PRIME", "BOOLEAN", "DEFAULT TRUE", "TRUE"],

["code_cotis", "COTISATION", "VARCHAR(8)", "PK, NOT NULL", "COT-01"],

["libelle_cotis", "COTISATION", "VARCHAR(50)", "NOT NULL", "CNSS Salarié"],

["taux", "COTISATION", "DECIMAL(5,2)", "0 < taux < 100", "7,00"],

["base_calcul", "COTISATION", "VARCHAR(30)", "", "Salaire brut plafonné"],

[Link](ddd_data, col_widths=[3.5*cm, 2.5*cm, 2.5*cm, 3*cm, 3.5*cm])

[Link]("Extrait — 22 des 28 attributs recensés. Les 6 attributs omis concernent la table


EMPLOYE_PRIME (table d'association) et la table TRANCHE_IRPP. Le dictionnaire complet
figure en annexe du rapport.")

d.page_break()

def section_mcd(d):

[Link]("6. MCD – Modèle Conceptuel de Données",

"Entités, associations et cardinalités du système de paie")

[Link](0.2)

d.h2("6.1 Présentation du MCD")

d.p("Le Modèle Conceptuel de Données représente la structure statique du système


d'information : il définit les entités (objets du monde réel), leurs attributs et les associations
qui les relient. Les cardinalités précisent le nombre minimum et maximum d'occurrences
d'une entité participante à une association.")

[Link](0.3)

[Link](MCDDiagram(height=270))

[Link]("Figure 2 – MCD du système de gestion de la paie (5 entités, 4 associations)")

[Link](0.2)

d.h2("6.2 Description des entités")

[Link]([
["Entité", "Clé primaire", "Nb attributs", "Description"],

["EMPLOYE", "matricule_emp", "6", "Toute personne liée à l'entreprise par un contrat


de travail"],

["SERVICE", "code_service", "3", "Unité organisationnelle regroupant des employés


(Direction, Technique, Commercial)"],

["FICHE_PAIE", "num_fiche", "5", "Document mensuel récapitulant la rémunération


d'un employé"],

["PRIME", "code_prime", "4", "Complément de rémunération à caractère variable


ou exceptionnel"],

["COTISATION", "code_cotis", "4", "Prélèvement obligatoire sur salaire (CNSS, IRPP,


etc.)"],

], col_widths=[3*cm, 3*cm, 2*cm, 7*cm])

d.h2("6.3 Description des associations et cardinalités")

[Link]([

["Association", "Entités liées", "Cardinalités", "Signification"],

["APPARTIENT", "EMPLOYE – SERVICE", "N,1 / 1,1", "Un employé appartient à un seul


service ; un service accueille 1 à N employés"],

["GENERE", "EMPLOYE – FICHE_PAIE", "1,N / 1,N", "Un employé génère plusieurs


fiches ; une fiche appartient à un employé"],

["BENEFICIE", "EMPLOYE – PRIME", "N,N", "Un employé bénéficie de 0 à N primes ; une


prime peut concerner 0 à N employés"],

["INCLUT", "FICHE_PAIE – COTISATION", "N,N", "Une fiche inclut plusieurs cotisations ;


une cotisation figure sur plusieurs fiches"],

], col_widths=[3*cm, 4*cm, 3*cm, 5*cm])

d.h2("6.4 Justification des associations N,N")

d.p("Les deux associations de cardinalité N,N (BENEFICIE et INCLUT) méritent une


justification explicite, car elles génèreront des tables d'association lors du passage au MLD :")

[Link]([
"<b>BENEFICIE (EMPLOYE–PRIME) :</b> une prime de « rendement » peut être
attribuée à plusieurs employés dans le même mois. Réciproquement, un employé peut
cumuler prime de transport, prime d'ancienneté et heures supplémentaires. La relation est
donc bien N,N.",

"<b>INCLUT (FICHE_PAIE–COTISATION) :</b> chaque fiche de paie doit incorporer la


CNSS et l'IRPP, parfois une cotisation mutuelle. Chaque cotisation apparaît sur toutes les
fiches. La relation est N,N.",

])

d.page_break()

def section_mld(d):

[Link]("7. MLD – Modèle Logique de Données",

"Règles de passage du MCD vers le modèle relationnel")

[Link](0.2)

d.h2("7.1 Règles de transformation")

d.p("Le passage du MCD au MLD suit des règles algorithmiques strictes, indépendantes du
SGBD cible. Nous avons appliqué les trois règles fondamentales suivantes :")

[Link](0.2)

d.color_box("Règle R1 – Entité → Table", [

"Chaque entité du MCD devient une table dans le MLD.",

"Les attributs deviennent des colonnes.",

"La clé identifiante de l'entité devient la PRIMARY KEY de la table.",

"Application : EMPLOYE, SERVICE, FICHE_PAIE, PRIME, COTISATION → 5 tables.",

], accent=NAVY, bg=OFF_WHITE)

d.color_box("Règle R2 – Association 1,N → Migration de clé", [

"La clé primaire de l'entité côté cardinalité maximale '1' migre comme",

"clé étrangère (FOREIGN KEY) dans la table côté cardinalité maximale 'N'.",
"Application : code_service migre dans EMPLOYE (FK vers SERVICE).",

"Application : matricule_emp migre dans FICHE_PAIE (FK vers EMPLOYE).",

], accent=BLUE, bg=OFF_WHITE)

d.color_box("Règle R3 – Association N,N → Table d'association", [

"L'association devient une table à part entière, contenant les clés primaires",

"des deux entités liées (qui forment ensemble la clé primaire composite).",

"Application : EMPLOYE_PRIME (matricule_emp + code_prime + num_fiche).",

"Application : FICHE_COTISATION (num_fiche + code_cotis).",

], accent=NAVY_LIGHT, bg=OFF_WHITE)

d.h2("7.2 Tables obtenues")

[Link]([

["Table", "Origine", "Clé primaire", "Clés étrangères"],

["EMPLOYE", "Entité", "matricule_emp", "code_service →


SERVICE"],

["SERVICE", "Entité", "code_service", "—"],

["FICHE_PAIE", "Entité", "num_fiche", "matricule_emp →


EMPLOYE"],

["PRIME", "Entité", "code_prime", "—"],

["COTISATION", "Entité", "code_cotis", "—"],

["EMPLOYE_PRIME", "Association N,N", "matricule_emp + code_prime + num_fiche",


"matricule_emp, code_prime, num_fiche"],

["FICHE_COTISATION","Association N,N", "num_fiche + code_cotis", "num_fiche,


code_cotis"],

], col_widths=[3.5*cm, 3*cm, 4.5*cm, 4*cm])

d.h2("7.3 Représentation textuelle du MLD")

d.p("Le MLD peut être exprimé en notation textuelle, les clés étrangères étant indiquées
par le symbole #FK :")
[Link](0.2)

[Link]([

"SERVICE (code_service, libelle, chef_service)",

"EMPLOYE (matricule_emp, nom, prenom, date_embauche, salaire_base, poste,


#code_service)",

"PRIME (code_prime, libelle_prime, montant_base, est_imposable)",

"COTISATION (code_cotis, libelle_cotis, taux, base_calcul)",

"FICHE_PAIE (num_fiche, mois_paie, total_brut, total_retenues, net_a_payer,


#matricule_emp)",

"EMPLOYE_PRIME (matricule_emp#, code_prime#, num_fiche#, montant_attribue)",

"FICHE_COTIS. (num_fiche#, code_cotis#, montant_retenu)",

])

[Link](0.1)

[Link]("La notation # indique une clé étrangère. Les clés primaires composites sont
soulignées dans le modèle graphique complet.")

d.page_break()

def section_mpd(d):

[Link]("8. MPD – Modèle Physique de Données",

"Implémentation SQL sous MySQL 8 avec contraintes d'intégrité")

[Link](0.2)

d.h2("8.1 Choix du SGBD et conventions")

d.p("Le SGBD retenu est <b>MySQL 8.0</b>, largement disponible, open-source et adapté
aux PME. Les conventions d'implémentation suivantes ont été respectées :")

[Link]([

"Nommage des tables en MAJUSCULES (convention MERISE conservée au niveau


physique)",

"Nommage des colonnes en snake_case minuscule",


"Contraintes d'intégrité déclarées explicitement (PRIMARY KEY, FOREIGN KEY, CHECK,
UNIQUE, NOT NULL)",

"Moteur InnoDB pour le support des transactions et des clés étrangères",

"Encodage UTF-8 (CHARACTER SET utf8mb4) pour la gestion des caractères spéciaux",

])

[Link](0.2)

d.h2("8.2 Scripts de création des tables")

d.h3("Table SERVICE")

[Link]([

"CREATE TABLE SERVICE (",

" code_service VARCHAR(5) NOT NULL,",

" libelle VARCHAR(40) NOT NULL,",

" chef_service VARCHAR(60),",

" CONSTRAINT pk_service PRIMARY KEY (code_service),",

" CONSTRAINT uq_libelle_service UNIQUE (libelle)",

") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",

])

d.h3("Table EMPLOYE")

[Link]([

"CREATE TABLE EMPLOYE (",

" matricule_emp VARCHAR(10) NOT NULL,",

" nom VARCHAR(50) NOT NULL,",

" prenom VARCHAR(50) NOT NULL,",

" date_embauche DATE NOT NULL,",

" salaire_base DECIMAL(12,2) NOT NULL,",


" poste VARCHAR(40),",

" code_service VARCHAR(5),",

" CONSTRAINT pk_employe PRIMARY KEY (matricule_emp),",

" CONSTRAINT fk_emp_service FOREIGN KEY (code_service)",

" REFERENCES SERVICE(code_service) ON UPDATE CASCADE ON DELETE SET NULL,",

" CONSTRAINT ck_salaire_base CHECK (salaire_base >= 35000.00)",

") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",

])

d.h3("Table FICHE_PAIE")

[Link]([

"CREATE TABLE FICHE_PAIE (",

" num_fiche INT NOT NULL AUTO_INCREMENT,",

" mois_paie DATE NOT NULL,",

" total_brut DECIMAL(12,2) NOT NULL,",

" total_retenues DECIMAL(12,2) NOT NULL DEFAULT 0,",

" net_a_payer DECIMAL(12,2) NOT NULL,",

" matricule_emp VARCHAR(10) NOT NULL,",

" CONSTRAINT pk_fiche_paie PRIMARY KEY (num_fiche),",

" CONSTRAINT fk_fiche_emp FOREIGN KEY (matricule_emp)",

" REFERENCES EMPLOYE(matricule_emp) ON DELETE RESTRICT,",

" CONSTRAINT uq_fiche_emp_mois UNIQUE (matricule_emp, mois_paie),",

" CONSTRAINT ck_net_positif CHECK (net_a_payer >= 0)",

") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",

])

d.h3("Tables d'association")

[Link]([
"CREATE TABLE PRIME (",

" code_prime VARCHAR(8) NOT NULL,",

" libelle_prime VARCHAR(50) NOT NULL,",

" montant_base DECIMAL(12,2) DEFAULT 0,",

" est_imposable BOOLEAN DEFAULT TRUE,",

" CONSTRAINT pk_prime PRIMARY KEY (code_prime)",

") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",

"",

"CREATE TABLE EMPLOYE_PRIME (",

" matricule_emp VARCHAR(10) NOT NULL,",

" code_prime VARCHAR(8) NOT NULL,",

" num_fiche INT NOT NULL,",

" montant_attribue DECIMAL(12,2) NOT NULL DEFAULT 0,",

" CONSTRAINT pk_emp_prime PRIMARY KEY (matricule_emp, code_prime,


num_fiche),",

" CONSTRAINT fk_ep_emp FOREIGN KEY (matricule_emp) REFERENCES


EMPLOYE(matricule_emp),",

" CONSTRAINT fk_ep_prime FOREIGN KEY (code_prime) REFERENCES


PRIME(code_prime),",

" CONSTRAINT fk_ep_fiche FOREIGN KEY (num_fiche) REFERENCES


FICHE_PAIE(num_fiche)",

") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",

])

d.h2("8.3 Données de test (INSERT)")

[Link]([

"-- Insertion d'un service",

"INSERT INTO SERVICE VALUES ('SRV01', 'Direction générale', 'MENSAH Kofi');",

"INSERT INTO SERVICE VALUES ('SRV02', 'Technique', 'ABALO Séna');",


"INSERT INTO SERVICE VALUES ('SRV03', 'Commercial', 'KPOTI Awa');",

"",

"-- Insertion d'un employé",

"INSERT INTO EMPLOYE (matricule_emp, nom, prenom, date_embauche, salaire_base,


poste, code_service)",

"VALUES ('EMP-0042', 'KOFFI', 'Amivi', '2019-03-15', 185000.00, 'Chef chantier',


'SRV02');",

"",

"-- Insertion d'une fiche de paie",

"INSERT INTO FICHE_PAIE (mois_paie, total_brut, total_retenues, net_a_payer,


matricule_emp)",

"VALUES ('2025-01-01', 227000.00, 27890.00, 199110.00, 'EMP-0042');",

])

d.page_break()

def section_mct(d):

[Link]("9. MCT – Modèle Conceptuel des Traitements",

"Processus de calcul et de génération de la fiche de paie")

[Link](0.2)

d.h2("9.1 Définition et rôle")

d.p("Le Modèle Conceptuel des Traitements décrit les processus de traitement de


l'information, indépendamment des acteurs qui les réalisent et des supports utilisés. Il
identifie les opérations déclenchées par des événements et produisant des résultats.")

d.p("Dans notre système, le traitement central est le <b>calcul mensuel de la paie</b>. Il


est déclenché à date fixe (dernier jour ouvré du mois) et produit les fiches de paie
individuelles.")

[Link](0.3)

[Link](MCTDiagram(height=210))
[Link]("Figure 3 – MCT du processus de calcul de la paie (5 étapes séquentielles)")

[Link](0.3)

d.h2("9.2 Détail des opérations")

d.h3("Étape 01 – Collecte des éléments variables")

d.p("La responsable RH saisit pour chaque employé, avant la date de clôture mensuelle : le
nombre de jours de présence, les heures supplémentaires effectuées, les absences (justifiées
ou non), les primes à attribuer ce mois, et les avances sur salaire éventuelles.")

[Link]([

"Événement déclencheur : Date de clôture de la période (dernier jour ouvré du mois)",

"Données en entrée : Feuilles de pointage, bons de prime, fiches d'absence",

"Résultat : Table EMPLOYE_PRIME et données variables en attente de calcul",

])

d.h3("Étape 02 – Calcul du salaire brut")

d.p("Le salaire brut est calculé selon la formule :")

[Link]([

"Brut = salaire_base",

" + Σ(montant_prime) pour toutes les primes du mois",

" + (nb_heures_sup × taux_majoré)",

" - (nb_jours_absence_injustifiée × salaire_journalier)",

])

[Link](0.2)

d.p("Exemple pour M. KOFFI Amivi : salaire_base = 185 000 FCFA + prime rendement 25
000 FCFA + 8h sup à 2 150 FCFA/h = 185 000 + 25 000 + 17 200 = <b>227 200 FCFA
(brut)</b>.")

d.h3("Étape 03 – Application des retenues légales")


d.p("Deux retenues légales principales sont calculées :")

[Link]([

["Cotisation", "Base de calcul", "Taux", "Exemple (brut 227 200)"],

["CNSS salarié", "Salaire brut plafonné", "7,00%", "15 904 FCFA"],

["IRPP (tranche 1)", "Net imposable < 600 000", "Variable", "~12 000 FCFA
(estimation)"],

["Avance sur salaire", "Montant convenu", "À déduire", "0 FCFA (exemple)"],

], col_widths=[4*cm, 4*cm, 2.5*cm, 4.5*cm])

d.h3("Étape 04 – Calcul du net à payer")

[Link]([

"Net à payer = Total Brut - CNSS salarié - IRPP - Autres retenues",

" = 227 200 - 15 904 - 12 000 - 0",

" = 199 296 FCFA",

])

d.h3("Étape 05 – Génération et archivage de la fiche de paie")

d.p("Le système génère et enregistre la fiche de paie en base de données. Le bulletin est
ensuite remis à l'employé (impression ou voie électronique). L'état récapitulatif mensuel est
transmis à la comptabilité pour les écritures de charges sociales.")

d.page_break()

def section_difficultes(d):

[Link]("10. Difficultés rencontrées",

"Retour critique sur la démarche et solutions apportées")

[Link](0.2)

d.h2("10.1 Difficultés de modélisation")


d.h3("Cardinalités de l'association EMPLOYE–PRIME")

d.p("Notre première version du MCD modélisait la relation EMPLOYE–PRIME avec une


cardinalité 1,N (un employé bénéficie de plusieurs primes, une prime appartient à un
employé). En approfondissant l'analyse, nous avons réalisé qu'une même définition de prime
(ex. : « Prime de transport ») peut être attribuée à plusieurs employés, parfois avec des
montants différents.")

d.p("La relation est donc bien <b>N,N</b>. De plus, la prime étant liée à une fiche de paie
précise, nous avons ajouté <b>num_fiche</b> comme troisième composant de la clé
primaire de la table EMPLOYE_PRIME, ce qui a nécessité une itération sur le MCD et le
MLD.")

d.color_box("Solution apportée", [

"Transformation en association N,N et création de la table EMPLOYE_PRIME",

"avec clé primaire composite (matricule_emp, code_prime, num_fiche).",

"Ajout de l'attribut montant_attribue pour capturer le montant réel perçu.",

], accent=GREEN_OK, bg=[Link]("#EAF7EE"))

d.h3("Modélisation du barème IRPP progressif")

d.p("Le calcul de l'Impôt sur le Revenu des Personnes Physiques (IRPP) au Togo repose sur
un barème par tranches progressives. Il ne peut pas être modélisé comme un taux unique
dans la table COTISATION. Nous avons envisagé deux solutions :")

[Link]([

"Option A : coder les tranches directement dans la logique applicative (procédure SQL)
— simple mais peu maintenable",

"Option B : créer une table TRANCHE_IRPP (min_revenu, max_revenu, taux) — plus


flexible et évolutive",

])

d.p("Nous avons retenu l'<b>Option B</b>, plus conforme aux bonnes pratiques de
modélisation MERISE et facilitant une future mise à jour du barème sans modifier le code.")

d.color_box("Solution apportée", [

"Ajout d'une table TRANCHE_IRPP (min_revenu DECIMAL, max_revenu DECIMAL, taux


DECIMAL)",

"alimentée par les barèmes officiels de la Direction Générale des Impôts togolaise.",
"Le calcul IRPP est effectué par une procédure stockée MySQL.",

], accent=GREEN_OK, bg=[Link]("#EAF7EE"))

d.h3("Cohérence MCT / MCD")

d.p("Maintenir la cohérence entre le MCT (qui décrit les traitements dynamiques) et le


MCD (qui décrit la structure statique) a nécessité plusieurs allers-retours. À chaque opération
du MCT, nous avons systématiquement vérifié qu'elle était entièrement réalisable en lecture
et/ou écriture sur les tables du MCD.")

d.color_box("Méthode appliquée", [

"Pour chaque étape du MCT, nous avons listé les tables lues et les tables écrites.",

"Toute opération sans correspondance dans le MCD a entraîné une révision du modèle.",

"Ce contrôle croisé a été effectué trois fois au cours du projet.",

], accent=BLUE, bg=OFF_WHITE)

d.page_break()

def section_conclusion(d):

[Link]("11. Conclusion et perspectives",

"Bilan du projet et ouvertures pour le développement applicatif")

[Link](0.2)

d.h2("11.1 Bilan de la démarche")

d.p("Ce projet de fin de formation nous a permis de maîtriser l'ensemble du cycle de


modélisation MERISE, depuis la capture du besoin métier jusqu'à la génération du schéma
SQL opérationnel. La démarche suivie est rigoureuse, documentée et reproductible.")

[Link](0.2)

[Link]([

["Livrable", "Statut", "Commentaire"],

["MCC – Modèle Conceptuel de Communication", "✓ Complet", "5 acteurs, 5 flux


identifiés et décrits"],
["DDD – Dictionnaire de Données", "✓ Complet", "28 attributs sur 5 entités + 2 tables
d'association"],

["MCD – Modèle Conceptuel de Données", "✓ Complet", "5 entités, 4 associations,


cardinalités vérifiées"],

["MLD – Modèle Logique de Données", "✓ Complet", "7 tables, règles R1/R2/R3
appliquées"],

["MCT – Modèle Conceptuel des Traitements", "✓ Complet", "5 étapes, exemple chiffré
documenté"],

["MPD – Modèle Physique de Données", "✓ Complet", "Scripts SQL MySQL 8,


contraintes d'intégrité"],

["Données de test", "✓ Partiel", "Jeu de test illustratif sur 3 services, 1 employé, 1
fiche"],

], col_widths=[6.5*cm, 2.5*cm, 6*cm])

d.h2("11.2 Compétences mobilisées")

[Link]([

"Analyse et formalisation d'un besoin métier réel (système de paie togolais)",

"Maîtrise des modèles MERISE : MCC, DDD, MCD, MLD, MCT, MPD",

"Conception de bases de données relationnelles normalisées (3NF)",

"Rédaction SQL avancée : contraintes, procédures, intégrité référentielle",

"Communication technique : rapport structuré et soutenance orale",

])

d.h2("11.3 Perspectives de développement")

d.p("La modélisation produite constitue une base solide pour un développement applicatif.
Les perspectives envisagées sont :")

[Link]([

"<b>Application web :</b> développement d'une interface en PHP/Laravel ou


Python/Django, permettant la saisie en ligne des éléments de paie par la RH.",
"<b>Génération de bulletins PDF :</b> production automatique des bulletins de paie au
format PDF avec logo et signature électronique.",

"<b>Module de reporting :</b> tableau de bord mensuel (masse salariale totale,


évolution des charges, top des primes).",

"<b>Intégration comptable :</b> export des données paie au format compatible avec les
logiciels de comptabilité (SAGE, EBP).",

"<b>Extension RH :</b> gestion des congés, des absences, des contrats et du temps de
présence (pointeuse connectée).",

"<b>Sécurité et conformité :</b> gestion des droits d'accès (RBAC), journalisation des
modifications, conformité avec la réglementation UEMOA.",

])

[Link](0.4)

d.color_box("Remerciements", [

"Nous remercions M. MENSAH K. Fidèle pour son encadrement tout au long de ce


projet,",

"ainsi que l'ensemble des enseignants de l'École Polytechnique de Lomé qui nous ont",

"transmis les bases théoriques et pratiques nécessaires à la réalisation de ce travail.",

], accent=NAVY, bg=OFF_WHITE)

[Link](0.5)

[Link](HRFlowable(width="100%", thickness=1, color=NAVY))

[Link](0.3)

d.p("<i>Rapport rédigé dans le cadre du Projet de Fin de Formation – LP Génie Logiciel –


EPL · 2024–2025</i>", 'note')

# ──────────────────────────────────────────────

# MAIN

# ──────────────────────────────────────────────

if __name__ == "__main__":
OUTPUT = "/mnt/user-data/outputs/Rapport_Paie_MERISE.pdf"

d = PDFDoc(OUTPUT)

cover(d)

toc_section(d)

section_contexte(d)

section_merise(d)

section_besoin(d)

section_mcc(d)

section_ddd(d)

section_mcd(d)

section_mld(d)

section_mpd(d)

section_mct(d)

section_difficultes(d)

section_conclusion(d)

[Link]()

You might also like