#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import sys import json import sqlite3 import pyexcel_ods from datetime import datetime from pathlib import Path class AccountingManager: """Gestionnaire des fichiers de comptabilité""" def __init__(self, db_path="invoices.db"): """Initialise le gestionnaire de comptabilité Args: db_path: Chemin vers la base de données SQLite """ self.db_path = db_path self.accounting_files = { "revenue": "compta.ods", "expenses": "spending.ods" } self.ensure_tables() def ensure_tables(self): """Assure que les tables nécessaires existent dans la base de données""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() # Table pour les revenus cursor.execute(''' CREATE TABLE IF NOT EXISTS accounting_revenue ( id INTEGER PRIMARY KEY AUTOINCREMENT, date TEXT NOT NULL, description TEXT NOT NULL, amount REAL NOT NULL, category TEXT, invoice_id INTEGER, notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (invoice_id) REFERENCES invoices(id) ) ''') # Table pour les dépenses cursor.execute(''' CREATE TABLE IF NOT EXISTS accounting_expenses ( id INTEGER PRIMARY KEY AUTOINCREMENT, date TEXT NOT NULL, description TEXT NOT NULL, amount REAL NOT NULL, category TEXT, payment_method TEXT, receipt_path TEXT, notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') conn.commit() conn.close() def import_revenue_data(self, file_path=None): """Importe les données de revenus depuis le fichier compta.ods Args: file_path: Chemin vers le fichier ODS (optionnel) Returns: int: Nombre d'entrées importées """ if file_path is None: file_path = self.accounting_files["revenue"] if not os.path.exists(file_path): print(f"Erreur: Fichier {file_path} introuvable") return 0 try: data = pyexcel_ods.get_data(file_path) # Supposons que la première feuille contient les données sheet_name = list(data.keys())[0] sheet_data = data[sheet_name] # Supposons que la première ligne contient les en-têtes headers = sheet_data[0] rows = sheet_data[1:] conn = sqlite3.connect(self.db_path) cursor = conn.cursor() count = 0 for row in rows: # Validation de base if len(row) < 3: # Au moins date, description, montant continue # Mappage des colonnes selon l'en-tête row_data = dict(zip(headers, row)) # Conversion des formats de date si nécessaire date_str = row_data.get('Date', '') if isinstance(date_str, str) and date_str: try: # Supposons le format JJ/MM/AAAA date_obj = datetime.strptime(date_str, '%d/%m/%Y') date_str = date_obj.strftime('%Y-%m-%d') except ValueError: # Garder la chaîne d'origine si la conversion échoue pass cursor.execute(''' INSERT INTO accounting_revenue (date, description, amount, category, notes) VALUES (?, ?, ?, ?, ?) ''', ( date_str, row_data.get('Description', ''), float(row_data.get('Montant', 0)), row_data.get('Catégorie', ''), row_data.get('Notes', '') )) count += 1 conn.commit() conn.close() return count except Exception as e: print(f"Erreur lors de l'importation des données: {e}") return 0 def import_expenses_data(self, file_path=None): """Importe les données de dépenses depuis le fichier spending.ods Args: file_path: Chemin vers le fichier ODS (optionnel) Returns: int: Nombre d'entrées importées """ if file_path is None: file_path = self.accounting_files["expenses"] if not os.path.exists(file_path): print(f"Erreur: Fichier {file_path} introuvable") return 0 try: data = pyexcel_ods.get_data(file_path) # Supposons que la première feuille contient les données sheet_name = list(data.keys())[0] sheet_data = data[sheet_name] # Supposons que la première ligne contient les en-têtes headers = sheet_data[0] rows = sheet_data[1:] conn = sqlite3.connect(self.db_path) cursor = conn.cursor() count = 0 for row in rows: # Validation de base if len(row) < 3: # Au moins date, description, montant continue # Mappage des colonnes selon l'en-tête row_data = dict(zip(headers, row)) # Conversion des formats de date si nécessaire date_str = row_data.get('Date', '') if isinstance(date_str, str) and date_str: try: # Supposons le format JJ/MM/AAAA date_obj = datetime.strptime(date_str, '%d/%m/%Y') date_str = date_obj.strftime('%Y-%m-%d') except ValueError: # Garder la chaîne d'origine si la conversion échoue pass cursor.execute(''' INSERT INTO accounting_expenses (date, description, amount, category, payment_method, notes) VALUES (?, ?, ?, ?, ?, ?) ''', ( date_str, row_data.get('Description', ''), float(row_data.get('Montant', 0)), row_data.get('Catégorie', ''), row_data.get('Méthode de Paiement', ''), row_data.get('Notes', '') )) count += 1 conn.commit() conn.close() return count except Exception as e: print(f"Erreur lors de l'importation des données: {e}") return 0 def get_balance_sheet(self, year=None): """Génère un bilan comptable Args: year: Année pour filtrer les résultats (optionnel) Returns: dict: Bilan avec revenus, dépenses et solde """ conn = sqlite3.connect(self.db_path) cursor = conn.cursor() date_filter = "" params = [] if year: date_filter = "WHERE date LIKE ?" params = [f"{year}%"] # Calculer le total des revenus cursor.execute(f''' SELECT SUM(amount) FROM accounting_revenue {date_filter} ''', params) total_revenue = cursor.fetchone()[0] or 0 # Calculer le total des dépenses cursor.execute(f''' SELECT SUM(amount) FROM accounting_expenses {date_filter} ''', params) total_expenses = cursor.fetchone()[0] or 0 # Obtenir la répartition par catégorie pour les revenus cursor.execute(f''' SELECT category, SUM(amount) as total FROM accounting_revenue {date_filter} GROUP BY category ORDER BY total DESC ''', params) revenue_by_category = {row[0] or 'Non catégorisé': row[1] for row in cursor.fetchall()} # Obtenir la répartition par catégorie pour les dépenses cursor.execute(f''' SELECT category, SUM(amount) as total FROM accounting_expenses {date_filter} GROUP BY category ORDER BY total DESC ''', params) expenses_by_category = {row[0] or 'Non catégorisé': row[1] for row in cursor.fetchall()} conn.close() return { 'total_revenue': total_revenue, 'total_expenses': total_expenses, 'balance': total_revenue - total_expenses, 'revenue_by_category': revenue_by_category, 'expenses_by_category': expenses_by_category } if __name__ == "__main__": # Usage comme script indépendant manager = AccountingManager() if len(sys.argv) > 1: command = sys.argv[1] if command == "import": print("Importation des données comptables...") rev_count = manager.import_revenue_data() exp_count = manager.import_expenses_data() print(f"Importation terminée: {rev_count} revenus et {exp_count} dépenses importés.") elif command == "balance": year = sys.argv[2] if len(sys.argv) > 2 else None balance = manager.get_balance_sheet(year) print("\n=== BILAN COMPTABLE ===") if year: print(f"Année: {year}") print(f"Total des revenus: {balance['total_revenue']:.2f} €") print(f"Total des dépenses: {balance['total_expenses']:.2f} €") print(f"Solde: {balance['balance']:.2f} €") print("\nRépartition des revenus par catégorie:") for cat, amount in balance['revenue_by_category'].items(): print(f" - {cat}: {amount:.2f} €") print("\nRépartition des dépenses par catégorie:") for cat, amount in balance['expenses_by_category'].items(): print(f" - {cat}: {amount:.2f} €") else: print(f"Commande inconnue: {command}") print("Utilisations possibles:") print(" - import: Importer les données des fichiers ODS") print(" - balance [année]: Afficher le bilan comptable") else: print("Veuillez spécifier une commande:") print(" - import: Importer les données des fichiers ODS") print(" - balance [année]: Afficher le bilan comptable")