feat: 예산 월별 분리 + 전월/다음달 복사 버튼
CI / build (push) Failing after 12m14s

- budgets/createBudget에 year/month 전달, copyBudget 추가
- BudgetView: '전월 예산 가져오기' / '다음 달로 복사' 버튼
- 백업 export는 현재 월 예산을 담음

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-06-30 22:35:49 +09:00
parent 80d7b8d552
commit f8369f5108
3 changed files with 78 additions and 7 deletions
+8 -4
View File
@@ -76,8 +76,12 @@ const realApi = {
},
// 예산 (사용자별)
budgets() {
return http.get('/account/budgets')
budgets({ year, month } = {}) {
return http.get('/account/budgets', { params: { year, month } })
},
// 다른 월 예산을 이 달로 복사(전월 복사 등)
copyBudget({ fromYear, fromMonth, toYear, toMonth }) {
return http.post('/account/budgets/copy', null, { params: { fromYear, fromMonth, toYear, toMonth } })
},
budgetStatus({ year, month }) {
return http.get('/account/budgets/status', { params: { year, month } })
@@ -91,8 +95,8 @@ const realApi = {
setExpectedIncome({ year, month, expectedIncome }) {
return http.put('/account/budgets/income', { expectedIncome }, { params: { year, month } })
},
createBudget(payload) {
return http.post('/account/budgets', payload)
createBudget(payload, { year, month } = {}) {
return http.post('/account/budgets', payload, { params: { year, month } })
},
updateBudget(id, payload) {
return http.put(`/account/budgets/${id}`, payload)
+2 -1
View File
@@ -44,7 +44,8 @@ export async function exportBackup() {
safe(accountApi.wallets()),
safe(accountApi.categories()),
safe(accountApi.recurrings()),
safe(accountApi.budgets()),
// 예산은 월별 — 백업엔 현재 월 예산을 담는다
safe(accountApi.budgets({ year: new Date().getFullYear(), month: new Date().getMonth() + 1 })),
safe(accountApi.tags()),
safe(accountApi.quickEntries()),
])
+68 -2
View File
@@ -127,7 +127,7 @@ async function load() {
error.value = null
try {
const params = { year: year.value, month: month.value }
const [st, raw] = await Promise.all([accountApi.budgetStatus(params), accountApi.budgets()])
const [st, raw] = await Promise.all([accountApi.budgetStatus(params), accountApi.budgets(params)])
statuses.value = st
budgetsRaw.value = raw
} catch (e) {
@@ -154,6 +154,46 @@ function nextMonth() {
loadIncome()
}
// ===== 월별 예산 복사 =====
const copying = ref(false)
const ym = (y, m) => `${y}.${String(m).padStart(2, '0')}`
async function copyToNextMonth() {
if (copying.value || !budgetsRaw.value.length) return
const to = month.value === 12 ? { y: year.value + 1, m: 1 } : { y: year.value, m: month.value + 1 }
const ok = await dialog.confirm(
`${ym(year.value, month.value)} 예산을 ${ym(to.y, to.m)}(다음 달)로 복사할까요?\n다음 달의 같은 분류 예산은 덮어씁니다.`,
{ title: '다음 달로 복사' },
)
if (!ok) return
copying.value = true
try {
await accountApi.copyBudget({ fromYear: year.value, fromMonth: month.value, toYear: to.y, toMonth: to.m })
await dialog.alert(`${ym(to.y, to.m)}로 예산을 복사했어요.`)
} catch (e) {
error.value = e.response?.data?.message || '복사에 실패했습니다.'
} finally {
copying.value = false
}
}
async function copyFromPrevMonth() {
if (copying.value) return
const prev = month.value === 1 ? { y: year.value - 1, m: 12 } : { y: year.value, m: month.value - 1 }
const ok = await dialog.confirm(
`전월(${ym(prev.y, prev.m)}) 예산을 ${ym(year.value, month.value)}로 가져올까요?\n이 달의 같은 분류 예산은 덮어씁니다.`,
{ title: '전월 예산 복사' },
)
if (!ok) return
copying.value = true
try {
await accountApi.copyBudget({ fromYear: prev.y, fromMonth: prev.m, toYear: year.value, toMonth: month.value })
await load()
} catch (e) {
error.value = e.response?.data?.message || '복사에 실패했습니다.'
} finally {
copying.value = false
}
}
function openCreate() {
editId.value = null
Object.assign(form, {
@@ -213,7 +253,7 @@ async function submit() {
submitting.value = true
try {
if (editId.value) await accountApi.updateBudget(editId.value, payload)
else await accountApi.createBudget(payload)
else await accountApi.createBudget(payload, { year: year.value, month: month.value })
formOpen.value = false
await load()
} catch (e) {
@@ -252,6 +292,12 @@ onMounted(() => {
</div>
</div>
<!-- 월별 예산 복사 -->
<div class="copy-actions">
<button type="button" class="copy-btn" :disabled="copying" @click="copyFromPrevMonth"> 전월 예산 가져오기</button>
<button type="button" class="copy-btn" :disabled="copying || !budgetsRaw.length" @click="copyToNextMonth">다음 달로 복사 </button>
</div>
<!-- 예상 수입 vs 예산 -->
<div class="income-panel">
<div class="inc-input">
@@ -431,6 +477,26 @@ button.primary {
text-align: center;
}
/* 예상 수입 vs 예산 */
.copy-actions {
display: flex;
gap: 0.5rem;
justify-content: flex-end;
flex-wrap: wrap;
margin: -0.4rem 0 1rem;
}
.copy-btn {
padding: 0.4rem 0.8rem;
border: 1px solid var(--color-border);
border-radius: 6px;
background: var(--color-background-soft);
color: var(--color-text);
font-size: 0.82rem;
cursor: pointer;
}
.copy-btn:disabled {
opacity: 0.45;
cursor: default;
}
.income-panel {
border: 1px solid var(--color-border);
border-radius: 8px;