|
|
@@ -247,15 +247,26 @@ router.get('/bilan/:property_id/:year', async (req, res) => {
|
|
|
|
|
|
const totalProvisions = leaseBilans.reduce((s, l) => s + l.provisions_percues, 0);
|
|
|
|
|
|
+ // Fetch existing closures for this property/year to mark closed leases.
|
|
|
+ const closures = (await pool.query(
|
|
|
+ 'SELECT id, lease_id, amount, applied_payment_id FROM charge_regularizations WHERE user_id = $1 AND property_id = $2 AND year = $3',
|
|
|
+ [req.userId, property_id, parseInt(year, 10)]
|
|
|
+ )).rows;
|
|
|
+ const closureByLeaseId = Object.fromEntries(closures.map(c => [c.lease_id, c]));
|
|
|
+
|
|
|
// Compute each tenant's share based on actual occupancy in the year.
|
|
|
const bilans = leaseBilans.map(l => {
|
|
|
const quote_part_charges = totalChargesReelles * l.occupancy_ratio;
|
|
|
const trop_percu = l.provisions_percues - quote_part_charges;
|
|
|
+ const closure = closureByLeaseId[l.lease_id] || null;
|
|
|
return {
|
|
|
...l,
|
|
|
ratio_percent: l.occupancy_ratio_percent,
|
|
|
quote_part_charges: round2(quote_part_charges),
|
|
|
- trop_percu: round2(trop_percu)
|
|
|
+ trop_percu: round2(trop_percu),
|
|
|
+ closed: !!closure,
|
|
|
+ regularization_id: closure?.id || null,
|
|
|
+ applied_payment_id: closure?.applied_payment_id || null,
|
|
|
};
|
|
|
});
|
|
|
|
|
|
@@ -612,4 +623,39 @@ router.post('/bilan/:property_id/:year/close', async (req, res) => {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
+// Cancel (undo) a lease closure for a given property/year
|
|
|
+router.delete('/bilan/:property_id/:year/close/:lease_id', async (req, res) => {
|
|
|
+ const { property_id, year, lease_id } = req.params;
|
|
|
+
|
|
|
+ const prop = (await pool.query('SELECT id FROM properties WHERE id = $1 AND user_id = $2', [property_id, req.userId])).rows[0];
|
|
|
+ if (!prop) return res.status(403).json({ error: 'Bien non autorisé' });
|
|
|
+
|
|
|
+ const reg = (await pool.query(
|
|
|
+ 'SELECT id, applied_payment_id FROM charge_regularizations WHERE user_id = $1 AND lease_id = $2 AND year = $3 LIMIT 1',
|
|
|
+ [req.userId, parseInt(lease_id, 10), parseInt(year, 10)]
|
|
|
+ )).rows[0];
|
|
|
+
|
|
|
+ if (!reg) return res.status(404).json({ error: 'Aucune clôture trouvée pour ce bail et cette année.' });
|
|
|
+
|
|
|
+ const client = await pool.connect();
|
|
|
+ try {
|
|
|
+ await client.query('BEGIN');
|
|
|
+ // If the regularization was applied to a payment, reset that payment's charge_regularization to 0.
|
|
|
+ if (reg.applied_payment_id) {
|
|
|
+ await client.query(
|
|
|
+ 'UPDATE payments SET charge_regularization = 0 WHERE id = $1 AND user_id = $2',
|
|
|
+ [reg.applied_payment_id, req.userId]
|
|
|
+ );
|
|
|
+ }
|
|
|
+ await client.query('DELETE FROM charge_regularizations WHERE id = $1', [reg.id]);
|
|
|
+ await client.query('COMMIT');
|
|
|
+ res.json({ success: true });
|
|
|
+ } catch (e) {
|
|
|
+ await client.query('ROLLBACK');
|
|
|
+ throw e;
|
|
|
+ } finally {
|
|
|
+ client.release();
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
module.exports = router;
|