import sqlite3 from datetime import datetime # ========================================== # 1. INITIALISATION DE LA BASE DE DONNÉES # ========================================== class DatabaseManager: def __init__(self, db_name="entreprise_erp.db"): self.conn = sqlite3.connect(db_name) self.cursor = self.conn.cursor() self.init_db() def init_db(self): # --- RH & SALAIRES --- self.cursor.execute(''' CREATE TABLE IF NOT EXISTS employes ( id INTEGER PRIMARY KEY AUTOINCREMENT, nom TEXT NOT NULL, prenom TEXT NOT NULL, poste TEXT NOT NULL, echelon INTEGER DEFAULT 1, solde_conges INTEGER DEFAULT 30, salaire_base REAL NOT NULL ) ''') self.cursor.execute(''' CREATE TABLE IF NOT EXISTS conges ( id INTEGER PRIMARY KEY AUTOINCREMENT, employe_id INTEGER, date_debut TEXT, date_fin TEXT, jours INTEGER, statut TEXT DEFAULT 'En attente', FOREIGN KEY(employe_id) REFERENCES employes(id) ) ''') self.cursor.execute(''' CREATE TABLE IF NOT EXISTS primes_notes ( id INTEGER PRIMARY KEY AUTOINCREMENT, employe_id INTEGER, type TEXT, -- 'PRIME' ou 'NOTE' valeur REAL, description TEXT, date TEXT, FOREIGN KEY(employe_id) REFERENCES employes(id) ) ''') self.cursor.execute(''' CREATE TABLE IF NOT EXISTS fiches_paie ( id INTEGER PRIMARY KEY AUTOINCREMENT, employe_id INTEGER, mois_annee TEXT, salaire_brut REAL, cotisations REAL, total_primes REAL, net_a_payer REAL, date_emission TEXT, FOREIGN KEY(employe_id) REFERENCES employes(id) ) ''') # --- COMPTABILITÉ & FINANCES --- self.cursor.execute(''' CREATE TABLE IF NOT EXISTS comptabilite ( id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT NOT NULL, -- 'RECETTE' ou 'DEPENSE' montant REAL NOT NULL, categorie TEXT NOT NULL, description TEXT, date TEXT NOT NULL ) ''') # --- INVENTAIRE & STOCKS --- self.cursor.execute(''' CREATE TABLE IF NOT EXISTS articles ( id INTEGER PRIMARY KEY AUTOINCREMENT, nom TEXT NOT NULL, quantite INTEGER DEFAULT 0, prix_unitaire REAL NOT NULL, seuil_alerte INTEGER DEFAULT 5 ) ''') # --- DEVIS ET FACTURES --- self.cursor.execute(''' CREATE TABLE IF NOT EXISTS documents_commerciaux ( id INTEGER PRIMARY KEY AUTOINCREMENT, client TEXT NOT NULL, type_doc TEXT NOT NULL, -- 'DEVIS' ou 'FACTURE' statut TEXT DEFAULT 'Brouillon', -- 'Accepté', 'Payé', 'Annulé' montant_ht REAL DEFAULT 0, tva REAL DEFAULT 20.0, montant_ttc REAL DEFAULT 0, date TEXT NOT NULL ) ''') self.cursor.execute(''' CREATE TABLE IF NOT EXISTS lignes_document ( id INTEGER PRIMARY KEY AUTOINCREMENT, doc_id INTEGER, article_id INTEGER, quantite INTEGER, prix_unitaire REAL, FOREIGN KEY(doc_id) REFERENCES documents_commerciaux(id), FOREIGN KEY(article_id) REFERENCES articles(id) ) ''') self.conn.commit() # ========================================== # 2. MODULE RH & GESTION DES SALAIRES # ========================================== class GestionRH: def __init__(self, db: DatabaseManager, finances): self.db = db self.finances = finances def ajouter_employe(self, nom: str, prenom: str, poste: str, salaire_base: float): self.db.cursor.execute( "INSERT INTO employes (nom, prenom, poste, salaire_base) VALUES (?, ?, ?, ?)", (nom, prenom, poste, salaire_base) ) self.db.conn.commit() print(f"[RH] Employé {prenom} {nom} ajouté.") def demander_conge(self, employe_id: int, date_debut: str, date_fin: str, jours: int): self.db.cursor.execute("SELECT solde_conges FROM employes WHERE id = ?", (employe_id,)) res = self.db.cursor.fetchone() if res and res[0] >= jours: self.db.cursor.execute( "INSERT INTO conges (employe_id, date_debut, date_fin, jours) VALUES (?, ?, ?, ?)", (employe_id, date_debut, date_fin, jours) ) self.db.cursor.execute( "UPDATE employes SET solde_conges = solde_conges - ? WHERE id = ?", (jours, employe_id) ) self.db.conn.commit() print(f"[RH] Congé de {jours} jours accordé.") else: print("[RH] Erreur : Solde de congé insuffisant.") def ajouter_prime_ou_note(self, employe_id: int, type_entree: str, valeur: float, description: str): date_actuelle = datetime.now().strftime("%Y-%m-%d") self.db.cursor.execute( "INSERT INTO primes_notes (employe_id, type, valeur, description, date) VALUES (?, ?, ?, ?, ?)", (employe_id, type_entree, valeur, description, date_actuelle) ) self.db.conn.commit() print(f"[RH] {type_entree} enregistrée pour l'employé ID {employe_id}.") def generer_fiche_paie(self, employe_id: int, mois_annee: str, taux_cotisations: float = 0.22): self.db.cursor.execute("SELECT nom, prenom, salaire_base FROM employes WHERE id = ?", (employe_id,)) emp = self.db.cursor.fetchone() if not emp: print("[RH] Employé introuvable.") return nom, prenom, salaire_base = emp # Calcul des primes du mois self.db.cursor.execute( "SELECT SUM(valeur) FROM primes_notes WHERE employe_id = ? AND type = 'PRIME'", (employe_id,) ) total_primes = self.db.cursor.fetchone()[0] or 0.0 salaire_brut = salaire_base + total_primes cotisations = salaire_brut * taux_cotisations net_a_payer = salaire_brut - cotisations date_emission = datetime.now().strftime("%Y-%m-%d") # Enregistrement dans la base self.db.cursor.execute(''' INSERT INTO fiches_paie (employe_id, mois_annee, salaire_brut, cotisations, total_primes, net_a_payer, date_emission) VALUES (?, ?, ?, ?, ?, ?, ?) ''', (employe_id, mois_annee, salaire_brut, cotisations, total_primes, net_a_payer, date_emission)) self.db.conn.commit() # Automatisation : enregistrement de la dépense en comptabilité self.finances.enregistrer_transaction( "DEPENSE", net_a_payer, "Salaires", f"Salaire {mois_annee} pour {prenom} {nom}" ) print(f"\n--- FICHE DE PAIE GENEREE ({mois_annee}) ---") print(f"Employé : {prenom} {nom}") print(f"Salaire Base : {salaire_base:.2f} €") print(f"Primes : {total_primes:.2f} €") print(f"Cotisations : -{cotisations:.2f} €") print(f"NET À PAYER : {net_a_payer:.2f} €\n") # ========================================== # 3. MODULE FINANCES & COMPTABILITÉ # ========================================== class GestionFinances: def __init__(self, db: DatabaseManager): self.db = db def enregistrer_transaction(self, type_trans: str, montant: float, categorie: str, description: str): date_actuelle = datetime.now().strftime("%Y-%m-%d %H:%M:%S") self.db.cursor.execute( "INSERT INTO comptabilite (type, montant, categorie, description, date) VALUES (?, ?, ?, ?, ?)", (type_trans.upper(), montant, categorie, description, date_actuelle) ) self.db.conn.commit() print(f"[COMPTA] Transaction {type_trans} de {montant:.2f} € enregistrée.") def obtenir_bilan(self): self.db.cursor.execute("SELECT SUM(montant) FROM comptabilite WHERE type = 'RECETTE'") total_recettes = self.db.cursor.fetchone()[0] or 0.0 self.db.cursor.execute("SELECT SUM(montant) FROM comptabilite WHERE type = 'DEPENSE'") total_depenses = self.db.cursor.fetchone()[0] or 0.0 solde = total_recettes - total_depenses print(f"\n--- BILAN FINANCIER GLOBAL ---") print(f"Recettes totales : {total_recettes:.2f} €") print(f"Dépenses totales : {total_depenses:.2f} €") print(f"Solde Net : {solde:.2f} €\n") # ========================================== # 4. MODULE FACTURATION (DEVIS & FACTURES) # ========================================== class GestionFacturation: def __init__(self, db: DatabaseManager, finances: GestionFinances, stock): self.db = db self.finances = finances self.stock = stock def creer_devis(self, client: str, articles_quantites: list) -> int: """ articles_quantites = [(article_id, quantite), ...] """ date_actuelle = datetime.now().strftime("%Y-%m-%d") self.db.cursor.execute( "INSERT INTO documents_commerciaux (client, type_doc, date) VALUES (?, 'DEVIS', ?)", (client, date_actuelle) ) doc_id = self.db.cursor.lastrowid montant_ht = 0.0 for article_id, qte in articles_quantites: self.db.cursor.execute("SELECT prix_unitaire FROM articles WHERE id = ?", (article_id,)) res = self.db.cursor.fetchone() if res: prix_u = res[0] montant_ht += prix_u * qte self.db.cursor.execute( "INSERT INTO lignes_document (doc_id, article_id, quantite, prix_unitaire) VALUES (?, ?, ?, ?)", (doc_id, article_id, qte, prix_u) ) tva = 20.0 montant_ttc = montant_ht * (1 + tva / 100) self.db.cursor.execute(""" UPDATE documents_commerciaux SET montant_ht = ?, tva = ?, montant_ttc = ? WHERE id = ? """, (montant_ht, tva, montant_ttc, doc_id)) self.db.conn.commit() print(f"[FACTURATION] Devis ID #{doc_id} créé pour {client}. Total TTC : {montant_ttc:.2f} €") return doc_id def convertir_devis_en_facture(self, devis_id: int) -> int: self.db.cursor.execute("SELECT client, montant_ht, tva, montant_ttc FROM documents_commerciaux WHERE id = ? AND type_doc = 'DEVIS'", (devis_id,)) devis = self.db.cursor.fetchone() if not devis: print("[FACTURATION] Devis non trouvé.") return client, m_ht, tva, m_ttc = devis date_actuelle = datetime.now().strftime("%Y-%m-%d") # Marquer devis comme accepté self.db.cursor.execute("UPDATE documents_commerciaux SET statut = 'Accepté' WHERE id = ?", (devis_id,)) # Créer la facture self.db.cursor.execute(""" INSERT INTO documents_commerciaux (client, type_doc, statut, montant_ht, tva, montant_ttc, date) VALUES (?, 'FACTURE', 'En attente', ?, ?, ?, ?) """, (client, m_ht, tva, m_ttc, date_actuelle)) facture_id = self.db.cursor.lastrowid # Dupliquer les lignes du devis sur la facture self.db.cursor.execute("SELECT article_id, quantite, prix_unitaire FROM lignes_document WHERE doc_id = ?", (devis_id,)) lignes = self.db.cursor.fetchall() for art_id, qte, prix_u in lignes: self.db.cursor.execute( "INSERT INTO lignes_document (doc_id, article_id, quantite, prix_unitaire) VALUES (?, ?, ?, ?)", (facture_id, art_id, qte, prix_u) ) self.db.conn.commit() print(f"[FACTURATION] Devis #{devis_id} converti en Facture #{facture_id}.") return facture_id def payer_facture(self, facture_id: int): self.db.cursor.execute("SELECT client, montant_ttc, statut FROM documents_commerciaux WHERE id = ? AND type_doc = 'FACTURE'", (facture_id,)) facture = self.db.cursor.fetchone() if not facture or facture[2] == 'Payé': print("[FACTURATION] Facture invalide ou déjà payée.") return client, m_ttc, _ = facture # 1. Mise à jour statut self.db.cursor.execute("UPDATE documents_commerciaux SET statut = 'Payé' WHERE id = ?", (facture_id,)) # 2. Décrémentation du stock self.db.cursor.execute("SELECT article_id, quantite FROM lignes_document WHERE doc_id = ?", (facture_id,)) lignes = self.db.cursor.fetchall() for art_id, qte in lignes: self.stock.sortie_stock_directe(art_id, qte) # 3. Enregistrement Recette self.finances.enregistrer_transaction("RECETTE", m_ttc, "Vente Client", f"Règlement Facture #{facture_id} - Client {client}") self.db.conn.commit() print(f"[FACTURATION] Facture #{facture_id} payée avec succès !") # ========================================== # 5. MODULE INVENTAIRE & STOCKS # ========================================== class GestionStock: def __init__(self, db: DatabaseManager, finances: GestionFinances): self.db = db self.finances = finances def ajouter_article(self, nom: str, quantite: int, prix_unitaire: float, seuil_alerte: int = 5): self.db.cursor.execute( "INSERT INTO articles (nom, quantite, prix_unitaire, seuil_alerte) VALUES (?, ?, ?, ?)", (nom, quantite, prix_unitaire, seuil_alerte) ) self.db.conn.commit() print(f"[STOCK] Article '{nom}' ajouté (ID: {self.db.cursor.lastrowid}).") def sortie_stock_directe(self, article_id: int, quantite: int): self.db.cursor.execute("SELECT quantite, nom, seuil_alerte FROM articles WHERE id = ?", (article_id,)) article = self.db.cursor.fetchone() if article and article[0] >= quantite: nouvelle_qte = article[0] - quantite self.db.cursor.execute("UPDATE articles SET quantite = ? WHERE id = ?", (nouvelle_qte, article_id)) if nouvelle_qte <= article[2]: print(f"⚠️ ALERTE STOCK : Niveau bas pour '{article[1]}' ({nouvelle_qte} restants).") else: print(f"❌ Stock insuffisant pour l'article ID {article_id}.") # ========================================== # 6. EXÉCUTION DU SYSTÈME COMPLET # ========================================== if __name__ == "__main__": db = DatabaseManager() # Initialisation croisée des modules finances = GestionFinances(db) stock = GestionStock(db, finances) rh = GestionRH(db, finances) facturation = GestionFacturation(db, finances, stock) print("=== DÉMONSTRATION DU SYSTÈME INTEGRÉ (ERP) ===\n") # 1. Enregistrement d'articles en stock stock.ajouter_article("Ecran 27 pouces", quantite=20, prix_unitaire=250.0, seuil_alerte=3) stock.ajouter_article("Clavier Mécanique", quantite=50, prix_unitaire=80.0, seuil_alerte=10) print("-" * 50) # 2. Cycle Facturation : Devis -> Facture -> Paiement devis_id = facturation.creer_devis(client="Société ABC", articles_quantites=[(1, 2), (2, 5)]) facture_id = facturation.convertir_devis_en_facture(devis_id) facturation.payer_facture(facture_id) # Déclenche la sortie de stock et la recette financière print("-" * 50) # 3. Cycle RH : Employé -> Prime -> Fiche de paie rh.ajouter_employe("Martin", "Sophie", "Cheffe de Projet", 3500.0) rh.ajouter_prime_ou_note(employe_id=1, type_entree="PRIME", valeur=400.0, description="Objectif T1 atteint") rh.generer_fiche_paie(employe_id=1, mois_annee="03/2026") # Déclenche la dépense financière print("-" * 50) # 4. Consultation du bilan comptable consolidé finances.obtenir_bilan()