diff --git a/src/views/account/AccountView.vue b/src/views/account/AccountView.vue
index 1fd61f5..b1f4e5e 100644
--- a/src/views/account/AccountView.vue
+++ b/src/views/account/AccountView.vue
@@ -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 () => {
-
-
+
+
+
+
+ 이자 {{ (form.interest || 0).toLocaleString('ko-KR') }}원
+ 원금상환 {{ (form.principal || 0).toLocaleString('ko-KR') }}원
+
+
+
+
+
+
+
+
@@ -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;
diff --git a/src/views/account/AccountWalletView.vue b/src/views/account/AccountWalletView.vue
index 87ae50b..bbb113c 100644
--- a/src/views/account/AccountWalletView.vue
+++ b/src/views/account/AccountWalletView.vue
@@ -33,6 +33,11 @@ const form = reactive({
openingBalance: 0,
openingDate: '',
currentValue: null,
+ // 대출(LOAN) 전용
+ loanRate: null,
+ loanMethod: '',
+ loanMonths: null,
+ loanStart: '',
})
const submitting = ref(false)
const formError = ref(null)
@@ -229,6 +234,10 @@ function openCreate() {
openingBalance: 0,
openingDate: '',
currentValue: null,
+ loanRate: null,
+ loanMethod: '',
+ loanMonths: null,
+ loanStart: '',
})
formError.value = null
formOpen.value = true
@@ -245,6 +254,10 @@ function openEdit(w) {
openingBalance: isLiability(w.type) ? -(w.openingBalance || 0) : w.openingBalance || 0,
openingDate: w.openingDate || '',
currentValue: w.currentValue ?? null,
+ loanRate: w.loanRate ?? null,
+ loanMethod: w.loanMethod || '',
+ loanMonths: w.loanMonths ?? null,
+ loanStart: w.loanStart || '',
})
formError.value = null
formOpen.value = true
@@ -258,6 +271,7 @@ async function submit() {
}
submitting.value = true
const magnitude = Number(form.openingBalance) || 0
+ const isLoan = form.type === 'LOAN'
const payload = {
type: form.type,
name: form.name.trim(),
@@ -271,6 +285,10 @@ async function submit() {
form.type === 'INVEST' && form.currentValue !== '' && form.currentValue != null
? Number(form.currentValue)
: null,
+ loanRate: isLoan && form.loanRate !== null && form.loanRate !== '' ? Number(form.loanRate) : null,
+ loanMethod: isLoan && form.loanMethod ? form.loanMethod : null,
+ loanMonths: isLoan && form.loanMonths ? Number(form.loanMonths) : null,
+ loanStart: isLoan && form.loanStart ? form.loanStart : null,
}
try {
if (editId.value) await accountApi.updateWallet(editId.value, payload)
@@ -297,6 +315,9 @@ async function remove(w) {
function cardTypeLabel(v) {
return v === 'CHECK' ? '체크' : v === 'CREDIT' ? '신용' : ''
}
+function loanMethodLabel(v) {
+ return v === 'EQUAL_PAYMENT' ? '원리금균등' : v === 'EQUAL_PRINCIPAL' ? '원금균등' : v === 'BULLET' ? '만기일시' : ''
+}
onMounted(async () => {
await load()
@@ -362,6 +383,9 @@ onBeforeUnmount(() => sortable?.destroy())
>{{ revealedAccts.has(w.id) ? '🙈' : '👁' }}
· 투자금 {{ won(w.investedAmount) }}
+
+ · {{ w.loanRate }}% / {{ loanMethodLabel(w.loanMethod) }}
+