klydekushy commited on
Commit
d0cfbd4
·
verified ·
1 Parent(s): 993d348

Update src/Analytics/AnalyseRepayment.py

Browse files
Files changed (1) hide show
  1. src/Analytics/AnalyseRepayment.py +91 -1
src/Analytics/AnalyseRepayment.py CHANGED
@@ -313,6 +313,85 @@ class AnalyseRepayment:
313
 
314
  return max(0, montant_manquant)
315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  def analyser_remboursement_complet(self, id_pret, date_paiement, montant_verse, taux_penalite=0.05):
317
  """
318
  Analyse complète d'un remboursement
@@ -381,6 +460,14 @@ class AnalyseRepayment:
381
  penalites_info['montant_penalites']
382
  )
383
 
 
 
 
 
 
 
 
 
384
  # Compilation des résultats
385
  return {
386
  'id_pret': id_pret,
@@ -398,5 +485,8 @@ class AnalyseRepayment:
398
  'solde_apres': soldes['solde_apres'],
399
  'statut_paiement': statut,
400
  'montant_a_reporter': round(montant_a_reporter, 0),
401
- 'prochaine_echeance': echeance_info['numero'] + 1 if echeance_info['numero'] < echeance_info['total'] else None
 
 
 
402
  }
 
313
 
314
  return max(0, montant_manquant)
315
 
316
+ def verifier_si_pret_termine(self, id_pret, solde_apres, montant_principal_paye, montant_interets_paye):
317
+ """
318
+ Vérifie si un prêt doit être automatiquement terminé selon 3 critères
319
+
320
+ Args:
321
+ id_pret: ID du prêt
322
+ solde_apres: Solde après le paiement en cours
323
+ montant_principal_paye: Montant du principal payé dans ce paiement
324
+ montant_interets_paye: Montant des intérêts payés dans ce paiement
325
+
326
+ Returns:
327
+ dict: {
328
+ 'est_termine': True/False,
329
+ 'critere': 'SOLDE_ZERO' / 'TOUTES_ECHEANCES' / 'MONTANT_TOTAL' / None,
330
+ 'message': Description de la raison
331
+ }
332
+ """
333
+ loan_data = self.get_loan_data(id_pret)
334
+ if loan_data is None:
335
+ return {'est_termine': False, 'critere': None, 'message': 'Prêt non trouvé'}
336
+
337
+ # CRITÈRE 1 : Solde = 0
338
+ if solde_apres <= 0:
339
+ return {
340
+ 'est_termine': True,
341
+ 'critere': 'SOLDE_ZERO',
342
+ 'message': f'Solde du prêt soldé (Solde final : {solde_apres:,.0f} XOF)'
343
+ }
344
+
345
+ # CRITÈRE 2 : Toutes les échéances payées (sans paiement partiel en suspens)
346
+ echeances = self.parse_echeances(loan_data['Dates_Versements'])
347
+ nb_echeances_prevues = len(echeances)
348
+
349
+ previous_payments = self.get_previous_payments(id_pret)
350
+ # Compter uniquement les paiements non partiels ou le paiement en cours
351
+ paiements_complets = [p for p in previous_payments if p.get('Statut_Paiement') not in ['PARTIEL', None]]
352
+ nb_paiements_valides = len(paiements_complets) + 1 # +1 pour le paiement actuel
353
+
354
+ # Vérifier qu'il n'y a pas d'ajustements en attente
355
+ ajustements_futurs = False
356
+ if not self.df_ajustements.empty:
357
+ # Chercher des ajustements sur des échéances futures non encore payées
358
+ for i in range(nb_paiements_valides + 1, nb_echeances_prevues + 1):
359
+ ajustements = self.df_ajustements[
360
+ (self.df_ajustements['ID_Pret'] == id_pret) &
361
+ (self.df_ajustements['Numero_Echeance'] == i)
362
+ ]
363
+ if not ajustements.empty:
364
+ ajustements_futurs = True
365
+ break
366
+
367
+ if nb_paiements_valides >= nb_echeances_prevues and not ajustements_futurs:
368
+ return {
369
+ 'est_termine': True,
370
+ 'critere': 'TOUTES_ECHEANCES',
371
+ 'message': f'Toutes les échéances payées ({nb_paiements_valides}/{nb_echeances_prevues})'
372
+ }
373
+
374
+ # CRITÈRE 3 : Montant total remboursé (Principal + Intérêts) >= Montant dû
375
+ total_principal_rembourse = sum(p.get('Montant_Principal', 0) for p in previous_payments) + montant_principal_paye
376
+ total_interets_rembourse = sum(p.get('Montant_Interets', 0) for p in previous_payments) + montant_interets_paye
377
+ total_rembourse = total_principal_rembourse + total_interets_rembourse
378
+
379
+ montant_total_du = loan_data['Montant_Total']
380
+
381
+ if total_rembourse >= montant_total_du:
382
+ return {
383
+ 'est_termine': True,
384
+ 'critere': 'MONTANT_TOTAL',
385
+ 'message': f'Montant total remboursé ({total_rembourse:,.0f} XOF >= {montant_total_du:,.0f} XOF)'
386
+ }
387
+
388
+ # Aucun critère rempli
389
+ return {
390
+ 'est_termine': False,
391
+ 'critere': None,
392
+ 'message': f'Prêt encore actif (Solde: {solde_apres:,.0f} XOF, Échéances: {nb_paiements_valides}/{nb_echeances_prevues})'
393
+ }
394
+
395
  def analyser_remboursement_complet(self, id_pret, date_paiement, montant_verse, taux_penalite=0.05):
396
  """
397
  Analyse complète d'un remboursement
 
460
  penalites_info['montant_penalites']
461
  )
462
 
463
+ # 8. Vérification automatique si le prêt doit être terminé
464
+ verification_cloture = self.verifier_si_pret_termine(
465
+ id_pret,
466
+ soldes['solde_apres'],
467
+ decomposition['montant_principal'],
468
+ decomposition['montant_interets']
469
+ )
470
+
471
  # Compilation des résultats
472
  return {
473
  'id_pret': id_pret,
 
485
  'solde_apres': soldes['solde_apres'],
486
  'statut_paiement': statut,
487
  'montant_a_reporter': round(montant_a_reporter, 0),
488
+ 'prochaine_echeance': echeance_info['numero'] + 1 if echeance_info['numero'] < echeance_info['total'] else None,
489
+ 'doit_cloturer': verification_cloture['est_termine'],
490
+ 'critere_cloture': verification_cloture['critere'],
491
+ 'message_cloture': verification_cloture['message']
492
  }