312 lines
11 KiB
Python
312 lines
11 KiB
Python
#!/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") |