first commit
This commit is contained in:
commit
e6c52820cd
227 changed files with 16156 additions and 0 deletions
43
core/data.py
Normal file
43
core/data.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import json
|
||||
import os
|
||||
|
||||
class Data:
|
||||
def __init__(self, file_path):
|
||||
"""
|
||||
Initialise la classe Data à partir d'un fichier JSON.
|
||||
|
||||
:param file_path: Chemin du fichier JSON.
|
||||
"""
|
||||
self.file_path = file_path
|
||||
|
||||
def save_data(self, data):
|
||||
"""
|
||||
Enregistre les données dans le fichier JSON.
|
||||
|
||||
:param data: Données à enregistrer.
|
||||
"""
|
||||
try:
|
||||
with open(self.file_path, 'w', encoding='utf-8') as file:
|
||||
# On remplace les caractères spéciaux par des espaces
|
||||
json.dump(data, file, ensure_ascii=False, indent=4)
|
||||
except Exception as e:
|
||||
print(f"Erreur lors de l'enregistrement des données : {e}")
|
||||
|
||||
def load_data(self):
|
||||
"""
|
||||
Charge les données à partir du fichier JSON.
|
||||
|
||||
:return: Données chargées.
|
||||
"""
|
||||
if not os.path.exists(self.file_path):
|
||||
print(f"Le fichier {self.file_path} n'existe pas.")
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(self.file_path, 'r', encoding='utf-8') as file:
|
||||
data = json.load(file)
|
||||
print(f"Données chargées avec succès depuis {self.file_path}")
|
||||
return data
|
||||
except Exception as e:
|
||||
print(f"Erreur lors du chargement des données : {e}")
|
||||
return None
|
||||
55
core/form.py
Normal file
55
core/form.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
class Form:
|
||||
def __init__(self, fields: list[dict]):
|
||||
"""
|
||||
Initialise la classe Form à partir d'une liste de champs.
|
||||
|
||||
:param fields: Liste de dictionnaire, chaque champs est défini comme :
|
||||
{
|
||||
"name": "client_name",
|
||||
"label": "Nom du client",
|
||||
"type": "text", # ou "number", "email", "select"...
|
||||
"options": ["Oui", "Non"], # facultatif, utilisé pour les champs de type "select"
|
||||
}
|
||||
"""
|
||||
self.fields = fields
|
||||
self.data = {}
|
||||
|
||||
def ask(self):
|
||||
print("Remplissez les informations suivantes :\n")
|
||||
for field in self.fields:
|
||||
name = field["name"]
|
||||
label = field.get("label", name.replace("_", " ").capitalize())
|
||||
field_type = field.get("type", "text")
|
||||
|
||||
value = None
|
||||
|
||||
if field_type == "select":
|
||||
options = field.get("options", [])
|
||||
print(f"{label} :")
|
||||
for i, option in enumerate(options):
|
||||
print(f" {i + 1}. {option}")
|
||||
while True:
|
||||
choice = input(f"Votre choix (numéro) : ")
|
||||
if choice.isdigit() and 1 <= int(choice) <= len(options):
|
||||
value = options[int(choice) - 1]
|
||||
break
|
||||
print("Choix invalide. Veuillez réessayer.")
|
||||
|
||||
elif field_type == "number":
|
||||
while True:
|
||||
value = input(f"{label} : ")
|
||||
if value.replace('.', '', 1).isdigit():
|
||||
value = float(value) if '.' in value else int(value)
|
||||
break
|
||||
print("Veuillez entrer un nombre valide.")
|
||||
|
||||
else:
|
||||
value = input(f"{label} : ")
|
||||
|
||||
self.data[name] = value
|
||||
|
||||
def get_data(self) -> dict:
|
||||
"""
|
||||
Retourne les données collectées sous forme de dictionnaire.
|
||||
"""
|
||||
return self.data
|
||||
254
core/generator.py
Normal file
254
core/generator.py
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
import json
|
||||
from fpdf import FPDF
|
||||
import os
|
||||
|
||||
class Generator:
|
||||
def __init__(self, form_data, template_path="Templates/proposition_commercial.json"):
|
||||
self.form_data = form_data
|
||||
self.template_path = template_path
|
||||
|
||||
def load_template(self):
|
||||
with open(self.template_path, 'r', encoding='utf-8') as file:
|
||||
template = json.load(file)
|
||||
return template
|
||||
|
||||
def replace_variables(self, content, visited=None):
|
||||
if visited is None:
|
||||
visited = set()
|
||||
|
||||
# Avoid infinite recursion by tracking visited objects
|
||||
if id(content) in visited:
|
||||
return content
|
||||
visited.add(id(content))
|
||||
|
||||
if isinstance(content, str):
|
||||
# Replace variables in a string
|
||||
for key, value in self.form_data.items():
|
||||
if key == "client" and isinstance(value, dict):
|
||||
# Handle client dictionary directly
|
||||
for sub_key, sub_value in value.items():
|
||||
placeholder = f"{{{sub_key}}}"
|
||||
content = content.replace(placeholder, str(sub_value))
|
||||
elif isinstance(value, dict):
|
||||
# Flatten nested dictionaries like 'client'
|
||||
for sub_key, sub_value in value.items():
|
||||
placeholder = f"{{{key}_{sub_key}}}"
|
||||
content = content.replace(placeholder, str(sub_value))
|
||||
elif isinstance(value, list):
|
||||
# Handle lists (e.g., features or services)
|
||||
if key == "features":
|
||||
formatted_features = "\n".join(f"- {item['description']}" for item in value if isinstance(item, dict) and 'description' in item)
|
||||
content = content.replace(f"{{{key}}}", formatted_features)
|
||||
elif key == "services":
|
||||
formatted_services = "\n".join(
|
||||
f"- {item['description']} : {item['prix_unitaire']} euros"
|
||||
for item in value if isinstance(item, dict) and 'description' in item and 'prix_unitaire' in item
|
||||
)
|
||||
content = content.replace(f"{{{key}}}", formatted_services)
|
||||
else:
|
||||
content = content.replace(f"{{{key}}}", str(value))
|
||||
return content
|
||||
elif isinstance(content, list):
|
||||
# Replace variables in each item of a list
|
||||
return [self.replace_variables(item, visited) for item in content]
|
||||
elif isinstance(content, dict):
|
||||
# Replace variables in a dictionary
|
||||
return {k: self.replace_variables(v, visited) for k, v in content.items()}
|
||||
return content
|
||||
|
||||
def generate_content(self):
|
||||
template = self.load_template()
|
||||
return self.replace_variables(template)
|
||||
|
||||
def generate_pdf(self, output_path=None):
|
||||
content = self.generate_content()
|
||||
|
||||
# Format the output file name based on the client name
|
||||
client_name = self.form_data.get("client_name", "client").replace(" ", "_").lower()
|
||||
if output_path is None:
|
||||
output_path = f"output/{client_name}_proposition_commercial.pdf"
|
||||
else:
|
||||
output_path = f"output/{output_path}/{client_name}_proposition_commercial.pdf"
|
||||
|
||||
pdf = FPDF()
|
||||
pdf.add_page()
|
||||
|
||||
# Ajouter un fond (image de fond)
|
||||
background_path = "Data/BG.png" # Chemin de l'image de fond
|
||||
if os.path.exists(background_path):
|
||||
pdf.image(background_path, x=0, y=0, w=210, h=297) # Dimensions pour une page A4
|
||||
|
||||
# Title
|
||||
title = content.get("title", "Proposition Commerciale")
|
||||
pdf.set_font("Helvetica", "B", 16)
|
||||
pdf.cell(0, 10, title, ln=True, align="C")
|
||||
pdf.ln(10)
|
||||
|
||||
# Main content
|
||||
pdf.set_font("Helvetica", "", 12)
|
||||
for section in content.get("sections", []):
|
||||
# Section header
|
||||
pdf.set_font("Helvetica", "B", 14)
|
||||
pdf.cell(0, 10, section.get("header", ""), ln=True)
|
||||
pdf.ln(5)
|
||||
|
||||
# Section content
|
||||
pdf.set_font("Helvetica", "", 12)
|
||||
section_content = section.get("content", [])
|
||||
if isinstance(section_content, list):
|
||||
for item in section_content:
|
||||
pdf.multi_cell(0, 10, str(item))
|
||||
pdf.ln(2)
|
||||
elif isinstance(section_content, str):
|
||||
pdf.multi_cell(0, 10, section_content)
|
||||
pdf.ln(5)
|
||||
|
||||
# Additional fields outside sections
|
||||
for key, value in content.items():
|
||||
if key not in ["sections", "title"]: # Exclure "sections" et "title"
|
||||
pdf.set_font("Helvetica", "B", 12)
|
||||
pdf.cell(0, 10, f"{key} :", ln=True)
|
||||
pdf.set_font("Helvetica", "", 12)
|
||||
|
||||
# Format the client data specially
|
||||
if key == "client" and isinstance(value, dict):
|
||||
client_str = "\n".join(f"{k.capitalize()}: {v}" for k, v in value.items())
|
||||
pdf.multi_cell(0, 10, client_str)
|
||||
else:
|
||||
pdf.multi_cell(0, 10, str(value))
|
||||
|
||||
pdf.ln(5)
|
||||
|
||||
# Ensure output folder exists
|
||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||
|
||||
pdf.output(output_path)
|
||||
print(f"PDF saved to {output_path}")
|
||||
|
||||
def generate_facture(self, output_path=None):
|
||||
content = self.generate_content()
|
||||
|
||||
# Format the output file name based on the client name
|
||||
# On récupère le nom du client dans le dictionnaire 'client'
|
||||
client = content.get("client", {})
|
||||
if isinstance(client, dict):
|
||||
client_name = client.get("nom", "client").replace(" ", "_").lower()
|
||||
else:
|
||||
# Fallback to a default name if 'client' is not a dictionary
|
||||
client_name = "client"
|
||||
print(f"Client name: {client_name}")
|
||||
|
||||
if output_path is None:
|
||||
output_path = f"output/{client_name}_devis.pdf"
|
||||
else:
|
||||
output_path = f"output/{output_path}/{client_name}_devis.pdf"
|
||||
|
||||
# Create PDF with invoice-like styling
|
||||
pdf = FPDF()
|
||||
pdf.add_page()
|
||||
|
||||
# Add background if exists
|
||||
background_path = "Data/BG.png"
|
||||
if os.path.exists(background_path):
|
||||
pdf.image(background_path, x=0, y=0, w=210, h=297)
|
||||
|
||||
# Document Title
|
||||
pdf.set_font("Helvetica", "B", 18)
|
||||
pdf.cell(0, 15, content.get("title", "Devis de service"), ln=True, align="C")
|
||||
|
||||
# Add a horizontal line
|
||||
pdf.line(10, pdf.get_y(), 200, pdf.get_y())
|
||||
pdf.ln(10)
|
||||
|
||||
# Header Info (Devis number and date)
|
||||
pdf.set_font("Helvetica", "B", 10)
|
||||
pdf.cell(95, 10, f"DEVIS N° : {content.get('numero', '')}", 0, 0)
|
||||
pdf.cell(95, 10, f"DATE : {content.get('date', '')}", 0, 1, 'R')
|
||||
pdf.ln(5)
|
||||
|
||||
# Client Information Box
|
||||
pdf.set_fill_color(240, 240, 240) # Light gray background
|
||||
pdf.rect(10, pdf.get_y(), 190, 30, style='F')
|
||||
|
||||
pdf.set_font("Helvetica", "B", 12)
|
||||
pdf.cell(0, 10, "INFORMATIONS CLIENT", 0, 1, 'L')
|
||||
|
||||
pdf.set_font("Helvetica", "", 10)
|
||||
client_data = content.get("client", {})
|
||||
if isinstance(client_data, dict):
|
||||
pdf.cell(40, 5, f"Nom:", 0, 0)
|
||||
pdf.cell(150, 5, f"{client_data.get('nom', '')}", 0, 1)
|
||||
|
||||
pdf.cell(40, 5, f"Email:", 0, 0)
|
||||
pdf.cell(150, 5, f"{client_data.get('email', '')}", 0, 1)
|
||||
|
||||
pdf.cell(40, 5, f"Téléphone:", 0, 0)
|
||||
pdf.cell(150, 5, f"{client_data.get('telephone', '')}", 0, 1)
|
||||
|
||||
pdf.cell(40, 5, f"Adresse:", 0, 0)
|
||||
pdf.cell(150, 5, f"{client_data.get('adresse', '')}", 0, 1)
|
||||
|
||||
pdf.ln(10)
|
||||
|
||||
# Services Table Header
|
||||
pdf.set_fill_color(60, 60, 60) # Dark gray for header
|
||||
pdf.set_text_color(255, 255, 255) # White text
|
||||
pdf.set_font("Helvetica", "B", 10)
|
||||
|
||||
# Table header
|
||||
pdf.cell(120, 10, "DESCRIPTION", 1, 0, 'C', 1)
|
||||
pdf.cell(35, 10, "PRIX UNITAIRE", 1, 0, 'C', 1)
|
||||
pdf.cell(35, 10, "MONTANT", 1, 1, 'C', 1)
|
||||
|
||||
# Reset color for table content
|
||||
pdf.set_text_color(0, 0, 0)
|
||||
pdf.set_font("Helvetica", "", 10)
|
||||
|
||||
# Services Table Content
|
||||
services = self.form_data.get("services", [])
|
||||
total = 0
|
||||
|
||||
if isinstance(services, list):
|
||||
for service in services:
|
||||
if isinstance(service, dict):
|
||||
description = service.get("description", "")
|
||||
prix = service.get("prix_unitaire", 0)
|
||||
total += float(prix)
|
||||
|
||||
pdf.cell(120, 8, description, 1, 0)
|
||||
pdf.cell(35, 8, f"{prix} E", 1, 0, 'R')
|
||||
pdf.cell(35, 8, f"{prix} E", 1, 1, 'R')
|
||||
|
||||
# Total Amount
|
||||
pdf.set_font("Helvetica", "B", 10)
|
||||
pdf.cell(155, 10, "TOTAL", 1, 0, 'R')
|
||||
pdf.cell(35, 10, f"{total} E", 1, 1, 'R')
|
||||
|
||||
# Payment Terms and Notes
|
||||
pdf.ln(10)
|
||||
pdf.set_font("Helvetica", "B", 10)
|
||||
pdf.cell(0, 10, "CONDITIONS DE PAIEMENT", 0, 1)
|
||||
|
||||
pdf.set_font("Helvetica", "", 10)
|
||||
payment_terms = self.form_data.get("payment_terms", "50% à la signature, 50% à la livraison")
|
||||
pdf.multi_cell(0, 5, payment_terms)
|
||||
|
||||
pdf.ln(5)
|
||||
pdf.set_font("Helvetica", "B", 10)
|
||||
pdf.cell(0, 10, "NOTES", 0, 1)
|
||||
|
||||
pdf.set_font("Helvetica", "", 10)
|
||||
pdf.multi_cell(0, 5, "Ce devis est valable 30 jours à compter de la date d'émission.")
|
||||
|
||||
# Footer
|
||||
pdf.ln(10)
|
||||
pdf.set_font("Helvetica", "I", 8)
|
||||
pdf.cell(0, 10, "Merci de votre confiance.", 0, 1, 'C')
|
||||
pdf.cell(0, 10, "Pour accepter ce devis, veuillez le signer et me le retourner par email.", 0, 1, 'C')
|
||||
|
||||
# Ensure output folder exists
|
||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||
|
||||
# Save the PDF
|
||||
pdf.output(output_path)
|
||||
print(f"PDF saved to {output_path}")
|
||||
0
core/utils.py
Normal file
0
core/utils.py
Normal file
Loading…
Add table
Add a link
Reference in a new issue