|
@@ -341,6 +341,102 @@ export default function LeaseDetail() {
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
|
|
+ {/* Récapitulatif annuel */}
|
|
|
|
|
+ {payments.length > 0 && (() => {
|
|
|
|
|
+ const byYear = {}
|
|
|
|
|
+ payments.forEach(p => {
|
|
|
|
|
+ const y = p.period_year
|
|
|
|
|
+ if (!byYear[y]) byYear[y] = { rent: 0, charges: 0, reg: 0, count: 0 }
|
|
|
|
|
+ byYear[y].rent += p.rent_paid
|
|
|
|
|
+ byYear[y].charges += p.charges_paid
|
|
|
|
|
+ byYear[y].reg += p.charge_regularization || 0
|
|
|
|
|
+ byYear[y].count += 1
|
|
|
|
|
+ })
|
|
|
|
|
+ const years = Object.keys(byYear).sort((a, b) => b - a)
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="card mb-6">
|
|
|
|
|
+ <h2 className="text-base font-semibold text-gray-800 mb-3">📊 Récapitulatif annuel</h2>
|
|
|
|
|
+ {/* Desktop */}
|
|
|
|
|
+ <div className="hidden md:block overflow-x-auto">
|
|
|
|
|
+ <table className="w-full text-sm">
|
|
|
|
|
+ <thead>
|
|
|
|
|
+ <tr className="border-b text-gray-500 text-left">
|
|
|
|
|
+ <th className="pb-2 pr-6 font-medium">Année</th>
|
|
|
|
|
+ <th className="pb-2 pr-6 font-medium text-right">Loyers encaissés</th>
|
|
|
|
|
+ <th className="pb-2 pr-6 font-medium text-right">Charges encaissées</th>
|
|
|
|
|
+ <th className="pb-2 pr-6 font-medium text-right">Régularisations</th>
|
|
|
|
|
+ <th className="pb-2 font-medium text-right">Total net</th>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </thead>
|
|
|
|
|
+ <tbody className="divide-y divide-gray-50">
|
|
|
|
|
+ {years.map(y => {
|
|
|
|
|
+ const d = byYear[y]
|
|
|
|
|
+ const net = d.rent + d.charges - d.reg
|
|
|
|
|
+ return (
|
|
|
|
|
+ <tr key={y} className="hover:bg-gray-50">
|
|
|
|
|
+ <td className="py-2 pr-6 font-semibold text-gray-800">{y}</td>
|
|
|
|
|
+ <td className="py-2 pr-6 text-right text-gray-700">{fmt(d.rent)}</td>
|
|
|
|
|
+ <td className="py-2 pr-6 text-right text-gray-500">{fmt(d.charges)}</td>
|
|
|
|
|
+ <td className="py-2 pr-6 text-right">
|
|
|
|
|
+ {d.reg !== 0
|
|
|
|
|
+ ? <span className={d.reg > 0 ? 'text-green-600' : 'text-orange-600'}>
|
|
|
|
|
+ {d.reg > 0 ? `−${fmt(d.reg)}` : `+${fmt(Math.abs(d.reg))}`}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ : <span className="text-gray-300">—</span>}
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td className="py-2 text-right font-bold text-blue-700">{fmt(net)}</td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ )
|
|
|
|
|
+ })}
|
|
|
|
|
+ </tbody>
|
|
|
|
|
+ <tfoot className="border-t-2 border-gray-200">
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td className="pt-2 pr-6 font-bold text-gray-900">Total</td>
|
|
|
|
|
+ <td className="pt-2 pr-6 text-right font-bold text-gray-900">
|
|
|
|
|
+ {fmt(years.reduce((s, y) => s + byYear[y].rent, 0))}
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td className="pt-2 pr-6 text-right font-bold text-gray-900">
|
|
|
|
|
+ {fmt(years.reduce((s, y) => s + byYear[y].charges, 0))}
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td className="pt-2 pr-6 text-right font-bold text-gray-900">
|
|
|
|
|
+ {fmt(years.reduce((s, y) => s + byYear[y].reg, 0))}
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td className="pt-2 text-right font-bold text-blue-800 text-base">
|
|
|
|
|
+ {fmt(years.reduce((s, y) => s + byYear[y].rent + byYear[y].charges - byYear[y].reg, 0))}
|
|
|
|
|
+ </td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </tfoot>
|
|
|
|
|
+ </table>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {/* Mobile */}
|
|
|
|
|
+ <div className="md:hidden space-y-2">
|
|
|
|
|
+ {years.map(y => {
|
|
|
|
|
+ const d = byYear[y]
|
|
|
|
|
+ const net = d.rent + d.charges - d.reg
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div key={y} className="bg-gray-50 rounded-xl px-4 py-3">
|
|
|
|
|
+ <div className="flex justify-between items-center mb-1">
|
|
|
|
|
+ <span className="font-bold text-gray-800">{y}</span>
|
|
|
|
|
+ <span className="font-bold text-blue-700">{fmt(net)}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="text-xs text-gray-500 space-y-0.5">
|
|
|
|
|
+ <div className="flex justify-between"><span>Loyers</span><span>{fmt(d.rent)}</span></div>
|
|
|
|
|
+ <div className="flex justify-between"><span>Charges</span><span>{fmt(d.charges)}</span></div>
|
|
|
|
|
+ {d.reg !== 0 && (
|
|
|
|
|
+ <div className={`flex justify-between font-medium ${d.reg > 0 ? 'text-green-600' : 'text-orange-600'}`}>
|
|
|
|
|
+ <span>Régularisations</span>
|
|
|
|
|
+ <span>{d.reg > 0 ? `−${fmt(d.reg)}` : `+${fmt(Math.abs(d.reg))}`}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )
|
|
|
|
|
+ })}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )
|
|
|
|
|
+ })()}
|
|
|
|
|
+
|
|
|
{/* Paiements */}
|
|
{/* Paiements */}
|
|
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-4">
|
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-4">
|
|
|
<h2 className="text-xl font-bold text-gray-900">💰 Historique des paiements</h2>
|
|
<h2 className="text-xl font-bold text-gray-900">💰 Historique des paiements</h2>
|