feat: 투자 소수점 매매 + 카드 할부(installment_months)

- invest_trade.quantity BIGINT→DECIMAL(18,6): 평단·원가·평가/실현손익 계산을 BigDecimal로 전환 (금액은 원단위 정수 유지)
- account_entry.installment_months 추가: 카드 지출 2~24개월 할부 기록(일시불 null), 응답에 포함

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-06-03 16:41:58 +09:00
co-authored by Claude Opus 4.8
parent f5e9b78a14
commit bd0a0f7776
11 changed files with 79 additions and 28 deletions
@@ -168,6 +168,7 @@ public class AccountService {
.memo(req.getMemo())
.walletId(req.getWalletId())
.toWalletId(transfer ? req.getToWalletId() : null)
.installmentMonths(installmentOf(req))
.build();
mapper.insert(entry);
applyTags(entry.getId(), req.getTagIds(), memberId);
@@ -186,6 +187,7 @@ public class AccountService {
entry.setMemo(req.getMemo());
entry.setWalletId(req.getWalletId());
entry.setToWalletId(transfer ? req.getToWalletId() : null);
entry.setInstallmentMonths(installmentOf(req));
mapper.update(entry);
mapper.deleteEntryTagsByEntryId(id);
@@ -448,6 +450,15 @@ public class AccountService {
return AccountEntryResponse.from(entry, mapper.findTagNamesByEntryId(entry.getId()));
}
/** 할부 개월수 정규화: 지출(EXPENSE)이고 2개월 이상일 때만 저장, 그 외(일시불·수입·이체)는 null */
private Integer installmentOf(AccountEntryRequest req) {
Integer m = req.getInstallmentMonths();
if (!"EXPENSE".equals(req.getType()) || m == null || m < 2) {
return null;
}
return m;
}
/** 선택된 태그 ID 중 해당 사용자 소유로 존재하는 것만 연결 */
private void applyTags(Long entryId, List<Long> tagIds, Long memberId) {
if (tagIds == null || tagIds.isEmpty()) {