Private
Public Access
1
0

Use daily interest accrual for lines of credit and auto loans, fix hasLoanDetail on account list

Lines of credit and auto loans use daily accrual (rate/365 × days) matching
actual bank calculations. Mortgages and personal loans keep monthly compounding.
Include LoanDetail in GetAllAsync so the loan cards correctly show status.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Catherine Renelle
2026-02-14 20:47:14 -05:00
parent 47010e5f55
commit 1b06894383
3 changed files with 37 additions and 6 deletions
+1 -1
View File
@@ -104,7 +104,7 @@
</v-card>
<v-alert v-if="loanDetail" type="info" variant="tonal" density="compact" class="mb-4">
Amounts shown are estimates based on standard monthly compounding. Actual values may differ due to your lender's rounding practices, extra payments, or rate changes.
Amounts shown are estimates. Lines of credit and auto loans use daily interest accrual; mortgages and personal loans use standard monthly compounding. Actual values may differ due to payment timing, your lender's rounding practices, extra payments, or rate changes.
</v-alert>
<!-- Amortization Schedule -->
@@ -18,6 +18,7 @@ public class AccountService : IAccountService
public async Task<List<AccountResponse>> GetAllAsync(Guid userId)
{
return await _db.Accounts
.Include(a => a.LoanDetail)
.Where(a => a.UserId == userId)
.OrderBy(a => a.SortOrder).ThenBy(a => a.Name)
.Select(a => MapToResponse(a))
+35 -5
View File
@@ -49,7 +49,7 @@ public class LoanService : ILoanService
ExtraPayment = request.ExtraPayment
};
var entries = GenerateAmortizationSchedule(loanDetail);
var entries = GenerateAmortizationSchedule(loanDetail, account.Type);
loanDetail.AmortizationEntries = entries;
_db.LoanDetails.Add(loanDetail);
@@ -82,7 +82,7 @@ public class LoanService : ILoanService
l.EscrowAmount = request.EscrowAmount;
l.ExtraPayment = request.ExtraPayment;
var entries = GenerateAmortizationSchedule(l);
var entries = GenerateAmortizationSchedule(l, account.Type);
foreach (var entry in entries)
{
entry.LoanDetailId = l.Id;
@@ -118,7 +118,9 @@ public class LoanService : ILoanService
// Build new schedule with extra payments
var balance = Math.Abs(account.Balance);
var useDailyAccrual = UsesDailyAccrual(account.Type);
var monthlyRate = l.InterestRate / 100m / 12m;
var dailyRate = l.InterestRate / 100m / 365m;
var extraMonthly = request.ExtraMonthlyPayment ?? 0;
var basePayment = l.MonthlyPayment - (l.EscrowAmount ?? 0);
var now = DateTime.UtcNow;
@@ -139,7 +141,18 @@ public class LoanService : ILoanService
paymentNum++;
var paymentDate = startDate.AddMonths(paymentNum);
var interest = Math.Round(balance * monthlyRate, 2);
decimal interest;
if (useDailyAccrual)
{
var prevDate = startDate.AddMonths(paymentNum - 1);
var days = (paymentDate - prevDate).Days;
interest = Math.Round(balance * dailyRate * days, 2);
}
else
{
interest = Math.Round(balance * monthlyRate, 2);
}
var principal = Math.Min(basePayment + extraMonthly - interest, balance);
if (principal < 0) principal = 0;
@@ -181,18 +194,35 @@ public class LoanService : ILoanService
return new LoanDefaultsResponse(0, interestRate, termMonths, hasSyncData);
}
private static List<AmortizationEntry> GenerateAmortizationSchedule(LoanDetail loan)
private static bool UsesDailyAccrual(AccountType type) =>
type is AccountType.LineOfCredit or AccountType.AutoLoan;
private static List<AmortizationEntry> GenerateAmortizationSchedule(LoanDetail loan, AccountType accountType)
{
var entries = new List<AmortizationEntry>();
var balance = loan.OriginalBalance;
var useDailyAccrual = UsesDailyAccrual(accountType);
var monthlyRate = loan.InterestRate / 100m / 12m;
var dailyRate = loan.InterestRate / 100m / 365m;
var payment = loan.MonthlyPayment - (loan.EscrowAmount ?? 0);
var extra = loan.ExtraPayment ?? 0;
for (int i = 1; i <= loan.TermMonths && balance > 0; i++)
{
var paymentDate = loan.OriginationDate.AddMonths(i - 1);
var interest = Math.Round(balance * monthlyRate, 2);
decimal interest;
if (useDailyAccrual)
{
var prevDate = loan.OriginationDate.AddMonths(i - 2);
var days = (paymentDate - prevDate).Days;
interest = Math.Round(balance * dailyRate * days, 2);
}
else
{
interest = Math.Round(balance * monthlyRate, 2);
}
var principal = Math.Min(payment + extra - interest, balance);
balance -= principal;
if (balance < 0.01m) balance = 0;