| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- const express = require('express');
- const PDFDocument = require('pdfkit');
- const pool = require('../db');
- const { authMiddleware } = require('../auth');
- const router = express.Router();
- router.use(authMiddleware);
- const MONTHS_FR = ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'];
- router.get('/:paymentId', async (req, res) => {
- const payment = (await pool.query(`
- SELECT p.*,
- l.rent_amount, l.charges_amount, l.deposit_amount,
- pr.name as property_name, pr.address as property_address, pr.city as property_city, pr.zip_code as property_zip,
- t.first_name as tenant_first_name, t.last_name as tenant_last_name, t.email as tenant_email,
- -- Propriétaire bailleur (owners table en priorité, fallback profil utilisateur)
- o.type as o_type,
- o.first_name as o_first_name, o.last_name as o_last_name,
- o.company_name as o_company_name, o.siret as o_siret,
- o.address as o_address, o.zip_code as o_zip, o.city as o_city,
- o.email as o_email, o.phone as o_phone,
- -- Fallback gestionnaire
- u.name as u_name, u.email as u_email,
- u.first_name as u_first_name, u.last_name as u_last_name,
- u.address as u_address, u.phone as u_phone
- FROM payments p
- JOIN leases l ON p.lease_id = l.id
- JOIN properties pr ON l.property_id = pr.id
- JOIN tenants t ON l.tenant_id = t.id
- JOIN users u ON p.user_id = u.id
- LEFT JOIN owners o ON l.owner_id = o.id
- WHERE p.id = $1 AND p.user_id = $2
- `, [req.params.paymentId, req.userId])).rows[0];
- if (!payment) return res.status(404).json({ error: 'Paiement non trouvé' });
- // Résoudre le nom du bailleur : owner en priorité, sinon profil gestionnaire
- let ownerFullName, ownerAddress, ownerEmail, ownerPhone, ownerSiret;
- if (payment.o_type) {
- ownerFullName = payment.o_type === 'morale'
- ? payment.o_company_name
- : `${payment.o_first_name || ''} ${payment.o_last_name || ''}`.trim();
- ownerSiret = payment.o_siret;
- ownerAddress = [payment.o_address, payment.o_zip && payment.o_city ? `${payment.o_zip} ${payment.o_city}` : (payment.o_zip || payment.o_city)].filter(Boolean).join(', ');
- ownerEmail = payment.o_email;
- ownerPhone = payment.o_phone;
- } else {
- ownerFullName = (payment.u_first_name && payment.u_last_name)
- ? `${payment.u_first_name} ${payment.u_last_name}`
- : payment.u_name;
- ownerSiret = null;
- ownerAddress = payment.u_address;
- ownerEmail = payment.u_email;
- ownerPhone = payment.u_phone;
- }
- const doc = new PDFDocument({ size: 'A4', margin: 50 });
- res.setHeader('Content-Type', 'application/pdf');
- res.setHeader('Content-Disposition', `attachment; filename=quittance_${payment.period_year}_${String(payment.period_month).padStart(2,'0')}.pdf`);
- doc.pipe(res);
- const monthName = MONTHS_FR[payment.period_month - 1];
- const total = payment.rent_paid + payment.charges_paid;
- // Header
- doc.fontSize(22).font('Helvetica-Bold').text('QUITTANCE DE LOYER', { align: 'center' });
- doc.moveDown(0.3);
- doc.fontSize(14).font('Helvetica').text(`Période : ${monthName} ${payment.period_year}`, { align: 'center' });
- doc.moveDown(1.5);
- // Separator
- doc.moveTo(50, doc.y).lineTo(545, doc.y).strokeColor('#2563eb').lineWidth(2).stroke();
- doc.moveDown(1);
- // Bailleur
- doc.fontSize(12).font('Helvetica-Bold').text('BAILLEUR');
- doc.font('Helvetica').fontSize(11).text(ownerFullName);
- if (ownerSiret) doc.text(`SIRET : ${ownerSiret}`);
- if (ownerAddress) doc.text(ownerAddress);
- if (ownerEmail) doc.text(ownerEmail);
- if (ownerPhone) doc.text(ownerPhone);
- doc.moveDown(1);
- // Locataire
- doc.fontSize(12).font('Helvetica-Bold').text('LOCATAIRE');
- doc.font('Helvetica').fontSize(11)
- .text(`${payment.tenant_first_name} ${payment.tenant_last_name}`)
- .text(payment.tenant_email || '');
- doc.moveDown(1);
- // Bien loué
- doc.fontSize(12).font('Helvetica-Bold').text('BIEN LOUÉ');
- doc.font('Helvetica').fontSize(11)
- .text(payment.property_name)
- .text(`${payment.property_address}`)
- .text(`${payment.property_zip} ${payment.property_city}`);
- doc.moveDown(1);
- // Separator
- doc.moveTo(50, doc.y).lineTo(545, doc.y).strokeColor('#e5e7eb').lineWidth(1).stroke();
- doc.moveDown(1);
- // Détail paiement
- doc.fontSize(12).font('Helvetica-Bold').text('DÉTAIL DU RÈGLEMENT');
- doc.moveDown(0.5);
- const col1 = 50, col2 = 400;
- const drawRow = (label, value, bold = false, color = '#000000') => {
- const y = doc.y;
- doc.font(bold ? 'Helvetica-Bold' : 'Helvetica').fontSize(11).fillColor(color);
- doc.text(label, col1, y);
- doc.text(`${Number(value).toFixed(2)} €`, col2, y, { width: 100, align: 'right' });
- doc.fillColor('#000000');
- doc.moveDown(0.6);
- };
- const isProrata = payment.is_prorata && payment.prorata_days && payment.prorata_total_days;
- if (isProrata) {
- drawRow(`Loyer au prorata (${payment.prorata_days}/${payment.prorata_total_days} jours)`, payment.rent_paid);
- } else {
- drawRow('Loyer hors charges', payment.rent_paid);
- }
- drawRow('Charges locatives', payment.charges_paid);
- const reg = payment.charge_regularization || 0;
- if (reg !== 0) {
- // positive reg = trop-perçu → déduit du loyer (bonne nouvelle pour locataire)
- // negative reg = complément → ajouté au loyer
- const regLabel = reg > 0
- ? `Régularisation de charges — trop-perçu (déduit)`
- : `Régularisation de charges — complément appelé`;
- drawRow(regLabel, -reg, false, reg > 0 ? '#15803d' : '#c2410c');
- }
- doc.moveTo(50, doc.y).lineTo(545, doc.y).strokeColor('#e5e7eb').lineWidth(0.5).stroke();
- doc.moveDown(0.3);
- const netTotal = payment.rent_paid + payment.charges_paid - reg;
- drawRow('TOTAL NET À PAYER', netTotal, true);
- doc.moveDown(0.5);
- // Mode et date de paiement
- const paymentMethods = { virement: 'Virement bancaire', cheque: 'Chèque', especes: 'Espèces', prelevement: 'Prélèvement automatique' };
- doc.font('Helvetica').fontSize(11)
- .text(`Mode de paiement : ${paymentMethods[payment.payment_method] || payment.payment_method}`)
- .text(`Date de paiement : ${new Date(payment.payment_date).toLocaleDateString('fr-FR')}`);
- if (payment.notes) {
- doc.moveDown(0.5).text(`Notes : ${payment.notes}`);
- }
- doc.moveDown(2);
- // Attestation
- doc.moveTo(50, doc.y).lineTo(545, doc.y).strokeColor('#e5e7eb').lineWidth(1).stroke();
- doc.moveDown(1);
- doc.font('Helvetica').fontSize(10).fillColor('#6b7280')
- .text(
- `Je soussigné(e) ${ownerFullName}, bailleur, donne quittance à ${payment.tenant_first_name} ${payment.tenant_last_name} ` +
- `pour la somme de ${netTotal.toFixed(2)} € correspondant au paiement du loyer et des charges du logement situé ` +
- `${payment.property_address}, ${payment.property_zip} ${payment.property_city}, ` +
- `pour la période de ${monthName} ${payment.period_year}.`,
- { align: 'justify' }
- );
- doc.moveDown(3);
- doc.fontSize(11).fillColor('#000000').text(`Fait le ${new Date().toLocaleDateString('fr-FR')}`);
- doc.moveDown(2);
- doc.text('Signature du bailleur :', { continued: false });
- doc.moveDown(1);
- doc.moveTo(50, doc.y).lineTo(200, doc.y).strokeColor('#000').lineWidth(1).stroke();
- doc.end();
- });
- module.exports = router;
|