|
@@ -25,6 +25,8 @@ function Modal({ title, onClose, children }) {
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const daysInMonth = (month, year) => new Date(year, month, 0).getDate()
|
|
|
|
|
+
|
|
|
export default function LeaseDetail() {
|
|
export default function LeaseDetail() {
|
|
|
const { id } = useParams()
|
|
const { id } = useParams()
|
|
|
const [lease, setLease] = useState(null)
|
|
const [lease, setLease] = useState(null)
|
|
@@ -46,6 +48,9 @@ export default function LeaseDetail() {
|
|
|
payment_method: 'virement',
|
|
payment_method: 'virement',
|
|
|
notes: '',
|
|
notes: '',
|
|
|
apply_regularization: false,
|
|
apply_regularization: false,
|
|
|
|
|
+ is_prorata: false,
|
|
|
|
|
+ prorata_days: '',
|
|
|
|
|
+ prorata_total_days: '',
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const [revForm, setRevForm] = useState({
|
|
const [revForm, setRevForm] = useState({
|
|
@@ -78,6 +83,9 @@ export default function LeaseDetail() {
|
|
|
...form,
|
|
...form,
|
|
|
lease_id: Number(id),
|
|
lease_id: Number(id),
|
|
|
charge_regularization: form.apply_regularization ? pendingReg.total : 0,
|
|
charge_regularization: form.apply_regularization ? pendingReg.total : 0,
|
|
|
|
|
+ is_prorata: form.is_prorata ? 1 : 0,
|
|
|
|
|
+ prorata_days: form.is_prorata ? form.prorata_days : null,
|
|
|
|
|
+ prorata_total_days: form.is_prorata ? form.prorata_total_days : null,
|
|
|
}
|
|
}
|
|
|
await api.post('/payments', payload)
|
|
await api.post('/payments', payload)
|
|
|
toast.success('Paiement enregistré !')
|
|
toast.success('Paiement enregistré !')
|
|
@@ -284,7 +292,10 @@ export default function LeaseDetail() {
|
|
|
<td className="px-6 py-4 font-medium text-gray-900">{MONTHS[p.period_month-1]} {p.period_year}</td>
|
|
<td className="px-6 py-4 font-medium text-gray-900">{MONTHS[p.period_month-1]} {p.period_year}</td>
|
|
|
<td className="px-6 py-4 text-gray-600">{fmtDate(p.payment_date)}</td>
|
|
<td className="px-6 py-4 text-gray-600">{fmtDate(p.payment_date)}</td>
|
|
|
<td className="px-6 py-4 text-gray-600 capitalize">{p.payment_method}</td>
|
|
<td className="px-6 py-4 text-gray-600 capitalize">{p.payment_method}</td>
|
|
|
- <td className="px-6 py-4 text-right text-gray-900">{fmt(p.rent_paid)}</td>
|
|
|
|
|
|
|
+ <td className="px-6 py-4 text-right text-gray-900">
|
|
|
|
|
+ {fmt(p.rent_paid)}
|
|
|
|
|
+ {p.is_prorata ? <span className="ml-1 text-xs text-amber-600" title={`Prorata ${p.prorata_days}/${p.prorata_total_days} j`}>⅟</span> : null}
|
|
|
|
|
+ </td>
|
|
|
<td className="px-6 py-4 text-right text-gray-600">{fmt(p.charges_paid)}</td>
|
|
<td className="px-6 py-4 text-right text-gray-600">{fmt(p.charges_paid)}</td>
|
|
|
<td className="px-6 py-4 text-right text-sm">
|
|
<td className="px-6 py-4 text-right text-sm">
|
|
|
{reg !== 0
|
|
{reg !== 0
|
|
@@ -350,6 +361,67 @@ export default function LeaseDetail() {
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+ {/* Prorata */}
|
|
|
|
|
+ <label className="flex items-center gap-3 cursor-pointer select-none">
|
|
|
|
|
+ <input type="checkbox" checked={form.is_prorata}
|
|
|
|
|
+ onChange={e => {
|
|
|
|
|
+ const enabled = e.target.checked
|
|
|
|
|
+ const total = daysInMonth(form.period_month, form.period_year)
|
|
|
|
|
+ setForm(f => ({
|
|
|
|
|
+ ...f,
|
|
|
|
|
+ is_prorata: enabled,
|
|
|
|
|
+ prorata_total_days: enabled ? total : '',
|
|
|
|
|
+ prorata_days: enabled ? total : '',
|
|
|
|
|
+ rent_paid: enabled ? lease.rent_amount : lease.rent_amount,
|
|
|
|
|
+ charges_paid: enabled ? lease.charges_amount : lease.charges_amount,
|
|
|
|
|
+ }))
|
|
|
|
|
+ }} />
|
|
|
|
|
+ <span className="text-sm font-medium text-gray-700">Mois incomplet — calculer au prorata</span>
|
|
|
|
|
+ </label>
|
|
|
|
|
+
|
|
|
|
|
+ {form.is_prorata && (
|
|
|
|
|
+ <div className="bg-amber-50 border border-amber-200 rounded-xl p-4 space-y-3">
|
|
|
|
|
+ <p className="text-xs text-amber-700 font-medium">Loyer et charges recalculés au prorata des jours occupés</p>
|
|
|
|
|
+ <div className="grid grid-cols-2 gap-3">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <label className="label text-xs">Jours occupés *</label>
|
|
|
|
|
+ <input className="input" type="number" min="1"
|
|
|
|
|
+ max={daysInMonth(form.period_month, form.period_year)}
|
|
|
|
|
+ value={form.prorata_days}
|
|
|
|
|
+ onChange={e => {
|
|
|
|
|
+ const days = Number(e.target.value)
|
|
|
|
|
+ const total = form.prorata_total_days || daysInMonth(form.period_month, form.period_year)
|
|
|
|
|
+ const ratio = days / total
|
|
|
|
|
+ setForm(f => ({
|
|
|
|
|
+ ...f,
|
|
|
|
|
+ prorata_days: days,
|
|
|
|
|
+ rent_paid: (lease.rent_amount * ratio).toFixed(2),
|
|
|
|
|
+ charges_paid: (lease.charges_amount * ratio).toFixed(2),
|
|
|
|
|
+ }))
|
|
|
|
|
+ }} />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <label className="label text-xs">Jours dans le mois</label>
|
|
|
|
|
+ <input className="input" type="number" min="28" max="31" value={form.prorata_total_days}
|
|
|
|
|
+ onChange={e => {
|
|
|
|
|
+ const total = Number(e.target.value)
|
|
|
|
|
+ const days = form.prorata_days || total
|
|
|
|
|
+ const ratio = days / total
|
|
|
|
|
+ setForm(f => ({
|
|
|
|
|
+ ...f,
|
|
|
|
|
+ prorata_total_days: total,
|
|
|
|
|
+ rent_paid: (lease.rent_amount * ratio).toFixed(2),
|
|
|
|
|
+ charges_paid: (lease.charges_amount * ratio).toFixed(2),
|
|
|
|
|
+ }))
|
|
|
|
|
+ }} />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <p className="text-xs text-amber-600">
|
|
|
|
|
+ Loyer plein : {fmt(lease.rent_amount)} · Charges pleines : {fmt(lease.charges_amount)}
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )}
|
|
|
|
|
+
|
|
|
{pendingReg.pending.length > 0 && (
|
|
{pendingReg.pending.length > 0 && (
|
|
|
<label className={`flex items-start gap-3 p-3 rounded-lg border cursor-pointer ${form.apply_regularization ? (pendingReg.total >= 0 ? 'bg-green-50 border-green-300' : 'bg-orange-50 border-orange-300') : 'bg-gray-50 border-gray-200'}`}>
|
|
<label className={`flex items-start gap-3 p-3 rounded-lg border cursor-pointer ${form.apply_regularization ? (pendingReg.total >= 0 ? 'bg-green-50 border-green-300' : 'bg-orange-50 border-orange-300') : 'bg-gray-50 border-gray-200'}`}>
|
|
|
<input type="checkbox" className="mt-0.5" checked={form.apply_regularization}
|
|
<input type="checkbox" className="mt-0.5" checked={form.apply_regularization}
|