feat(account): 대출 계좌 금리/상환방식 저장 + 상환 시 납입금액 자동분리
CI / build (push) Failing after 14m10s

- wallet 테이블에 loan_rate/loan_method/loan_months/loan_start 컬럼 추가(ALTER TABLE)
- 대출 계좌 폼에 연이자율(%), 상환방식(원리금균등/원금균등/만기일시), 기간, 시작일 입력 필드 추가
- 계좌 목록에 금리·상환방식 표시
- 상환 폼: 금리 설정된 대출 계좌 선택 시 납입금액 입력 → 이자/원금 자동계산
  - 원리금균등/원금균등: 잔액 × 연이율/12 = 이자, 납입액-이자 = 원금
  - 만기일시상환: 납입액 전체 이자로 처리
  - 자동계산 후 수동 조정 가능

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-07-02 00:02:10 +09:00
parent e1c077dccd
commit f8e6c6b0ee
2 changed files with 103 additions and 2 deletions
+56 -2
View File
@@ -127,6 +127,33 @@ const repayTargetIsCard = computed(() => {
const w = wallets.value.find((x) => x.id === form.toWalletId)
return !!w && w.type === 'CARD'
})
// 금리가 설정된 대출 계좌를 선택한 경우 자동계산 활성
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 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)
}
}
watch(loanPaymentAmount, (val) => { if (val) calcLoanRepayment(Number(val)) })
watch(() => form.toWalletId, () => {
loanPaymentAmount.value = null
form.principal = null
form.interest = null
})
// 카드 지출일 때만 할부 입력 노출 (2~24개월, 일시불은 빈값)
const showInstallment = computed(() => form.type === 'EXPENSE' && form.walletKind === 'CARD')
const installmentMonthly = computed(() => {
@@ -1135,8 +1162,23 @@ onMounted(async () => {
</option>
</select>
</label>
<label>원금<input v-model.number="form.principal" type="number" min="0" placeholder="원 (이체로 기록)" :disabled="submitting" /></label>
<label>이자<input v-model.number="form.interest" type="number" min="0" placeholder="원 (지출·분류 이자)" :disabled="submitting" /></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">
<span>이자 <b>{{ (form.interest || 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>
</template>
<template v-else>
<label>원금<input v-model.number="form.principal" type="number" min="0" placeholder="원 (이체로 기록)" :disabled="submitting" /></label>
<label>이자<input v-model.number="form.interest" type="number" min="0" placeholder="원 (지출·분류 이자)" :disabled="submitting" /></label>
</template>
<label v-if="repayTargetIsCard">연회비<input v-model.number="form.annualFee" type="number" min="0" placeholder="원 (지출·분류 연회비)" :disabled="submitting" /></label>
</template>
@@ -1973,6 +2015,18 @@ button.primary {
.acc-chip.add-chip.sub-add {
flex: 0 0 auto;
}
.loan-breakdown {
display: flex;
gap: 1rem;
padding: 0.45rem 0.6rem;
border-radius: 6px;
background: var(--color-background-soft);
font-size: 0.85rem;
color: var(--color-text);
}
.loan-breakdown b {
color: hsla(160, 100%, 37%, 1);
}
.tag-field {
display: flex;
flex-direction: column;