Add realistic loan amortization with currentBalance back-calculation
- Extend schema with effectiveAnnualRate, totalInterest, totalAmount - Back-calculate paid months from currentBalance and rebuild schedule - Allow schedule calculation from termMonths without monthlyPayment - Handle NaN form values gracefully - Show effective rate, total interest and total amount in UI - Add amortization unit tests
This commit is contained in:
@@ -8,7 +8,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@
|
||||
import { formatAmount } from "@/lib/format";
|
||||
import { LoanFormDialog } from "@/components/loans/LoanFormDialog";
|
||||
import { AmortizationSchedule } from "@/components/loans/AmortizationSchedule";
|
||||
import { buildSchedule, currentBalanceFromSchedule } from "@convex-lib/amortization";
|
||||
import { buildSchedule } from "@convex-lib/amortization";
|
||||
|
||||
export function LoansPage() {
|
||||
const loans = useQuery(api.loans.list);
|
||||
@@ -25,9 +25,9 @@ export function LoansPage() {
|
||||
startDate,
|
||||
monthlyPayment: loan.monthlyPayment,
|
||||
termMonths: loan.termMonths,
|
||||
currentBalance: loan.currentBalance ?? undefined,
|
||||
});
|
||||
const balance =
|
||||
loan.currentBalance ?? currentBalanceFromSchedule(schedule.schedule, startDate);
|
||||
const balance = schedule.currentBalance;
|
||||
return { loan, schedule, balance };
|
||||
});
|
||||
}, [loans]);
|
||||
@@ -47,21 +47,27 @@ export function LoansPage() {
|
||||
<TableHead>Gläubiger</TableHead>
|
||||
<TableHead>Summe</TableHead>
|
||||
<TableHead>Zins</TableHead>
|
||||
<TableHead>Eff. Zins</TableHead>
|
||||
<TableHead>Rate</TableHead>
|
||||
<TableHead>Restschuld</TableHead>
|
||||
<TableHead>Gesamtzinsen</TableHead>
|
||||
<TableHead>Gesamtbetrag</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{enriched.map(({ loan, balance }) => (
|
||||
{enriched.map(({ loan, schedule, balance }) => (
|
||||
<TableRow key={loan._id}>
|
||||
<TableCell>{loan.name}</TableCell>
|
||||
<TableCell>{loan.lender ?? "–"}</TableCell>
|
||||
<TableCell>{formatAmount(loan.principal)}</TableCell>
|
||||
<TableCell>{loan.annualInterestRate.toFixed(2)} %</TableCell>
|
||||
<TableCell>{loan.effectiveAnnualRate ? `${loan.effectiveAnnualRate.toFixed(2)} %` : "–"}</TableCell>
|
||||
<TableCell>{loan.monthlyPayment ? formatAmount(loan.monthlyPayment) : "–"}</TableCell>
|
||||
<TableCell>{formatAmount(balance)}</TableCell>
|
||||
<TableCell>{formatAmount(schedule.totalInterest)}</TableCell>
|
||||
<TableCell>{formatAmount(schedule.totalAmount)}</TableCell>
|
||||
<TableCell>{loan.status}</TableCell>
|
||||
<TableCell className="space-x-1">
|
||||
<Button size="sm" variant="outline" onClick={() => setEditLoan(loan)}>
|
||||
|
||||
Reference in New Issue
Block a user