Spaces:
Running
Running
| import streamlit as st | |
| import pandas as pd | |
| import smtplib | |
| from email.mime.text import MIMEText | |
| from email.mime.multipart import MIMEMultipart | |
| from datetime import datetime, timedelta | |
| # ============================================================================== | |
| # CONFIGURATION SMTP (À sécuriser via st.secrets sur Hugging Face) | |
| # ============================================================================== | |
| # Dans st.secrets, ajoutez une section [smtp] avec : | |
| # server = "smtp.gmail.com" | |
| # port = 587 | |
| # email = "votre_email@gmail.com" | |
| # password = "votre_mot_de_passe_application" | |
| # ============================================================================== | |
| def envoyer_email_rappel(destinataire_client, destinataire_admin, details_pret): | |
| """ | |
| Envoie un email au Client (Rappel) et à l'Admin (Notification). | |
| """ | |
| try: | |
| smtp_server = st.secrets["smtp"]["server"] | |
| smtp_port = st.secrets["smtp"]["port"] | |
| sender_email = st.secrets["smtp"]["email"] | |
| sender_password = st.secrets["smtp"]["password"] | |
| except Exception: | |
| st.error("⚠️ Secrets SMTP non configurés. Impossible d'envoyer l'email.") | |
| return False | |
| sujet = f"🔔 Rappel Échéance - Prêt {details_pret['ID_Pret']}" | |
| # Corps du message | |
| corps = f""" | |
| Bonjour {details_pret['Nom_Complet']}, | |
| Ceci est un rappel automatique concernant votre prêt chez Vortex-Flux. | |
| Une échéance de paiement est prévue pour demain. | |
| 📋 Détails de l'échéance : | |
| -------------------------------- | |
| ID Prêt : {details_pret['ID_Pret']} | |
| Date prévue : {details_pret['Date_Echeance']} | |
| Montant : {details_pret['Montant_Versement']} XOF | |
| -------------------------------- | |
| Merci de prévoir les fonds nécessaires. | |
| Cordialement, | |
| L'équipe Vortex-Flux. | |
| """ | |
| try: | |
| # Connexion SMTP | |
| server = smtplib.SMTP(smtp_server, smtp_port) | |
| server.starttls() | |
| server.login(sender_email, sender_password) | |
| # 1. Envoi au CLIENT | |
| if destinataire_client and "@" in destinataire_client: | |
| msg_client = MIMEMultipart() | |
| msg_client['From'] = sender_email | |
| msg_client['To'] = destinataire_client | |
| msg_client['Subject'] = sujet | |
| msg_client.attach(MIMEText(corps, 'plain')) | |
| server.sendmail(sender_email, destinataire_client, msg_client.as_string()) | |
| # 2. Envoi à l'ADMIN (Copie) | |
| msg_admin = MIMEMultipart() | |
| msg_admin['From'] = sender_email | |
| msg_admin['To'] = destinataire_admin | |
| msg_admin['Subject'] = f"[ADMIN] Copie Rappel - {details_pret['Nom_Complet']}" | |
| msg_admin.attach(MIMEText(f"Notification envoyée au client.\n\n{corps}", 'plain')) | |
| server.sendmail(sender_email, destinataire_admin, msg_admin.as_string()) | |
| server.quit() | |
| return True | |
| except Exception as e: | |
| st.error(f"Erreur technique SMTP : {e}") | |
| return False | |
| def verifier_et_notifier_echeances(client, sheet_name): | |
| st.markdown("### 📧 Centre de Notifications") | |
| # Utilisation de session_state pour éviter de spammer à chaque rechargement de page | |
| if 'check_done' not in st.session_state: | |
| st.session_state.check_done = False | |
| col_btn, col_info = st.columns([1, 3]) | |
| with col_btn: | |
| lancer_verif = st.button("🔄 Vérifier Échéances (1.5j)") | |
| if lancer_verif: | |
| st.session_state.check_done = True | |
| try: | |
| sh = client.open(sheet_name) | |
| # 1. Récupération des Prêts | |
| ws_prets = sh.worksheet("Prets_Master") | |
| df_prets = pd.DataFrame(ws_prets.get_all_records()) | |
| # 2. Récupération des Clients (pour avoir les emails) | |
| ws_clients = sh.worksheet("Clients_KYC") | |
| df_clients = pd.DataFrame(ws_clients.get_all_records()) | |
| # Création d'un dictionnaire ID -> Email pour recherche rapide | |
| map_emails = dict(zip(df_clients['ID_Client'], df_clients['Email'])) | |
| now = datetime.now() | |
| delta_limit = timedelta(days=1.5) # 36 heures | |
| alertes_count = 0 | |
| st.write("Analyse des échéanciers en cours...") | |
| for index, row in df_prets.iterrows(): | |
| # On ne traite que les prêts ACTIFS | |
| if str(row.get('Statut', '')).upper() != "ACTIF": | |
| continue | |
| # Récupération de la chaîne de dates (ex: "10/05/2025;17/05/2025") | |
| dates_str = str(row.get('Dates_Versements', '')) | |
| if not dates_str: | |
| continue | |
| dates_list = dates_str.split(';') | |
| for d_str in dates_list: | |
| try: | |
| d_obj = datetime.strptime(d_str.strip(), "%d/%m/%Y") | |
| # LOGIQUE 1.5 JOURS | |
| # L'échéance est dans le futur ET la différence est < 1.5 jours | |
| diff = d_obj - now | |
| if timedelta(seconds=0) < diff <= delta_limit: | |
| client_id = row['ID_Client'] | |
| email_client = map_emails.get(client_id, "") | |
| # Email Admin (Vous) - À changer ou mettre dans secrets | |
| email_admin = st.secrets["smtp"]["email"] if "smtp" in st.secrets else "admin@vortex.com" | |
| details = { | |
| 'ID_Pret': row['ID_Pret'], | |
| 'Nom_Complet': row['Nom_Complet'], | |
| 'Date_Echeance': d_str, | |
| 'Montant_Versement': row['Montant_Versement'] | |
| } | |
| # Envoi | |
| succes = envoyer_email_rappel(email_client, email_admin, details) | |
| if succes: | |
| st.toast(f"📧 Rappel envoyé pour {row['Nom_Complet']} (Echéance : {d_str})", icon="✅") | |
| alertes_count += 1 | |
| else: | |
| st.toast(f"❌ Échec envoi pour {row['Nom_Complet']}", icon="⚠️") | |
| except ValueError: | |
| continue # Format date invalide, on ignore | |
| if alertes_count == 0: | |
| st.info("Aucune échéance proche (< 1.5 jours) détectée.") | |
| else: | |
| st.success(f"✅ Terminé : {alertes_count} notifications envoyées.") | |
| except Exception as e: | |
| st.error(f"Erreur lors de la vérification : {e}") |