Spaces:
Running
Running
File size: 6,814 Bytes
eae5bee 59a8c9f eae5bee 59a8c9f eae5bee 59a8c9f eae5bee 59a8c9f eae5bee 59a8c9f eae5bee 59a8c9f eae5bee 59a8c9f eae5bee 59a8c9f eae5bee 59a8c9f eae5bee 59a8c9f eae5bee 59a8c9f eae5bee 59a8c9f eae5bee 59a8c9f eae5bee |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
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}") |