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;