- loan_amount(대출 실행 금액) 컬럼 추가(DB/도메인/DTO/mapper) - 계좌 폼: 대출 실행 금액 입력 / 기록 시작 시 잔액(기존 openingBalance) 레이블 분리 - 계좌 카드: 실행금액·금리·상환방식 표시 - 상환 자동계산 3방식 분기: - 원리금균등: 납입금액 입력 → 이자(잔액×월이율) / 원금(납입-이자) 분리 - 원금균등: 실행금액÷기간=월원금, 잔액×월이율=이자 자동계산 - 만기일시: 잔액×월이율=이자만 자동계산, 원금 0 - 자동계산 후 이자·원금 수동 조정 가능 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -132,23 +132,46 @@ const repayTargetLoanWallet = computed(() => {
|
||||
const w = wallets.value.find((x) => x.id === form.toWalletId)
|
||||
return w?.type === 'LOAN' && w.loanRate ? w : null
|
||||
})
|
||||
// 납입총액 입력 → 원금/이자 자동분리
|
||||
// 납입금액 입력용 (원리금균등만 사용)
|
||||
const loanPaymentAmount = ref(null)
|
||||
|
||||
function monthlyInterestOf(loan) {
|
||||
return Math.round(Math.abs(loan.balance || 0) * (Number(loan.loanRate) / 100 / 12))
|
||||
}
|
||||
|
||||
// 상환방식별 자동계산: 원금균등·만기일시는 납입금액 입력 불필요
|
||||
const loanAutoResult = computed(() => {
|
||||
const loan = repayTargetLoanWallet.value
|
||||
if (!loan) return null
|
||||
const interest = monthlyInterestOf(loan)
|
||||
if (loan.loanMethod === 'BULLET') {
|
||||
return { interest, principal: 0, total: interest, needsInput: false }
|
||||
}
|
||||
if (loan.loanMethod === 'EQUAL_PRINCIPAL' && loan.loanAmount && loan.loanMonths) {
|
||||
const principal = Math.round(Number(loan.loanAmount) / loan.loanMonths)
|
||||
return { interest, principal, total: principal + interest, needsInput: false }
|
||||
}
|
||||
return { interest: null, principal: null, total: null, needsInput: true }
|
||||
})
|
||||
|
||||
watch(loanAutoResult, (r) => {
|
||||
if (r && !r.needsInput) {
|
||||
form.interest = r.interest
|
||||
form.principal = r.principal
|
||||
loanPaymentAmount.value = r.total
|
||||
}
|
||||
})
|
||||
|
||||
function calcLoanRepayment(payment) {
|
||||
const loan = repayTargetLoanWallet.value
|
||||
if (!loan || !payment || payment <= 0) return
|
||||
const monthlyRate = Number(loan.loanRate) / 100 / 12
|
||||
const remainingDebt = Math.abs(loan.balance || 0)
|
||||
const monthlyInterest = Math.round(remainingDebt * monthlyRate)
|
||||
if (loan.loanMethod === 'BULLET') {
|
||||
form.interest = payment
|
||||
form.principal = 0
|
||||
} else {
|
||||
form.interest = Math.min(monthlyInterest, payment)
|
||||
form.principal = Math.max(0, payment - form.interest)
|
||||
}
|
||||
const interest = monthlyInterestOf(loan)
|
||||
form.interest = Math.min(interest, payment)
|
||||
form.principal = Math.max(0, payment - form.interest)
|
||||
}
|
||||
watch(loanPaymentAmount, (val) => { if (val) calcLoanRepayment(Number(val)) })
|
||||
watch(loanPaymentAmount, (val) => {
|
||||
if (loanAutoResult.value?.needsInput && val) calcLoanRepayment(Number(val))
|
||||
})
|
||||
watch(() => form.toWalletId, () => {
|
||||
loanPaymentAmount.value = null
|
||||
form.principal = null
|
||||
@@ -1162,15 +1185,26 @@ onMounted(async () => {
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
<!-- 금리 설정된 대출 계좌: 납입금액 입력 → 원금/이자 자동계산 -->
|
||||
<!-- 금리 설정된 대출 계좌: 방식별 자동계산 -->
|
||||
<template v-if="repayTargetLoanWallet">
|
||||
<label>납입금액
|
||||
<input v-model.number="loanPaymentAmount" type="number" min="0"
|
||||
placeholder="이번 달 납입할 총금액" :disabled="submitting" @input="calcLoanRepayment(loanPaymentAmount)" />
|
||||
</label>
|
||||
<div v-if="loanPaymentAmount > 0" class="loan-breakdown">
|
||||
<!-- 원리금균등: 납입금액 직접 입력 -->
|
||||
<template v-if="loanAutoResult?.needsInput">
|
||||
<label>납입금액(원리금균등)
|
||||
<input v-model.number="loanPaymentAmount" type="number" min="0"
|
||||
placeholder="이번 달 납입할 총금액" :disabled="submitting" />
|
||||
</label>
|
||||
</template>
|
||||
<!-- 원금균등·만기일시: 자동계산 결과 표시 -->
|
||||
<template v-else>
|
||||
<div class="loan-breakdown">
|
||||
<span class="bd-label">{{ repayTargetLoanWallet.loanMethod === 'BULLET' ? '만기일시' : '원금균등' }} 자동계산</span>
|
||||
<span>납입 <b>{{ (loanAutoResult?.total || 0).toLocaleString('ko-KR') }}원</b></span>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 계산 결과 표시 + 수동 조정 -->
|
||||
<div v-if="(form.interest ?? null) !== null || (form.principal ?? null) !== null" class="loan-breakdown">
|
||||
<span>이자 <b>{{ (form.interest || 0).toLocaleString('ko-KR') }}원</b></span>
|
||||
<span>원금상환 <b>{{ (form.principal || 0).toLocaleString('ko-KR') }}원</b></span>
|
||||
<span>원금 <b>{{ (form.principal || 0).toLocaleString('ko-KR') }}원</b></span>
|
||||
</div>
|
||||
<label>이자(조정)<input v-model.number="form.interest" type="number" min="0" :disabled="submitting" /></label>
|
||||
<label>원금(조정)<input v-model.number="form.principal" type="number" min="0" :disabled="submitting" /></label>
|
||||
@@ -2027,6 +2061,12 @@ button.primary {
|
||||
.loan-breakdown b {
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
}
|
||||
.loan-breakdown .bd-label {
|
||||
width: 100%;
|
||||
font-size: 0.78rem;
|
||||
opacity: 0.6;
|
||||
margin-bottom: 0.1rem;
|
||||
}
|
||||
.tag-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
Reference in New Issue
Block a user