invoice-manager/accounting_data/accounting_manager.py

312 lines
11 KiB
Python
Raw Normal View History

#!/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")