Spaces:
Running
Running
Download src/DocumentGen/AutoPDFGeneration.py from klydekushy/Vortex-Flux: direct link, hf CLI and curl.
- Browser
- Download file 25.5 kB
-
https://huggingface.co/spaces/klydekushy/Vortex-Flux/resolve/main/src/DocumentGen/AutoPDFGeneration.py
- Command line
-
hf download hf://spaces/klydekushy/Vortex-Flux/src/DocumentGen/AutoPDFGeneration.py
-
curl -L -o AutoPDFGeneration.py https://huggingface.co/spaces/klydekushy/Vortex-Flux/resolve/main/src/DocumentGen/AutoPDFGeneration.py
25.5 kB
| #Ce fichier est utiliser pour generer les docs sur les contrats de dette client et garant. | |
| from io import BytesIO | |
| import pandas as pd | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.lib import colors | |
| from reportlab.lib.units import cm | |
| from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer | |
| from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle | |
| from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY | |
| def format_currency(amount): | |
| return f"{int(amount):,}".replace(",", " ") | |
| def get_common_styles(): | |
| styles = getSampleStyleSheet() | |
| if 'TitleCenter' not in styles: | |
| styles.add(ParagraphStyle(name='TitleCenter', parent=styles['Heading1'], alignment=TA_CENTER, fontSize=16, spaceAfter=20)) | |
| if 'NormalJustify' not in styles: | |
| styles.add(ParagraphStyle(name='NormalJustify', parent=styles['Normal'], alignment=TA_JUSTIFY, fontSize=11, leading=14)) | |
| if 'BoldLabel' not in styles: | |
| styles.add(ParagraphStyle(name='BoldLabel', parent=styles['Normal'], fontName='Helvetica-Bold', fontSize=11)) | |
| if 'OffreTag' not in styles: | |
| styles.add(ParagraphStyle( | |
| name='OffreTag', parent=styles['Normal'], | |
| fontName='Helvetica-Bold', fontSize=9, | |
| textColor=colors.HexColor('#444444'), | |
| spaceAfter=4, spaceBefore=4, | |
| alignment=TA_LEFT | |
| )) | |
| return styles | |
| def _offre_table(offre, styles): | |
| """ | |
| Retourne un bloc Table affichant l'offre commerciale selectionnee, | |
| a inserer dans le preambule des contrats. | |
| """ | |
| offre_val = offre if offre else "N/A" | |
| data = [["OFFRE COMMERCIALE", offre_val]] | |
| t = Table(data, colWidths=[7*cm, 8*cm]) | |
| t.setStyle(TableStyle([ | |
| ('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#cccccc')), | |
| ('BACKGROUND', (0, 0), (0, 0), colors.HexColor('#f0f0f0')), | |
| ('FONTNAME', (0, 0), (0, 0), 'Helvetica-Bold'), | |
| ('FONTNAME', (1, 0), (1, 0), 'Helvetica-Bold'), | |
| ('TEXTCOLOR', (1, 0), (1, 0), colors.HexColor('#1a2a4a')), | |
| ('FONTSIZE', (0, 0), (-1, -1), 10), | |
| ('PADDING', (0, 0), (-1, -1), 6), | |
| ])) | |
| return t | |
| # ============================================================================ | |
| # SIGNATURE ELECTRONIQUE (bloc place en fin de document) | |
| # ============================================================================ | |
| # Chaque generateur accepte `signature=None`. Sans signature : bloc manuscrit | |
| # historique. Avec signature (dict) : bloc de signature electronique. Les PDF sont | |
| # generes avec invariant=1 (aucun horodatage ni identifiant aleatoire) : memes | |
| # donnees = memes octets, ce qui permet de recalculer l'empreinte d'un contrat. | |
| # | |
| # Cles attendues dans `signature` : id_signature, nom, role, date_utc (JJ/MM/AAAA HH:MM:SS), | |
| # ip, id_masque, hash_document, scelle, url_verification. | |
| def _ligne_fait_a(signature): | |
| if signature and signature.get("date_utc"): | |
| return f"FAIT A DAKAR, LE : {str(signature['date_utc'])[:10]}" | |
| return "FAIT A DAKAR, LE : __________________" | |
| def _table_parties_signature(libelle_preteur, nom_preteur, libelle_signataire, nom_signataire, preteur_signe=False): | |
| data = [ | |
| [libelle_preteur, libelle_signataire], | |
| [nom_preteur, nom_signataire], | |
| ["Signature electronique ci-dessous" if preteur_signe else "", "Signature electronique ci-dessous"], | |
| ] | |
| t = Table(data, colWidths=[8*cm, 8*cm]) | |
| t.setStyle(TableStyle([('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold')])) | |
| return t | |
| def _bloc_signature_electronique(signature, styles): | |
| """Flowables du bloc de signature electronique (fin de document).""" | |
| from xml.sax.saxutils import escape | |
| petit = ParagraphStyle(name='SigPetit', parent=styles['Normal'], fontSize=8, leading=10) | |
| mono = ParagraphStyle(name='SigMono', parent=styles['Normal'], fontName='Courier', fontSize=7, leading=9) | |
| def p(texte, style=petit): | |
| return Paragraph(escape(str(texte)), style) | |
| lignes = [ | |
| ["SIGNATURE ELECTRONIQUE", ""], | |
| ["Signataire", p(f"{signature.get('nom', '')} ({signature.get('role', '')})")], | |
| ["Mention", p("« Lu et approuve » - acceptation par case a cocher")], | |
| ["Date et heure (UTC)", p(signature.get("date_utc", ""))], | |
| ["Adresse IP", p(signature.get("ip") or "N/A")], | |
| # Absente des signatures anterieures : leurs PDF restent identiques octet pour octet. | |
| *([["Localisation (GPS)", p(signature["localisation"])]] if signature.get("localisation") else []), | |
| ["Verification d'identite", p(signature.get("verification") or ( | |
| f"4 derniers caracteres de la piece d'identite ({signature.get('id_masque', '')}) " | |
| "et code a usage unique envoye par email : valides"))], | |
| ["Identifiant de signature", p(signature.get("id_signature", ""))], | |
| ["Empreinte SHA-256 du document", p(signature.get("hash_document", ""), mono)], | |
| ["Sceau d'authenticite (HMAC)", p(signature.get("scelle", ""), mono)], | |
| ["Verification", p(signature.get("url_verification", ""))], | |
| ] | |
| t = Table(lignes, colWidths=[5*cm, 11*cm]) | |
| t.setStyle(TableStyle([ | |
| ('GRID', (0, 0), (-1, -1), 0.5, colors.grey), | |
| ('SPAN', (0, 0), (-1, 0)), | |
| ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#1a2a4a')), | |
| ('TEXTCOLOR', (0, 0), (-1, 0), colors.white), | |
| ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), | |
| ('BACKGROUND', (0, 1), (0, -1), colors.whitesmoke), | |
| ('FONTNAME', (0, 1), (0, -1), 'Helvetica-Bold'), | |
| ('FONTSIZE', (0, 0), (-1, -1), 8), | |
| ('VALIGN', (0, 0), (-1, -1), 'TOP'), | |
| ('PADDING', (0, 0), (-1, -1), 4), | |
| ])) | |
| return [t] | |
| # ============================================================================ | |
| # 1. CONTRAT DE PRET | |
| # ============================================================================ | |
| def generer_contrat_pret(loan_data, client_info, df_amortissement, signature=None, signature_preteur=None): | |
| buffer = BytesIO() | |
| doc = SimpleDocTemplate(buffer, pagesize=A4, | |
| rightMargin=2*cm, leftMargin=2*cm, | |
| topMargin=2*cm, bottomMargin=2*cm, | |
| invariant=1) | |
| styles = get_common_styles() | |
| story = [] | |
| offre = loan_data.get('Offre', '') | |
| # En-tete | |
| story.append(Paragraph(f"N° {loan_data['ID_Pret']}", styles['Normal'])) | |
| story.append(Paragraph("CONTRAT DE PRET A INTERETS", styles['TitleCenter'])) | |
| # [OK] Bloc offre commerciale juste apres le titre | |
| story.append(Spacer(1, 6)) | |
| story.append(_offre_table(offre, styles)) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("Entre les soussignes:", styles['Normal'])) | |
| story.append(Spacer(1, 10)) | |
| # I. LE PRETEUR | |
| story.append(Paragraph("<b>I. LE PRETEUR</b>", styles['Heading3'])) | |
| lender_text = """ | |
| <b>Nom complet :</b> MOUSSIPI YA NDEME KLYDE JAIMAL<br/> | |
| <b>Type de piece d'identite :</b> PASSEPORT<br/> | |
| <b>Numero de la piece d'identite :</b> AA287935<br/> | |
| <b>Pays de residence :</b> FRANCE<br/> | |
| Ci-apres designe « le Preteur »,<br/> | |
| D'une part, | |
| """ | |
| story.append(Paragraph(lender_text, styles['Normal'])) | |
| story.append(Spacer(1, 10)) | |
| # II. LE BENEFICIAIRE | |
| story.append(Paragraph("<b>II. LE BENEFICIAIRE (EMPRUNTEUR)</b>", styles['Heading3'])) | |
| borrower_text = f""" | |
| <b>Nom complet :</b> {client_info['Nom_Complet']}<br/> | |
| <b>Date de naissance :</b> {client_info.get('Date_Naissance', 'N/A')}<br/> | |
| <b>Statut professionnel :</b> {client_info['Statut_Pro']}<br/> | |
| <b>Adresse :</b> {client_info.get('Adresse', 'N/A')}<br/> | |
| <b>Ville de residence :</b> {client_info['Ville']}<br/> | |
| <b>Numero de telephone :</b> {client_info.get('Telephone', 'N/A')}<br/> | |
| <b>Type de piece d'identite :</b> {client_info.get('Type_Piece_Identite', 'N/A')}<br/> | |
| <b>Numero de la piece d'identite :</b> {client_info.get('ID_Officiel', 'N/A')}<br/> | |
| Ci-apres designe « l'Emprunteur »,<br/> | |
| D'autre part, | |
| """ | |
| story.append(Paragraph(borrower_text, styles['Normal'])) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("Il a ete convenu et arrete ce qui suit :", styles['Normal'])) | |
| story.append(Spacer(1, 15)) | |
| # ARTICLES | |
| story.append(Paragraph("<b>ARTICLE 1 - OBJET DU CONTRAT</b>", styles['Heading4'])) | |
| story.append(Paragraph( | |
| "Le present contrat a pour objet l'octroi par le Preteur a l'Emprunteur d'un pret d'argent " | |
| "a interets, conformement aux dispositions du Code des Obligations Civiles et Commerciales " | |
| "du Senegal (COCC).", styles['NormalJustify'])) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("<b>ARTICLE 2 - CARACTERISTIQUES DU PRET</b>", styles['Heading4'])) | |
| data_carac = [ | |
| ["Offre commerciale", offre if offre else "N/A"], # [OK] Offre dans le tableau | |
| ["Montant du capital", f"{format_currency(loan_data['Montant_Capital'])} XOF"], | |
| ["Motif du pret", loan_data.get('Motif', 'Personnel')], | |
| ["Taux hebdomadaire", f"{loan_data['Taux_Hebdo']} %"], | |
| ["Duree", f"{loan_data['Duree_Semaines']} semaines"], | |
| ["Date de deblocage", loan_data['Date_Deblocage']], | |
| ["Date d'echeance finale", loan_data['Date_Fin']], | |
| ] | |
| t_carac = Table(data_carac, colWidths=[7*cm, 8*cm]) | |
| t_carac.setStyle(TableStyle([ | |
| ('GRID', (0, 0), (-1, -1), 0.5, colors.grey), | |
| ('BACKGROUND', (0, 0), (0, -1), colors.whitesmoke), | |
| ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#e8eef4')), | |
| ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), | |
| ])) | |
| story.append(t_carac) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("<b>ARTICLE 3 - TABLEAU D'AMORTISSEMENT</b>", styles['Heading4'])) | |
| data_amort = [["Date", "Versement", "Solde Restant"]] | |
| for _, row in df_amortissement.iterrows(): | |
| versement = int(row['Versement']) if pd.notna(row['Versement']) else 0 | |
| solde = int(row['Solde_Restant']) if pd.notna(row['Solde_Restant']) else 0 | |
| data_amort.append([ | |
| row['Date'], | |
| f"{versement:,}".replace(",", " "), | |
| f"{solde:,}".replace(",", " ") | |
| ]) | |
| if len(data_amort) > 20: | |
| data_amort = data_amort[:20] | |
| data_amort.append(["...", "...", "..."]) | |
| t_amort = Table(data_amort, colWidths=[4*cm, 5*cm, 5*cm]) | |
| t_amort.setStyle(TableStyle([ | |
| ('GRID', (0, 0), (-1, -1), 0.5, colors.grey), | |
| ('BACKGROUND', (0, 0), (-1, 0), colors.navy), | |
| ('TEXTCOLOR', (0, 0), (-1, 0), colors.white), | |
| ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), | |
| ])) | |
| story.append(t_amort) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("<b>ARTICLE 4 - OBLIGATION DE REMBOURSEMENT</b>", styles['Heading4'])) | |
| story.append(Paragraph( | |
| "L'Emprunteur s'engage de maniere ferme, irrevocable et inconditionnelle a rembourser " | |
| "l'integralite des sommes dues, meme en cas de perte d'emploi, difficultes financieres, " | |
| "maladie ou tout autre evenement personnel. AUCUNE EXCUSE OU CIRCONSTANCE NE POURRA " | |
| "JUSTIFIER UN NON-REMBOURSEMENT.", styles['NormalJustify'])) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("<b>ARTICLE 5 - RETARD DE REMBOURSEMENT</b>", styles['Heading4'])) | |
| story.append(Paragraph("Tout retard de paiement entraine immediatement, sans mise en demeure prealable :", styles['Normal'])) | |
| story.append(Paragraph("Des penalites de retard fixees a 5% du montant de l'echeance impayee par semaine de retard.", styles['Normal'], bulletText="•")) | |
| story.append(Paragraph("La possibilite pour le Preteur d'exiger le paiement immediat de la totalite du solde restant du.", styles['Normal'], bulletText="•")) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("<b>ARTICLE 6 - DECHEANCE DU TERME</b>", styles['Heading4'])) | |
| story.append(Paragraph( | |
| "Le Preteur pourra prononcer la decheance du terme et exiger le remboursement immediat " | |
| "de la totalite de la dette en cas de : non-paiement d'une seule echeance, fausse " | |
| "declaration de revenus ou d'identite, refus de cooperation de l'Emprunteur.", styles['NormalJustify'])) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("<b>ARTICLE 7 - DROIT DE RETRACTATION</b>", styles['Heading4'])) | |
| story.append(Paragraph( | |
| "L'Emprunteur dispose d'un delai de retractation de sept (7) jours calendaires a compter " | |
| "de la date de deblocage effectif des fonds. La retractation doit etre notifiee par ecrit. " | |
| "L'Emprunteur devra restituer l'integralite des fonds recus.", styles['NormalJustify'])) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("<b>ARTICLE 8 - DROIT APPLICABLE ET JURIDICTION COMPETENTE</b>", styles['Heading4'])) | |
| story.append(Paragraph( | |
| "Le present contrat est regi par le droit Senegalais. Tout litige releve de la competence " | |
| "exclusive des tribunaux de Dakar.", styles['NormalJustify'])) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("<b>ARTICLE 9 - ACCEPTATION</b>", styles['Heading4'])) | |
| story.append(Paragraph( | |
| "L'Emprunteur reconnait avoir lu, compris et accepte toutes les clauses du present contrat " | |
| "sans contrainte ni reserve.", styles['NormalJustify'])) | |
| story.append(Spacer(1, 20)) | |
| story.append(Paragraph(_ligne_fait_a(signature), styles['Normal'])) | |
| story.append(Spacer(1, 20)) | |
| if signature: | |
| story.append(_table_parties_signature( | |
| "LE PRETEUR", "MOUSSIPI YA NDEME KLYDE JAIMAL", | |
| "LE BENEFICIAIRE", f"{client_info['Nom_Complet']}", preteur_signe=bool(signature_preteur))) | |
| story.append(Spacer(1, 10)) | |
| story.extend(_bloc_signature_electronique(signature, styles)) | |
| if signature_preteur: | |
| story.append(Spacer(1, 10)) | |
| story.extend(_bloc_signature_electronique(signature_preteur, styles)) | |
| else: | |
| data_sign = [ | |
| ["LE PRETEUR", "LE BENEFICIAIRE"], | |
| ["MOUSSIPI YA NDEME KLYDE JAIMAL", f"{client_info['Nom_Complet']}"], | |
| ["", "Signature precedee de la mention manuscrite :"], | |
| ["Signature :", "« Lu et approuve »"], | |
| ["\n\n\n", "\n\n\n"] | |
| ] | |
| t_sign = Table(data_sign, colWidths=[8*cm, 8*cm]) | |
| story.append(t_sign) | |
| doc.build(story) | |
| buffer.seek(0) | |
| return buffer | |
| # ============================================================================ | |
| # 2. RECONNAISSANCE DE DETTE | |
| # ============================================================================ | |
| def generer_reconnaissance_dette(loan_data, client_info, signature=None, montant_en_lettres_auto=False): | |
| """`montant_en_lettres_auto` : remplit le montant en lettres sous le montant en chiffres | |
| (snapshots version >= 2 ; les anciens restent identiques octet pour octet). | |
| La mention « Bon pour... » n'est jamais remplie par le systeme : elle est saisie par le | |
| signataire lui-meme sur la page de signature (signature["mention_bon_pour"]).""" | |
| buffer = BytesIO() | |
| doc = SimpleDocTemplate(buffer, pagesize=A4, | |
| rightMargin=2*cm, leftMargin=2*cm, | |
| topMargin=2*cm, bottomMargin=2*cm, | |
| invariant=1) | |
| styles = get_common_styles() | |
| story = [] | |
| offre = loan_data.get('Offre', '') | |
| story.append(Paragraph("LETTRE DE RECONNAISSANCE DE DETTE", styles['TitleCenter'])) | |
| # [OK] Offre sous le titre | |
| story.append(Spacer(1, 6)) | |
| story.append(_offre_table(offre, styles)) | |
| story.append(Spacer(1, 15)) | |
| story.append(Paragraph("Je soussigne(e) :", styles['Normal'])) | |
| info_debtor = f""" | |
| <b>Nom et prenoms :</b> {client_info['Nom_Complet']}<br/> | |
| <b>Date de naissance :</b> {client_info.get('Date_Naissance', '................')}<br/> | |
| <b>Statut professionnel :</b> {client_info['Statut_Pro']}<br/> | |
| <b>Adresse :</b> {client_info.get('Adresse', '................')}<br/> | |
| <b>Ville de residence :</b> {client_info['Ville']}<br/> | |
| <b>Numero de telephone :</b> {client_info.get('Telephone', '................')}<br/> | |
| <b>Type de piece d'identite :</b> {client_info.get('Type_Piece_Identite', '................')}<br/> | |
| <b>Numero de la piece d'identite :</b> {client_info.get('ID_Officiel', '................')}<br/> | |
| """ | |
| story.append(Paragraph(info_debtor, styles['Normal'])) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph( | |
| "reconnais, librement, expressement et sans aucune contrainte, devoir la somme " | |
| "ci-apres indiquee a :", styles['NormalJustify'])) | |
| story.append(Paragraph( | |
| "<b>Monsieur MOUSSIPI YA NDEME KLYDE JAIMAL</b>, titulaire du passeport n° AA287935, " | |
| "residant en France, ci-apres designe « le Preteur ».", styles['Normal'])) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph( | |
| f"Je reconnais devoir au Preteur la somme totale de " | |
| f"<b>{format_currency(loan_data['Montant_Total'])} FCFA</b> " | |
| f"au titre de l'offre <b>{offre if offre else 'N/A'}</b>.", # [OK] Offre dans le corps | |
| styles['Normal'])) | |
| if montant_en_lettres_auto: | |
| from DocumentGen.nombres_fr import montant_en_lettres | |
| story.append(Paragraph(f"({montant_en_lettres(loan_data['Montant_Total'])} Francs CFA).", styles['Normal'])) | |
| else: | |
| story.append(Paragraph("(......................................................................... Francs CFA).", styles['Normal'])) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph( | |
| f"Cette somme correspond au montant total a rembourser au titre du Contrat de pret a " | |
| f"interets n° {loan_data['ID_Pret']}, incluant le capital, les interets, les penalites " | |
| f"eventuelles et tous frais prevus audit contrat.", styles['NormalJustify'])) | |
| story.append(Spacer(1, 5)) | |
| story.append(Paragraph( | |
| "Je m'engage de maniere ferme, irrevocable et inconditionnelle a rembourser integralement " | |
| "ladite somme selon les modalites prevues dans le contrat de pret.", styles['NormalJustify'])) | |
| story.append(Spacer(1, 5)) | |
| story.append(Paragraph( | |
| "Je reconnais expressement qu'aucune difficulte personnelle, professionnelle, financiere " | |
| "ou medicale, aucune perte d'emploi ou baisse de revenus, ne saurait en aucun cas justifier " | |
| "un retard ou un refus de paiement.", styles['NormalJustify'])) | |
| story.append(Spacer(1, 5)) | |
| story.append(Paragraph( | |
| "En cas de non-respect de mes obligations, le Preteur pourra exiger le paiement immediat " | |
| "de la totalite des sommes restant dues, appliquer les penalites de retard prevues au " | |
| "contrat de pret et engager toute procedure de recouvrement.", styles['NormalJustify'])) | |
| story.append(Spacer(1, 5)) | |
| story.append(Paragraph( | |
| "Je reconnais avoir lu, compris et accepte l'integralite des engagements resultant de la " | |
| "presente reconnaissance de dette, que je signe en toute connaissance de cause.", styles['NormalJustify'])) | |
| story.append(Spacer(1, 20)) | |
| story.append(Paragraph(_ligne_fait_a(signature), styles['Normal'])) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("<b>L'EMPRUNTEUR (DEBITEUR)</b>", styles['Normal'])) | |
| story.append(Paragraph(f"Nom : {client_info['Nom_Complet']}", styles['Normal'])) | |
| story.append(Spacer(1, 5)) | |
| mention = str((signature or {}).get("mention_bon_pour", "") or "").strip() | |
| if mention: | |
| from xml.sax.saxutils import escape | |
| story.append(Paragraph(f"<i>« {escape(mention)} »</i>", styles['Normal'])) | |
| story.append(Paragraph("<font size=7>(mention saisie par le signataire lui-meme lors de la " | |
| "signature electronique)</font>", styles['Normal'])) | |
| else: | |
| story.append(Paragraph( | |
| "<i>« Bon pour reconnaissance de dette de la somme de " | |
| "......................... Francs CFA, que je m'engage a rembourser integralement »</i>", | |
| styles['Normal'])) | |
| story.append(Spacer(1, 15)) | |
| if signature: | |
| story.extend(_bloc_signature_electronique(signature, styles)) | |
| else: | |
| story.append(Paragraph("Signature :", styles['Normal'])) | |
| doc.build(story) | |
| buffer.seek(0) | |
| return buffer | |
| # ============================================================================ | |
| # 3. CONTRAT DE CAUTION | |
| # ============================================================================ | |
| def generer_contrat_caution(loan_data, garant_info, signature=None, signature_preteur=None): | |
| buffer = BytesIO() | |
| doc = SimpleDocTemplate(buffer, pagesize=A4, | |
| rightMargin=2*cm, leftMargin=2*cm, | |
| topMargin=2*cm, bottomMargin=2*cm, | |
| invariant=1) | |
| styles = get_common_styles() | |
| story = [] | |
| offre = loan_data.get('Offre', '') | |
| story.append(Paragraph("CONTRAT DE CAUTION SOLIDAIRE", styles['TitleCenter'])) | |
| story.append(Paragraph( | |
| f"Relatif au Contrat de pret a interets n° {loan_data['ID_Pret']}", | |
| styles['TitleCenter'])) | |
| # [OK] Offre sous le titre | |
| story.append(Spacer(1, 6)) | |
| story.append(_offre_table(offre, styles)) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("Entre les soussignes :", styles['Normal'])) | |
| story.append(Paragraph("<b>I. LE PRETEUR</b>", styles['Heading3'])) | |
| story.append(Paragraph( | |
| "Nom complet : MOUSSIPI YA NDEME KLYDE JAIMAL<br/>" | |
| "Numero piece : AA287935 (Passeport)<br/>" | |
| "Pays : FRANCE<br/>" | |
| "Ci-apres designe « le Preteur »", styles['Normal'])) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("<b>II. LE GARANT (CAUTION)</b>", styles['Heading3'])) | |
| garant_txt = f""" | |
| Nom complet : {garant_info['Nom_Complet']}<br/> | |
| Statut professionnel : {garant_info.get('Statut_Pro', 'N/A')}<br/> | |
| Ville : {garant_info.get('Ville', 'N/A')}<br/> | |
| Telephone : {garant_info.get('Telephone', 'N/A')}<br/> | |
| Ci-apres designe « le Garant », | |
| """ | |
| story.append(Paragraph(garant_txt, styles['Normal'])) | |
| story.append(Spacer(1, 15)) | |
| story.append(Paragraph("<b>ARTICLE 1 - OBJET DU CONTRAT</b>", styles['Heading4'])) | |
| story.append(Paragraph( | |
| f"Le Garant s'engage a garantir l'execution integrale des obligations de remboursement " | |
| f"de l'Emprunteur, beneficiaire du Contrat de pret n° {loan_data['ID_Pret']} " | |
| f"souscrit dans le cadre de l'offre <b>{offre if offre else 'N/A'}</b>.", # [OK] Offre dans l'objet | |
| styles['NormalJustify'])) | |
| story.append(Paragraph("<b>ARTICLE 2 - MONTANT ET ETENDUE</b>", styles['Heading4'])) | |
| story.append(Paragraph( | |
| "La garantie couvre l'integralite des sommes dues : capital, interets, penalites de retard. " | |
| "L'engagement est illimite jusqu'au paiement total.", styles['NormalJustify'])) | |
| story.append(Paragraph("<b>ARTICLE 3 - NATURE DE L'ENGAGEMENT</b>", styles['Heading4'])) | |
| story.append(Paragraph( | |
| "Le Garant se porte caution solidaire et renonce expressement au benefice de discussion " | |
| "et au benefice de division. Le Preteur pourra reclamer directement au Garant le paiement " | |
| "sans mise en demeure prealable de l'Emprunteur.", styles['NormalJustify'])) | |
| story.append(Paragraph("<b>ARTICLE 4 - EXIGIBILITE IMMEDIATE</b>", styles['Heading4'])) | |
| story.append(Paragraph( | |
| "En cas de non-remboursement d'une seule echeance par l'Emprunteur, le Preteur pourra " | |
| "exiger immediatement du Garant le paiement de la totalite des sommes dues.", styles['NormalJustify'])) | |
| story.append(Paragraph("<b>ARTICLES 5 A 9</b>", styles['Heading4'])) | |
| story.append(Paragraph( | |
| "L'engagement demeure valable jusqu'au remboursement integral. Le present contrat est " | |
| "regi par le droit senegalais (Tribunaux de Dakar). Le Garant reconnait avoir lu et " | |
| "accepte toutes les clauses.", styles['NormalJustify'])) | |
| story.append(Spacer(1, 20)) | |
| story.append(Paragraph(_ligne_fait_a(signature), styles['Normal'])) | |
| if signature: | |
| story.append(_table_parties_signature( | |
| "LE PRETEUR", "MOUSSIPI YA NDEME K.J.", | |
| "LE GARANT (CAUTION)", f"{garant_info['Nom_Complet']}", preteur_signe=bool(signature_preteur))) | |
| story.append(Spacer(1, 10)) | |
| story.extend(_bloc_signature_electronique(signature, styles)) | |
| if signature_preteur: | |
| story.append(Spacer(1, 10)) | |
| story.extend(_bloc_signature_electronique(signature_preteur, styles)) | |
| else: | |
| data_sign = [ | |
| ["LE PRETEUR", "LE GARANT (CAUTION)"], | |
| ["MOUSSIPI YA NDEME K.J.", f"{garant_info['Nom_Complet']}"], | |
| ["", "Signature precedee de la mention manuscrite :"], | |
| ["Signature :", "« Bon pour caution solidaire,\nrenonciation aux benefices de discussion »"], | |
| ["\n\n", "\n\n"] | |
| ] | |
| t_sign = Table(data_sign, colWidths=[8*cm, 8*cm]) | |
| story.append(t_sign) | |
| doc.build(story) | |
| buffer.seek(0) | |
| return buffer |