import os from .client import Client from .handler import ClientHandler from .prospect import Prospect from .prospect_handler import ProspectHandler from datetime import date def clear_screen(): """Efface l'écran du terminal""" os.system('cls' if os.name == 'nt' else 'clear') def main(): """Point d'entrée pour l'application CRM""" client_handler = ClientHandler() prospect_handler = ProspectHandler() while True: clear_screen() print("=== CRM - Gestion des Relations Clients ===\n") print("1. Gestion des Clients") print("2. Gestion des Prospects") print("3. Tableau de bord") print("4. Retour au menu principal") choice = input("\nSélectionnez une option (1-4): ") if choice == "1": # Gestion des clients manage_clients(client_handler, prospect_handler) elif choice == "2": # Gestion des prospects manage_prospects(client_handler, prospect_handler) elif choice == "3": # Tableau de bord show_dashboard(client_handler, prospect_handler) elif choice == "4": # Retour au menu principal print("\nRetour au menu principal...") break else: print("\nOption invalide. Veuillez réessayer.") input("\nAppuyez sur Entrée pour continuer...") def display_all_clients(client_handler): """Affiche tous les clients""" clear_screen() print("=== Liste des Clients ===\n") clients = client_handler.get_all_clients() if not clients: print("Aucun client trouvé.") return for i, client in enumerate(clients, 1): print(f"{i}. {client.name} ({client.company})") print(f" Email: {client.email} | Téléphone: {client.phone}") print(f" Dernière interaction: {client.last_contact}") print(f" Documents: Devis ({len(client.linked_docs['devis'])}) | " f"Propositions ({len(client.linked_docs['propositions'])}) | " f"Factures ({len(client.linked_docs['factures'])})") print("") def search_clients(client_handler): """Recherche des clients par nom""" clear_screen() print("=== Recherche de Clients ===\n") search_term = input("Entrez un terme de recherche (nom, email, entreprise): ") if not search_term: print("Recherche annulée.") return results = [] clients = client_handler.get_all_clients() for client in clients: if (search_term.lower() in client.name.lower() or search_term.lower() in client.email.lower() or search_term.lower() in client.company.lower()): results.append(client) if not results: print(f"Aucun client trouvé pour '{search_term}'.") return print(f"\n{len(results)} client(s) trouvé(s):\n") for i, client in enumerate(results, 1): print(f"{i}. {client.name} ({client.company})") print(f" Email: {client.email} | Téléphone: {client.phone}") print("") # Offrir la possibilité de voir les détails d'un client if results: choice = input("\nEntrez le numéro du client pour voir les détails (ou Entrée pour annuler): ") if choice.isdigit() and 1 <= int(choice) <= len(results): client = results[int(choice) - 1] display_client_details(client) def add_new_client(client_handler): """Ajoute un nouveau client""" clear_screen() print("=== Ajout d'un Nouveau Client ===\n") # Recueillir les informations du client name = input("Nom du client: ") if not name: print("Le nom est obligatoire. Opération annulée.") return company = input("Entreprise: ") email = input("Email: ") phone = input("Téléphone: ") notes = input("Notes: ") # Créer le nouveau client new_client = Client( name=name, company=company, email=email, phone=phone, notes=notes ) # Ajouter le client client_handler.add_client(new_client) print(f"\nClient '{name}' ajouté avec succès!") def edit_client(client_handler): """Modifie un client existant""" clear_screen() print("=== Modification d'un Client ===\n") # Afficher tous les clients pour sélection clients = client_handler.get_all_clients() if not clients: print("Aucun client trouvé.") return for i, client in enumerate(clients, 1): print(f"{i}. {client.name} ({client.company})") # Sélectionner un client à modifier choice = input("\nEntrez le numéro du client à modifier (ou Entrée pour annuler): ") if not choice.isdigit() or int(choice) < 1 or int(choice) > len(clients): print("Opération annulée ou sélection invalide.") return client = clients[int(choice) - 1] # Afficher les détails actuels print(f"\nModification de '{client.name}':") print(f"1. Nom: {client.name}") print(f"2. Entreprise: {client.company}") print(f"3. Email: {client.email}") print(f"4. Téléphone: {client.phone}") print(f"5. Notes: {client.notes}") print("6. Tout modifier") print("7. Annuler") # Choisir ce qu'il faut modifier edit_choice = input("\nQue souhaitez-vous modifier (1-7): ") if edit_choice == "7": print("Modification annulée.") return if edit_choice == "1" or edit_choice == "6": client.name = input(f"Nouveau nom [{client.name}]: ") or client.name if edit_choice == "2" or edit_choice == "6": client.company = input(f"Nouvelle entreprise [{client.company}]: ") or client.company if edit_choice == "3" or edit_choice == "6": client.email = input(f"Nouvel email [{client.email}]: ") or client.email if edit_choice == "4" or edit_choice == "6": client.phone = input(f"Nouveau téléphone [{client.phone}]: ") or client.phone if edit_choice == "5" or edit_choice == "6": client.notes = input(f"Nouvelles notes [{client.notes}]: ") or client.notes # Mettre à jour le client client_handler.update_client(client) print(f"\nClient '{client.name}' mis à jour avec succès!") def delete_client(client_handler): """Supprime un client""" clear_screen() print("=== Suppression d'un Client ===\n") # Afficher tous les clients pour sélection clients = client_handler.get_all_clients() if not clients: print("Aucun client trouvé.") return for i, client in enumerate(clients, 1): print(f"{i}. {client.name} ({client.company})") # Sélectionner un client à supprimer choice = input("\nEntrez le numéro du client à supprimer (ou Entrée pour annuler): ") if not choice.isdigit() or int(choice) < 1 or int(choice) > len(clients): print("Opération annulée ou sélection invalide.") return client = clients[int(choice) - 1] # Confirmer la suppression confirm = input(f"Êtes-vous sûr de vouloir supprimer '{client.name}'? (o/n): ") if confirm.lower() != 'o': print("Suppression annulée.") return # Supprimer le client client_handler.delete_client(client.id) print(f"\nClient '{client.name}' supprimé avec succès!") def view_client_documents(client_handler): """Affiche les documents associés à un client""" clear_screen() print("=== Documents d'un Client ===\n") # Afficher tous les clients pour sélection clients = client_handler.get_all_clients() if not clients: print("Aucun client trouvé.") return for i, client in enumerate(clients, 1): print(f"{i}. {client.name} ({client.company})") # Sélectionner un client choice = input("\nEntrez le numéro du client pour voir ses documents (ou Entrée pour annuler): ") if not choice.isdigit() or int(choice) < 1 or int(choice) > len(clients): print("Opération annulée ou sélection invalide.") return client = clients[int(choice) - 1] display_client_documents(client) def display_client_details(client): """Affiche les détails d'un client""" clear_screen() print(f"=== Détails du Client: {client.name} ===\n") print(f"ID: {client.id}") print(f"Nom: {client.name}") print(f"Entreprise: {client.company}") print(f"Email: {client.email}") print(f"Téléphone: {client.phone}") print(f"Notes: {client.notes}") print(f"Dernière interaction: {client.last_contact}") print(f"Prochaine action: {client.next_action}") if client.tags: print(f"Tags: {', '.join(client.tags)}") # Afficher les documents display_client_documents(client) def display_client_documents(client): """Affiche les documents associés à un client""" print(f"\n=== Documents de {client.name} ===\n") devis = client.linked_docs.get('devis', []) propositions = client.linked_docs.get('propositions', []) factures = client.linked_docs.get('factures', []) if not devis and not propositions and not factures: print("Aucun document trouvé pour ce client.") return if devis: print("Devis:") for i, doc in enumerate(devis, 1): print(f" {i}. {os.path.basename(doc)}") if propositions: print("\nPropositions commerciales:") for i, doc in enumerate(propositions, 1): print(f" {i}. {os.path.basename(doc)}") if factures: print("\nFactures:") for i, doc in enumerate(factures, 1): print(f" {i}. {os.path.basename(doc)}") # Option pour ouvrir un document print("\n1. Ouvrir un document") print("2. Retour") choice = input("\nQue souhaitez-vous faire (1-2): ") if choice == "1": # Choisir le type de document print("\nType de document:") print("1. Devis") print("2. Proposition commerciale") print("3. Facture") doc_type_choice = input("\nSélectionnez le type de document (1-3): ") doc_list = [] if doc_type_choice == "1" and devis: doc_list = devis doc_type_name = "devis" elif doc_type_choice == "2" and propositions: doc_list = propositions doc_type_name = "proposition commerciale" elif doc_type_choice == "3" and factures: doc_list = factures doc_type_name = "facture" else: print("Choix invalide ou aucun document de ce type.") return # Afficher les documents du type sélectionné print(f"\n{doc_type_name.capitalize()}s disponibles:") for i, doc in enumerate(doc_list, 1): print(f" {i}. {os.path.basename(doc)}") # Sélectionner un document à ouvrir doc_choice = input(f"\nSélectionnez un {doc_type_name} à ouvrir (1-{len(doc_list)}): ") if doc_choice.isdigit() and 1 <= int(doc_choice) <= len(doc_list): selected_doc = doc_list[int(doc_choice) - 1] open_document(selected_doc) else: print("Sélection invalide.") def open_document(document_path): """Ouvre un document PDF""" try: if os.path.exists(document_path): if os.name == 'nt': # Windows os.startfile(document_path) elif os.name == 'posix': # macOS et Linux if 'darwin' in os.uname().sysname.lower(): # macOS os.system(f'open "{document_path}"') else: # Linux os.system(f'xdg-open "{document_path}"') print(f"Document ouvert: {os.path.basename(document_path)}") else: print(f"Erreur: Le document {document_path} n'existe pas.") except Exception as e: print(f"Erreur lors de l'ouverture du document: {e}") def manage_clients(client_handler, prospect_handler): """Gestion des clients""" while True: clear_screen() print("=== Gestion des Clients ===\n") print("1. Afficher tous les clients") print("2. Rechercher un client") print("3. Ajouter un nouveau client") print("4. Modifier un client") print("5. Supprimer un client") print("6. Voir les documents d'un client") print("7. Retour") choice = input("\nSélectionnez une option (1-7): ") if choice == "1": # Afficher tous les clients display_all_clients(client_handler) elif choice == "2": # Rechercher un client search_clients(client_handler) elif choice == "3": # Ajouter un nouveau client add_new_client(client_handler) elif choice == "4": # Modifier un client edit_client(client_handler) elif choice == "5": # Supprimer un client delete_client(client_handler) elif choice == "6": # Voir les documents d'un client view_client_documents(client_handler) elif choice == "7": # Retour break else: print("\nOption invalide. Veuillez réessayer.") input("\nAppuyez sur Entrée pour continuer...") def manage_prospects(client_handler, prospect_handler): """Gestion des prospects""" while True: clear_screen() print("=== Gestion des Prospects ===\n") print("1. Afficher tous les prospects") print("2. Rechercher un prospect") print("3. Ajouter un nouveau prospect") print("4. Modifier un prospect") print("5. Supprimer un prospect") print("6. Convertir un prospect en client") print("7. Afficher les prospects par statut") print("8. Retour") choice = input("\nSélectionnez une option (1-8): ") if choice == "1": # Afficher tous les prospects display_all_prospects(prospect_handler) elif choice == "2": # Rechercher un prospect search_prospects(prospect_handler) elif choice == "3": # Ajouter un nouveau prospect add_new_prospect(prospect_handler) elif choice == "4": # Modifier un prospect edit_prospect(prospect_handler) elif choice == "5": # Supprimer un prospect delete_prospect(prospect_handler) elif choice == "6": # Convertir un prospect en client convert_prospect_to_client(prospect_handler, client_handler) elif choice == "7": # Afficher les prospects par statut display_prospects_by_status(prospect_handler) elif choice == "8": # Retour break else: print("\nOption invalide. Veuillez réessayer.") input("\nAppuyez sur Entrée pour continuer...") def show_dashboard(client_handler, prospect_handler): """Affiche un tableau de bord avec des statistiques""" clear_screen() print("=== Tableau de Bord CRM ===\n") # Récupérer les données clients = client_handler.get_all_clients() prospects = prospect_handler.get_all_prospects() # Statistiques de base print(f"Nombre total de clients: {len(clients)}") print(f"Nombre total de prospects: {len(prospects)}") # Statistiques des prospects par statut statuses = {} for prospect in prospects: status = prospect.status if status in statuses: statuses[status] += 1 else: statuses[status] = 1 if statuses: print("\nProspects par statut:") for status, count in statuses.items(): print(f" {status}: {count}") # Derniers prospects ajoutés recent_prospects = prospect_handler.get_recent_prospects(days=30) if recent_prospects: print(f"\nNouveaux prospects (30 derniers jours): {len(recent_prospects)}") # Activité récente print("\nRécapitulatif des activités récentes:") print(" Propositions commerciales en attente...") print(" Devis en cours...") # Suggestions d'actions print("\nSuggestions d'actions:") if len(prospects) > 0: print(" • Suivez vos prospects pour augmenter votre taux de conversion") if len(clients) > 0: print(" • Contactez vos clients existants pour des opportunités de vente additionnelle") if len(prospects) == 0: print(" • Ajoutez des prospects pour développer votre portefeuille client") def display_all_prospects(prospect_handler): """Affiche tous les prospects""" clear_screen() print("=== Liste des Prospects ===\n") prospects = prospect_handler.get_all_prospects() if not prospects: print("Aucun prospect trouvé.") return for i, prospect in enumerate(prospects, 1): print(f"{i}. {prospect.name} ({prospect.company})") print(f" Email: {prospect.email} | Téléphone: {prospect.phone}") print(f" Statut: {prospect.status} | Source: {prospect.source}") print(f" Dernière interaction: {prospect.last_contact}") print("") def search_prospects(prospect_handler): """Recherche des prospects par nom""" clear_screen() print("=== Recherche de Prospects ===\n") search_term = input("Entrez un terme de recherche (nom, email, entreprise): ") if not search_term: print("Recherche annulée.") return results = [] prospects = prospect_handler.get_all_prospects() for prospect in prospects: if (search_term.lower() in prospect.name.lower() or search_term.lower() in prospect.email.lower() or search_term.lower() in prospect.company.lower()): results.append(prospect) if not results: print(f"Aucun prospect trouvé pour '{search_term}'.") return print(f"\n{len(results)} prospect(s) trouvé(s):\n") for i, prospect in enumerate(results, 1): print(f"{i}. {prospect.name} ({prospect.company})") print(f" Email: {prospect.email} | Téléphone: {prospect.phone}") print(f" Statut: {prospect.status}") print("") # Offrir la possibilité de voir les détails d'un prospect if results: choice = input("\nEntrez le numéro du prospect pour voir les détails (ou Entrée pour annuler): ") if choice.isdigit() and 1 <= int(choice) <= len(results): prospect = results[int(choice) - 1] display_prospect_details(prospect) def add_new_prospect(prospect_handler): """Ajoute un nouveau prospect""" clear_screen() print("=== Ajout d'un Nouveau Prospect ===\n") # Recueillir les informations du prospect name = input("Nom du prospect: ") if not name: print("Le nom est obligatoire. Opération annulée.") return company = input("Entreprise: ") email = input("Email: ") phone = input("Téléphone: ") source = input("Source (site web, référence, salon, etc.): ") notes = input("Notes: ") # Statut du prospect print("\nStatut du prospect:") print("1. Nouveau") print("2. Contacté") print("3. Qualifié") print("4. Proposition envoyée") print("5. Non intéressé") status_choice = input("\nSélectionnez le statut (1-5): ") status = "Nouveau" # Défaut if status_choice == "1": status = "Nouveau" elif status_choice == "2": status = "Contacté" elif status_choice == "3": status = "Qualifié" elif status_choice == "4": status = "Proposition envoyée" elif status_choice == "5": status = "Non intéressé" # Créer le nouveau prospect new_prospect = Prospect( name=name, company=company, email=email, phone=phone, source=source, notes=notes, status=status ) # Ajouter le prospect prospect_handler.add_prospect(new_prospect) print(f"\nProspect '{name}' ajouté avec succès!") def edit_prospect(prospect_handler): """Modifie un prospect existant""" clear_screen() print("=== Modification d'un Prospect ===\n") # Afficher tous les prospects pour sélection prospects = prospect_handler.get_all_prospects() if not prospects: print("Aucun prospect trouvé.") return for i, prospect in enumerate(prospects, 1): print(f"{i}. {prospect.name} ({prospect.company}) - {prospect.status}") # Sélectionner un prospect à modifier choice = input("\nEntrez le numéro du prospect à modifier (ou Entrée pour annuler): ") if not choice.isdigit() or int(choice) < 1 or int(choice) > len(prospects): print("Opération annulée ou sélection invalide.") return prospect = prospects[int(choice) - 1] # Afficher les détails actuels print(f"\nModification de '{prospect.name}':") print(f"1. Nom: {prospect.name}") print(f"2. Entreprise: {prospect.company}") print(f"3. Email: {prospect.email}") print(f"4. Téléphone: {prospect.phone}") print(f"5. Source: {prospect.source}") print(f"6. Statut: {prospect.status}") print(f"7. Notes: {prospect.notes}") print("8. Tout modifier") print("9. Annuler") # Choisir ce qu'il faut modifier edit_choice = input("\nQue souhaitez-vous modifier (1-9): ") if edit_choice == "9": print("Modification annulée.") return if edit_choice == "1" or edit_choice == "8": prospect.name = input(f"Nouveau nom [{prospect.name}]: ") or prospect.name if edit_choice == "2" or edit_choice == "8": prospect.company = input(f"Nouvelle entreprise [{prospect.company}]: ") or prospect.company if edit_choice == "3" or edit_choice == "8": prospect.email = input(f"Nouvel email [{prospect.email}]: ") or prospect.email if edit_choice == "4" or edit_choice == "8": prospect.phone = input(f"Nouveau téléphone [{prospect.phone}]: ") or prospect.phone if edit_choice == "5" or edit_choice == "8": prospect.source = input(f"Nouvelle source [{prospect.source}]: ") or prospect.source if edit_choice == "6" or edit_choice == "8": print("\nStatut du prospect:") print("1. Nouveau") print("2. Contacté") print("3. Qualifié") print("4. Proposition envoyée") print("5. Non intéressé") status_choice = input(f"\nSélectionnez le statut (1-5) [Actuel: {prospect.status}]: ") if status_choice == "1": prospect.status = "Nouveau" elif status_choice == "2": prospect.status = "Contacté" elif status_choice == "3": prospect.status = "Qualifié" elif status_choice == "4": prospect.status = "Proposition envoyée" elif status_choice == "5": prospect.status = "Non intéressé" if edit_choice == "7" or edit_choice == "8": prospect.notes = input(f"Nouvelles notes [{prospect.notes}]: ") or prospect.notes # Mettre à jour le prospect prospect_handler.update_prospect(prospect) print(f"\nProspect '{prospect.name}' mis à jour avec succès!") def delete_prospect(prospect_handler): """Supprime un prospect""" clear_screen() print("=== Suppression d'un Prospect ===\n") # Afficher tous les prospects pour sélection prospects = prospect_handler.get_all_prospects() if not prospects: print("Aucun prospect trouvé.") return for i, prospect in enumerate(prospects, 1): print(f"{i}. {prospect.name} ({prospect.company}) - {prospect.status}") # Sélectionner un prospect à supprimer choice = input("\nEntrez le numéro du prospect à supprimer (ou Entrée pour annuler): ") if not choice.isdigit() or int(choice) < 1 or int(choice) > len(prospects): print("Opération annulée ou sélection invalide.") return prospect = prospects[int(choice) - 1] # Confirmer la suppression confirm = input(f"Êtes-vous sûr de vouloir supprimer '{prospect.name}'? (o/n): ") if confirm.lower() != 'o': print("Suppression annulée.") return # Supprimer le prospect prospect_handler.delete_prospect(prospect.id) print(f"\nProspect '{prospect.name}' supprimé avec succès!") def convert_prospect_to_client(prospect_handler, client_handler): """Convertit un prospect en client""" clear_screen() print("=== Conversion d'un Prospect en Client ===\n") # Afficher tous les prospects pour sélection prospects = prospect_handler.get_all_prospects() if not prospects: print("Aucun prospect trouvé.") return for i, prospect in enumerate(prospects, 1): print(f"{i}. {prospect.name} ({prospect.company}) - {prospect.status}") # Sélectionner un prospect à convertir choice = input("\nEntrez le numéro du prospect à convertir en client (ou Entrée pour annuler): ") if not choice.isdigit() or int(choice) < 1 or int(choice) > len(prospects): print("Opération annulée ou sélection invalide.") return prospect = prospects[int(choice) - 1] # Confirmer la conversion confirm = input(f"Êtes-vous sûr de vouloir convertir '{prospect.name}' en client? (o/n): ") if confirm.lower() != 'o': print("Conversion annulée.") return # Convertir le prospect en client client = prospect_handler.convert_to_client(prospect.id, client_handler) if client: print(f"\nProspect '{prospect.name}' converti en client avec succès!") else: print("\nErreur lors de la conversion du prospect en client.") def display_prospect_details(prospect): """Affiche les détails d'un prospect""" clear_screen() print(f"=== Détails du Prospect: {prospect.name} ===\n") print(f"ID: {prospect.id}") print(f"Nom: {prospect.name}") print(f"Entreprise: {prospect.company}") print(f"Email: {prospect.email}") print(f"Téléphone: {prospect.phone}") print(f"Source: {prospect.source}") print(f"Statut: {prospect.status}") print(f"Notes: {prospect.notes}") print(f"Dernière interaction: {prospect.last_contact}") print(f"Prochaine action: {prospect.next_action}") if prospect.tags: print(f"Tags: {', '.join(prospect.tags)}") def display_prospects_by_status(prospect_handler): """Affiche les prospects regroupés par statut""" clear_screen() print("=== Prospects par Statut ===\n") # Récupérer tous les prospects prospects = prospect_handler.get_all_prospects() if not prospects: print("Aucun prospect trouvé.") return # Regrouper les prospects par statut status_groups = {} for prospect in prospects: status = prospect.status if status not in status_groups: status_groups[status] = [] status_groups[status].append(prospect) # Afficher les prospects par groupe de statut for status, prospects_list in status_groups.items(): print(f"\n=== {status} ({len(prospects_list)}) ===") for i, prospect in enumerate(prospects_list, 1): print(f"{i}. {prospect.name} ({prospect.company})") print(f" Email: {prospect.email} | Téléphone: {prospect.phone}") print("\n") # Option pour filtrer par un statut spécifique print("Voulez-vous voir les détails d'un statut spécifique?") print("1. Nouveau") print("2. Contacté") print("3. Qualifié") print("4. Proposition envoyée") print("5. Non intéressé") print("6. Retour") choice = input("\nSélectionnez une option (1-6): ") status_mapping = { "1": "Nouveau", "2": "Contacté", "3": "Qualifié", "4": "Proposition envoyée", "5": "Non intéressé" } if choice in status_mapping: selected_status = status_mapping[choice] filtered_prospects = prospect_handler.get_prospects_by_status(selected_status) if filtered_prospects: clear_screen() print(f"=== Prospects avec statut: {selected_status} ===\n") for i, prospect in enumerate(filtered_prospects, 1): print(f"{i}. {prospect.name} ({prospect.company})") print(f" Email: {prospect.email} | Téléphone: {prospect.phone}") print(f" Dernière interaction: {prospect.last_contact}") print(f" Notes: {prospect.notes[:50]}..." if len(prospect.notes) > 50 else f" Notes: {prospect.notes}") print("") # Option pour voir les détails d'un prospect spécifique detail_choice = input("\nEntrez le numéro du prospect pour voir les détails (ou Entrée pour annuler): ") if detail_choice.isdigit() and 1 <= int(detail_choice) <= len(filtered_prospects): prospect = filtered_prospects[int(detail_choice) - 1] display_prospect_details(prospect) else: print(f"\nAucun prospect avec le statut '{selected_status}' trouvé.") if __name__ == "__main__": main()