Commit Graph

80 Commits

Author SHA1 Message Date
GA Pro bfc724669b feat: incar 갭 반영 수수료/운영 14개 도메인 풀스택 + 배치통합 + 신입교육자료
incar CMS(116메뉴) 벤치마킹 갭분석으로 ga-pro에 없던 14개 도메인을 풀스택 추가.

P7 수수료 계산 7종 (V88~V96):
- 수입수수료+수지차, 소개·이관수수료, 정착지원금, 인정실적+환산율,
  시상, 생보운영지원수수료, 지점장수당
- 각 rule/ledger + 공식(보험료×요율, 수입−지급 등) Service 내장

P8 운영/정산 7종 (V97~V105):
- 단계마감, 본사정산, 등급평가, 공동계약, 보증보험, 적립금, 수수료시뮬레이터
- close/reopen/apply/simulate/upsert 등 액션 + 손익·잔액·등급 산정 로직

배치 통합 (V106):
- settle_master.other_commission_total 컬럼 추가(DEFAULT 0, 백필)
- 설계사 지급성 9종 원장 aggregateByAgent → AggregateStep gross 합산
- 빈 원장 0 → 기존 정산결과·무결성 불변(비파괴적)

테스트: P7/P8 Service 공식 단위테스트 21건(ga-api:test 57건 전체 통과)
프론트: 화면 14개 + React.lazy 코드 스플리팅(단일 4MB → 130+ 청크)
문서: DOMAIN_GAP_P7/P8.md, 신입교육_보험과수수료_완전기초.md, DOMAIN_KNOWLEDGE/HANDOFF 갱신

검증: 전체 ./gradlew build(test 포함) SUCCESSFUL, Flyway V88~V106 success,
GET+액션POST smoke 5xx 0건, 9개 집계쿼리 SQL 유효성 확인, 공식 런타임 실측.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 15:45:10 +09:00
GA Pro 70620463b4 docs: 도메인 지식 문서 신입사원 교육용 확장
- 목차(TOC) 추가
- §3 보험 기초(보험을 처음 접한다면) — 보험료/보험종류/유지율/위촉해촉 등 입문 용어
- §7 수수료 계산 공식과 숫자 예시 — 기본 3단계 공식(보험료×보험사요율×지급률)
  + 모집/분급(7년)/차등환수/오버라이드/세금상세 worked example
  (CommissionCalculator·TaxCalculator·CalcOverrideStep 실제 코드 기준)
- §13 용어 사전 확장(보험 기초 용어 + 요율 용어 추가)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 10:48:27 +09:00
GA Pro 81877dcb2b fix: 필수 파라미터 누락 시 500→400 정정 + 전체 메뉴 점검/도메인 문서
- GlobalExceptionHandler: MissingServletRequestParameterException 핸들러 추가.
  기존엔 미처리로 generic Exception→E500. 이제 INVALID_PARAMETER(400) 반환.
  (전수 점검 중 /api/settle/summary 가 settleMonth 누락 시 500 반환하던 결함 발견)
- smoke_allmenus.py: 전체 메뉴 화면 1차 GET 엔드포인트 67개 전수 점검 스크립트.
  admin 전권 로그인 후 5xx 만 FAIL 분류. 검증: 66 PASS + 1 정상400, 5xx 0건.
- DOMAIN_KNOWLEDGE.md: 보험 GA 수수료 정산 도메인 입문 문서
  (수수료 종류/규제룰/세무공제/12단계 정산 파이프라인/상태흐름/용어사전).

검증: 5모듈 compileJava + ga-frontend vite build + PWA tsc PASS,
전체 테스트 112건 PASS, 모바일 me/* 5개 200.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 10:23:44 +09:00
GA Pro fcddfea4f6 test: SqlSessionFactory 테스트 환경 문제 해결 — 전체 112건 PASS
@SpringBootTest 컨텍스트가 @MapperScan(com.ga) 으로 모든 매퍼 MapperFactoryBean 을 만드는데
test 프로파일이 MybatisAutoConfiguration 을 제외해 SqlSessionFactory 가 없어 깨지던 문제.
기존엔 매퍼를 하나씩 @MockBean 으로 막았으나 신규 매퍼 누락 시마다 컨텍스트 로딩 실패.

- TestMyBatisConfig: 미접속 PGSimpleDataSource + SqlSessionFactory 제공 → 전 매퍼 빈 생성, 목록 갱신 불필요
- AbstractControllerTest @Import 에 TestMyBatisConfig 추가
- SettleControllerTest hold stub 2인자→3인자 정정(testCompile 실패 해소)
- HANDOFF §3-17 추가 + 테스트 상태 서술 정정

검증: ./gradlew test --rerun-tasks → 112건 PASS (ga-common 57 / ga-api 36 / ga-batch 19, 실패 0). DB 무관.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 23:08:11 +09:00
GA Pro e407263370 fix: 회계분개 멱등성 — 동일 정산월 재실행 시 분개 중복 INSERT 방지 (V87)
AccountingEntryGenerateStep(Step8b)이 selectBySettleMonth 결과를 무조건 insert 하여
동일 정산월 재정산 시 분개가 누적 중복되던 버그. payment_id 가 항상 NULL(원장 기반)이라
기존 주석의 existsByPaymentId 중복방어는 불가능했음.

- V87: contract_accounting_entry.settle_month 컬럼 추가 + 기존데이터 백필(description 6자리) + 미전기 부분 인덱스
- core: VO settleMonth 필드, Mapper deleteUnpostedBySettleMonth, insertBatch/selectBySettleMonth 에 settle_month 반영
- batch: 재생성 전 deleteUnpostedBySettleMonth 호출(마감 채번분 journal_no IS NOT NULL 보존)

검증: ga-core/ga-batch/ga-api compileJava SUCCESS, Flyway v87 적용(백필 433건 NULL 0),
  202605 정산 2회 실행 → 193건 유지(중복 0, 수정 전이라면 386). 멱등성 PASS. 테스트데이터 롤백 완료.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 22:58:49 +09:00
GA Pro a5ae95e34c docs: HANDOFF §3-16 계약 승계(V86) 추가 + 재개 절차 갱신(v86/ga-batch 트리거)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 22:16:17 +09:00
GA Pro 4dbc688f79 fix: AgentDeductionMapper.updateStatusBatch XML 구문 누락 보강
AggregateStep(Step8) 이 차감 APPLIED 일괄 전이로 호출하는 updateStatusBatch 가
인터페이스에만 선언되고 XML 구문이 없어, PENDING 차감이 있는 정산월 집계 시
'Invalid bound statement (not found)' 로 aggregate 전체가 FAILED 되던 잠복 버그.
updateStatus 동일 패턴 + foreach IN 으로 구현. (계약승계 e2e 중 발견, 승계와 무관)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 21:44:34 +09:00
GA Pro bd5ff4ec14 feat: 계약 승계(contract succession) — 유지수수료 귀속 후임 전환 (V86)
설계사 해촉/이관 시 보험계약의 유지수수료 귀속을 승계월 기준 후임 설계사로 전환.
모집수수료는 원 모집설계사 귀속 유지(고아계약 승계 표준).

- DB V86: contract_succession 테이블 + 인덱스 + 메뉴/권한(GRP_PRD) + 공통코드 2그룹 + 테스트데이터 1건
- core: ContractSuccession VO/Resp/SaveReq/SearchParam + Mapper(.java/.xml)
  selectEffectiveSuccessions(settleMonth): 계약별 최근 ACTIVE 승계 1건 (DISTINCT ON)
- api: ContractSuccessionService/Controller (/api/contract-successions, CRUD+cancel)
- batch: CalcMaintainStep 이 effective 승계 사전로드(N+1 회피) 후 effectiveAgentId 결정,
  CommissionCalculator.calcMaintainLedger 오버로드로 후임 등급 payout + ledger agent_id 후임 기록.
  AggregateStep 은 ledger agent_id 집계라 무변경.
- frontend: api/succession.ts + ContractSuccessions.tsx + App.tsx 라우트

검증: 5모듈 compileJava SUCCESS / ga-frontend tsc 0 / Flyway v86 적용 /
  API CRUD+cancel+from==to 가드(E411) 라이브 PASS / resolver 쿼리 라이브 검증(계약1→후임 agent2).
배치 풀잡 e2e 는 정산월 원장 재생성(파괴적)이라 미실행.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 21:28:00 +09:00
GA Pro d8d64625a9 feat: P6 후속 4 — 출금 폼 잔여 한도 미리 표시
- 백엔드 GET /api/mobile/me/settle-masters/{id}/withdrawable-balance
  · 응답 {settleId, netAmount, usedAmount, remainingAmount}
  · 본인 settle_master 검증 후 net - sumActiveAmountByMaster 반환
  · createWithdrawRequest 가 동일 조회 로직 재사용 (중복 제거)
- PWA WithdrawCreate: settleMaster 선택 시 잔액 카드 표시 + amount 클라이언트 사전 검증

검증: ga-api compileJava BUILD SUCCESSFUL / ga-mobile-pwa tsc --noEmit 0 오류
(라이브 e2e 는 DB/docker 미가동으로 차단 — 환경 복구 후 스모크 예정)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 20:52:17 +09:00
GA Pro 366f49c3f0 feat: P6 후속 3 — 출금 잔여한도 검증 + 알림 모두읽음 + HANDOFF 갱신
출금 잔여 한도 검증:
- WithdrawRequestMapper.sumActiveAmountByMaster(settleMasterId) 추가
  · CANCELLED 외 모든 활성 출금의 amount SUM (COALESCE 0)
- MobileMeService.createWithdrawRequest 에 잔여 한도 가드 추가
  · 1) settle_master 본인 소유 확인 (FORBIDDEN)
  · 2) 잔여 = net_amount - 활성 출금합계
  · 3) 요청 amount > 잔여 시 INVALID_PARAMETER(E411) 던짐
  · 메시지에 잔여 금액 명시 ("출금 가능 잔액(N)을 초과합니다")

PWA 알림 모두읽음:
- POST /api/mobile/me/notifications/read-all (기존 UserNotificationMapper.markAllRead 재사용)
- NoticeList 내알림 탭 헤더에 "모두 읽음 (N)" 버튼 (안읽음 카운트 표시)
- 성공 시 invalidate notifications + summary 쿼리

검증:
- ga-api compileJava + ga-mobile-pwa tsc PASS
- e2e (agent01 / userId=4 / agentId=1):
  · settle#51 net=35,079원, 기존 사용 5,000원
  · amount=999,999 → E411 "출금 가능 잔액(30079.00)을 초과합니다"
  · amount=100   → success, requestId=5
  · markAllRead → 200, summary.unread=0
- 이전 e2e 검증 (총 9건) 모두 PASS

HANDOFF.md §3-15 갱신 (2026-05-27 P6 후속 전체 정리 + agent01 테스트 계정 명시).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 00:00:17 +09:00
GA Pro dfce9e5c02 feat: P6 후속 2 — PWA 본인 프로필/출금취소 + SETTLED/FAILED 자동 알림
PWA 본인 프로필:
- Profile.tsx 신규 (phone 자기수정 Dialog + 비밀번호 변경 + 로그아웃)
- TabBar 에 "내정보" 탭 추가 (총 5탭)
- 백엔드 PUT /api/mobile/me/profile/phone (MobileMeService.updateMyPhone, 토큰에서 userId 강제)
- 비밀번호 변경은 기존 /api/auth/password 재사용

PWA 출금 신청 취소:
- 백엔드 POST /api/mobile/me/withdraw-requests/{id}/cancel
  · 본인 agentId 검증 후 기존 WithdrawRequestService.cancel 호출
  · REQUESTED 상태에서만 가능 (기존 검증 재사용)
- WithdrawList 행을 SwipeAction 으로 감싸 "취소" 액션 노출
  · REQUESTED 상태 행만 SwipeAction 활성

출금 SETTLED/FAILED 본인 알림 자동 발행:
- WithdrawBankSettleService 가 SETTLED/FAILED 전이 시 user_notification INSERT
- 신청자(requestedBy) 에게 RefType=WITHDRAW, refId=requestId 로 발행
- 알림 발행 실패해도 동기화 흐름 비차단 (warn 로그만)
- PWA NoticeList 의 "내 알림" 탭에서 즉시 확인 가능

검증:
- ga-api compileJava + ga-frontend/ga-mobile-pwa tsc PASS
- PUT /api/mobile/me/profile/phone (admin) → 200, GET /api/auth/me 의 phone 변경 확인
- POST /api/mobile/me/withdraw-requests/1/cancel (admin) → E403 (agentId null 보안 강제)
- POST /api/internal/bank-settle → 200 data=0 (이번엔 ACCEPTED 잔여 없음, 알림 발행 경로 코드상 검증)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 23:18:39 +09:00
GA Pro 2c9bf710bc feat: P6 후속 — PWA 출금폼/명세상세/토큰refresh + 펌뱅킹 SETTLED 동기화 + user phone UI
외부 SDK 차단 항목 외 P6 잔여 운영성 작업을 일괄 완료. DB 변경 없음(v85 유지).

PWA:
- 출금 신청 작성 폼 (WithdrawCreate.tsx + POST /api/mobile/me/withdraw-requests + GET /settle-masters)
  · 클라이언트가 보낸 agentId/userId 무시, 토큰에서 강제 결정
- 명세서 상세 + 엑셀 다운로드 (StatementDetail.tsx + GET /api/mobile/me/statements/{id}[/download])
  · 본인 agentId 검증 후 기존 CommissionStatementService.download() 재사용
- 토큰 자동 refresh (request.ts interceptor + authStore.refreshToken)
  · 401 시 단일 refresh 호출로 합치고 broadcast, login/refresh 자체 401 은 무한루프 차단

펌뱅킹 SETTLED 동기화:
- WithdrawBankSettleService + POST /api/internal/bank-settle (BankSettleController)
- Mapper: selectAcceptedTransfers / markSettled / markSettleFailed
- 어댑터 queryStatus 폴링 결과로 SETTLED→COMPLETED, FAILED→APPROVED(재시도풀) 전이
- app.bank-settle.cron 옵션 (기본 비활성)

사용자 phone 등록 UI:
- PUT /api/system/users/{id}/phone — NotBlank 검증 없는 단일 패치 endpoint
- UserList 에 phone 컬럼 + "전화번호" 액션 → Antd Modal 인라인 편집
- 결재 SMS 발신 전제 충족

검증 (9/9 PASS, 라이브):
- ga-common/ga-core/ga-external-adapter/ga-api compileJava BUILD SUCCESSFUL
- ga-frontend / ga-mobile-pwa tsc --noEmit PASS
- POST /api/internal/bank-settle → data=3 (3건 COMPLETED 전이 확인)
- PUT /api/system/users/1/phone → 200, 이후 GET 으로 phone 노출 확인
- 보안 강제: admin(agentId=null)으로 /api/mobile/me/{statements,settle-masters,withdraw-requests} → E403
- POST /api/auth/refresh → 200, 새 accessToken 발급
- 3개 서비스 라이브: ga-api :8082 / ga-frontend :3000 / ga-mobile-pwa :3001

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 22:53:59 +09:00
GA Pro ee3e0d40c5 feat: P6 모바일 PWA 보안 강화 — /api/mobile/me/* 서버측 본인 필터
스캐폴드의 클라이언트 agentId 쿼리 필터(변조 가능)를
서버측 강제 필터로 교체. 토큰 → user → agentId 자동 추출.

백엔드 (ga-api):
- MobileMeService — 클라이언트 param 의 agentId/userId 는 무시·덮어쓰기.
  agentId 미연결 시 BizException(FORBIDDEN). PageHelper 로 정확한 total 산출.
- MobileMeController — GET /api/mobile/me/{summary,statements,
  withdraw-requests,notifications} + POST /notifications/{id}/read.
  별도 메뉴 권한 없이 인증만 통과 (PWA 사용자 전체 대상).

ga-mobile-pwa:
- api/statement.ts, api/withdraw.ts → /api/mobile/me/* 호출, agentId 제거
- api/notification.ts 신규 — list/markRead/summary
- Home.tsx — summary() 단일 호출로 3카운트 + 안 읽음 Badge
- NoticeList.tsx — Tabs(내 알림 / 공지). 알림 클릭 markRead + summary 무효화
- Statement/WithdrawList — E403(설계사 미연결) 시 ErrorBlock 안내

라이브 검증:
- ga-api 컴파일 BUILD SUCCESSFUL · 재기동 OK
- PWA tsc PASS · vite build PASS (precache 538KB, gzip 176KB)
- admin(agentId=null): /summary → 200 OK 0/0/0,
  /statements → E403 차단 정상, /notifications → 200 OK total=0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 23:32:19 +09:00
GA Pro 23fdd8474b feat: P6 모바일 셀프 조회 PWA 스캐폴드 (ga-mobile-pwa)
설계사 본인 정산명세서/출금/공지를 모바일에서 조회하는 PWA 신규 모듈.
데스크탑 admin UI(ga-frontend)와 별도. 백엔드 무변경 — 기존 ga-api(8082)
재사용. 토큰 키도 mobile.accessToken 으로 분리.

스택:
- Vite + React 18 + TS + antd-mobile@5
- vite-plugin-pwa (manifest + service worker autoUpdate)
- React Query + Zustand (persist) + react-router-dom
- 포트 3001, /api → 8082 dev 프록시

화면 5종 (NavBar + List + PullToRefresh + TabBar):
- /login         — POST /api/auth/login + GET /api/auth/me
- /              — 홈 (요약 카드 + 메뉴, 로그아웃)
- /statement     — GET /api/statements?agentId=…  (본인 명세서)
- /withdraw      — GET /api/withdraw-requests?agentId=…  (본인 출금이력)
- /notice        — GET /api/notices/active

PWA:
- manifest: name/icon/theme/standalone, ko, /
- /api/** NetworkFirst 5s 타임아웃 / 5분 캐시 (오프라인 시 직전 응답)
- 아이콘은 SVG placeholder (192/512) — 본 배포 시 PNG 권장

검증:
- npm install 407 packages
- tsc --noEmit PASS
- npm run build PASS — manifest.webmanifest + sw.js + workbox-*.js
  자동 생성, precache 8 entries 528KB, 번들 gzip 173KB
- vite dev :3001 LISTENING + ga-api :8082 정상
- /api/statements total=112, /api/withdraw-requests total=3,
  /api/notices/active 0건, /api/auth/me 정상

잔여 (스캐폴드 이후):
- 백엔드 /api/mobile/me/* 전용 endpoint (서버측 본인 필터 강제)
- 출금 신청 작성 폼, 명세서 상세, 본인 알림(user_notification)
- 토큰 만료 자동 refresh, 푸시 알림 (FCM)
- 실 아이콘 PNG, manualChunks 분리

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 23:22:18 +09:00
GA Pro 4a7a4eeb7a chore: 검증용 토큰/임시 파일 제거 + .gitignore 보강
이전 커밋(5ee8321)에 검증 중 만든 login.json/token.txt 가 실수로 포함되어
JWT 토큰이 히스토리에 노출됨. 토큰은 이미 expired 되었지만(7200s) 즉시 제거.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 22:58:05 +09:00
GA Pro 5ee8321ef2 feat: P6 외부연동 호출 감사 이력 (V85)
펌뱅킹/SMS·카카오 어댑터 모든 호출을 external_call_log 테이블에 AOP 자동 영속화.
외부 SDK 통합 후에도 그대로 재사용. 운영 감사·장애 추적·SDK 검증 용도.

- V85: external_call_log 테이블 + 인덱스 4종 (called_at desc / type / target / failures)
       + SYSTEM_EXTERNAL_CALL 메뉴 + READ 권한 (SUPER_ADMIN/ADMIN)
- ga-core: ExternalCallLogVO/Resp/SearchParam + Mapper (insertOne / selectList / selectById)
- ga-api/aop: ExternalCallLoggingAspect — BankTransferAdapter+/MessageAdapter+
  모든 메서드 @Around 영속화. 호출자 트랜잭션과 분리(REQUIRES_NEW),
  request/response 는 PII 마스킹된 JSON 한 줄 요약(계좌·전화 뒤 4자리, 이름 첫글자만),
  영속화 자체가 실패해도 비즈니스 흐름 비차단(warn 로그만), 예외는 그대로 propagate.
- ga-api: ExternalCallLogService + Controller GET /api/external-call-logs[/{id}]
- ga-frontend: ExternalCallLogList.tsx + api/externalCallLog.ts + App.tsx 라우트
  /system/external-call-log (ProTable + Drawer 상세)

라이브 검증:
- Flyway V85 자동 적용 → 운영 DB schema v85
- 5모듈 컴파일 BUILD SUCCESSFUL + ga-frontend tsc --noEmit 통과
- 시나리오: withdraw#3 신규 → 결재 advance ×2 → 펌뱅킹 호출
  external_call_log #1: BANK/MockBankTransferAdapter/requestTransfer/
  success=true/durMs=3/target=WITHDRAW#3/accountNoMasked="****0123"

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 22:56:27 +09:00
GA Pro 0e8c563a9a feat: P6 어댑터 PoC 보강 — Resp 노출 / phone 매핑 / 재시도 배치 / SDK 분기
1. WithdrawRequestResp / VO / Mapper.xml / 화면에 transfer_tx_id/transfer_status/transfer_at 노출
2. UserMapper.selectFirstActiveContactByRoleCode — step.roleCode → user.phone 1차 매핑
   - ApprovalService.sendStepNotification 이 실제 결재자 user_id/phone 사용 (기존 0L 하드코딩 해소)
   - phone 미확보 시 SMS skip (DB 알림은 유지)
3. 펌뱅킹 재시도 — WithdrawBankRetryService + POST /api/internal/bank-retry
   - WithdrawRequestMapper.selectFailedTransfers 추가
   - @EnableScheduling + cron 옵션(기본 비활성), app.bank-retry.limit
4. 어댑터 프로파일 분기 — @ConditionalOnProperty
   - app.adapter.bank: mock(기본) | kftc → KftcBankTransferAdapter 스켈레톤
   - app.adapter.message: mock(기본) | toast → ToastMessageAdapter 스켈레톤
   - 활성화 시 UnsupportedOperationException 명시
5. V83(menu_code 오기로 0건 적용) + V84(실제 WITHDRAW 메뉴에 EXECUTE 권한 보강)
2026-05-24 02:45:13 +09:00
GA Pro b04fa79840 feat: P6 외부연동 어댑터 모듈 PoC (펌뱅킹/SMS·카카오)
신규 모듈 ga-external-adapter — 외부 SDK 의존성을 격리.
실제 SDK 통합 시 이 모듈의 구현체만 교체하면 ga-api/ga-common 무변경.

- ga-common.adapter.bank: BankTransferAdapter 인터페이스 + Request/Response/Status
- ga-common.adapter.message: MessageAdapter 인터페이스 + Channel(SMS/KAKAO)/Request/Response
- ga-external-adapter: MockBankTransferAdapter / MockMessageAdapter (로그 + 마스킹)
- V82: withdraw_request.transfer_tx_id/transfer_status/transfer_at + 부분 인덱스
- ApprovalService.handleApprovalCallback(WITHDRAW) — 최종 승인 시 펌뱅킹 어댑터 호출
  - 성공: status APPROVED → SENT 전이, transfer_tx_id 영속화
  - 실패: APPROVED 유지 + fail_reason 기록 (재시도 배치는 추후)
- ApprovalService.sendStepNotification — DB 알림 외 SMS 어댑터 호출 (non-critical)
- WithdrawRequestMapper.updateTransferResult 추가
2026-05-24 02:10:06 +09:00
GA Pro c005ad705a feat: 분급 비율 회차(월) 단위 등록 지원 (V81)
- installment_ratio.plan_month INT NULL 컬럼 추가
  - NULL = 연 단위(연차 합계, 기존 동작)
  - 1~12 = 해당 회차(월) 단위 비율
- UNIQUE 재정의: NULLS NOT DISTINCT (product_category, plan_year, plan_month, effective_from)
- InstallmentPlanGenerator: planMonth=NULL이면 1, NOT NULL이면 그대로 사용해 settle_month 계산
- 분급비율 화면: 회차(월) 컬럼·입력 추가, "(연차-1)*12+월" 통합 회차 표기
2026-05-24 01:27:47 +09:00
GA Pro 3f52596a8b fix: 전체 메뉴 점검 — 회계분개/KPI 매퍼 컬럼 오류 + 결재 mine/pending 엔드포인트
전체 화면 API 점검(85종) 중 발견한 오류 수정:

- ContractAccountingEntryMapper: ac.contract_no 미존재 -> ac.policy_no AS contract_no
  (contract 테이블 컬럼은 policy_no. 데이터 적재 후 표면화된 버그)
- KpiMapper: mv_*.refreshed_at 가 now() 기반 timestamptz 인데 VO는 LocalDateTime
  -> refreshed_at::timestamp 캐스팅 (monthly/org/cumulative/retention 4종)
- ApprovalController/Service: /requests/mine, /requests/pending 엔드포인트 부재로
  /requests/{requestId} 에 매칭돼 400 -> 두 엔드포인트 신규 추가

검증: 전체 엔드포인트 ERROR 0건. 정산 배치 데이터 + KPI MV 새로고침으로
KPI 대시보드 4화면(월별/조직별/누적/유지율) 정상 표시.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 01:41:26 +09:00
GA Pro c2a92d13cb fix: 정산 배치 파이프라인 정합 — 매퍼 NULL 파라미터 + override_settle 제약 + 정산지급 데이터
정산/지급 화면이 전부 비어있던 원인: 운영 DB에서 정산 배치가 실행된 적 없음.
MONTHLY_SETTLEMENT 배치를 돌리는 과정에서 발견한 버그 수정 + 데이터 구성.

매퍼 버그 (PG: could not determine data type of parameter):
- RegulatoryLimitMapper/ChargebackGradeMapper/InstallmentRatioMapper:
  #{productCategory} 가 IS NULL 비교에 쓰여 타입 추론 실패 -> jdbcType=VARCHAR 명시
- ForecastKpiMonthlyMapper: #{targetOrgId} 동일 -> jdbcType=BIGINT 명시

스키마 (V79):
- override_settle.settle_id NOT NULL 완화 — calcOverride(Step7)는 settle_master
  생성(Step8) 전 단계라 settle_id 를 채울 수 없음

데이터 (V80):
- 배치 실행 결과: settle_master 150 / 원장 433 / 명세서 112 / 분개 433 (2026-02~04)
- payment 는 settle_master 파생 생성, 운영성 도메인(공제/마감/세금계산서/이의신청/
  시책/정정)은 대표 샘플 시드 (멱등 NOT EXISTS)

검증: 배치 12스텝 COMPLETED, 정산/지급 10개 엔드포인트 200 OK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 00:48:08 +09:00
GA Pro 6b44be2101 fix: 실행.bat — gradlew 경로를 .\ 명시로 수정 (실행 검증 완료)
원인: NoDefaultCurrentDirectoryInExePath 환경에서 cmd가 현재 폴더의
gradlew.bat 을 못 찾음. 백엔드 기동 라인을 'cd /d %~dp0 && .\gradlew.bat'
으로 수정. 한글/chcp 제거(UTF-8 배치 파서 깨짐 회피)해 순수 ASCII로 작성.

검증: 실행.bat 더블클릭(Start-Process) -> 백엔드(8082)+프론트(3000) 정상 기동,
localhost:3000 화면 200, 프록시 로그인 성공, /api/year-end-statements 정상.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 00:10:23 +09:00
GA Pro dd3c46a7c9 docs: 프로젝트 한 번에 실행하는 방법 (실행.bat + 실행방법.md)
비개발자도 더블클릭 한 번으로 백엔드(ga-api 8082)+프론트(ga-frontend 3000)를
동시에 띄우도록 실행.bat 추가. 실행/접속/종료/문제해결을 실행방법.md에 정리.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:36:13 +09:00
GA Pro 49f08f001d feat: P5-W3 본론 완료 — 회계결산/이상치예측/연말명세서 (V74~V78)
P5-W2 원천세·부가세 분기신고 미커밋분 + P5-W3 메뉴/권한 보강(V74) + P5-W3 본론(V75~V78)을 일괄 정리.

- V75 accounting_close / account_balance_snapshot (회계 결산)
- V76 retention_anomaly_alert / forecast_kpi_monthly (이상치·예측 KPI)
- V77 year_end_statement (연말 지급명세서)
- V78 P5-W3 본론 메뉴 4 + 권한 + 공통코드 7그룹 + 테스트데이터
- api: AccountingClose/YearEndStatement/ForecastKpi/RetentionAnomaly Service+Controller
- batch: BatchConfig Step 11/12 (원천세·부가세 분기) 연결
- frontend: 4화면(AccountingClose/YearEndStatement/RetentionAnomaly/ForecastKpi) + API 모듈 4 + 라우트
- frontend: usePermission/PermissionButton EXECUTE 권한 누락 보강
- fix: YearEndStatementMapper.xml 미존재 컬럼 a.agent_code 참조 제거

통합검증: Flyway V74~V78 운영DB 적용(v78), 4모듈 compileJava + 프론트 build PASS,
스모크 GET 8/8 + 쓰기 4/4 PASS, verify_v74.py 20/20 PASS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:22:34 +09:00
GA Pro d5ed642c80 동기화 2026-05-15 00:25:18 +09:00
ysukkyu 88e14edffd 1 2026-05-14 18:14:49 +09:00
GA Pro c0105c6847 동기화 2026-05-14 01:22:47 +09:00
GA Pro 958fe6b980 동기화 2026-05-14 01:21:27 +09:00
GA Pro acea2afce5 docs: 세션 마무리 핸드오프 노트 — 잔여 P2/P3 작업 정리
2026-05-12 세션에서 코드 품질 5단계 + 도메인 P0/P1 완료(26 커밋).
다음 세션에서 P2 Wave 방식 진입을 위한 핸드오프 문서.

내용:
- 핸드오프 시점 상태 (마지막 커밋, 빌드 상태)
- 완료 작업 요약 (코드 품질 / P0 / P1)
- 잔여 작업 (P1 정산 통합 / P2 8항목 / P3 KPI / 마무리 통합)
- 재개 절차 (팀 재생성, 권장 시작점)
- PL 가이드 (시간차 중복 메시지 처리, 책임 경계)
- 채택한 표준 가정값 (세율/1200%/분급/차등환수)
2026-05-13 00:58:56 +09:00
GA Pro 3831eeaf89 feat(api): ChargebackCalculator + ChargebackGradeService + Controller (도메인 P1-3-c)
도메인 P1 마지막 항목 — 차등 환수 마무리.

ChargebackCalculator (pure @Component):
- calculate(contractDate, terminationDate, originalCommission, productCategory)
- monthsElapsed = ChronoUnit.MONTHS.between
- selectByMonths로 적용 비율 조회
- chargebackAmount = originalCommission × percent / 100 (HALF_UP, scale 0)
- grade 없으면 ZERO + "적용 환수 구간 없음"

ChargebackResult record:
- chargebackAmount, monthsElapsed, appliedPercent, reason

ChargebackGradeService:
- list/detail (readOnly)
- create/update (@Transactional, 중복 검증)
- deactivate (@Transactional)

ChargebackGradeController:
- GET/POST/PUT 표준 + /api/chargeback-grades/calculate (외부 호출용)
- 모든 보호 엔드포인트 @RequirePermission, CUD에 @DataChangeLog

ga-batch 정산 step 자동 통합은 마무리 통합 단계에서.
2026-05-13 00:56:24 +09:00
GA Pro 44dbbf37da feat(core): ChargebackGrade VO 세트 + Mapper (도메인 P1-3-b)
V28 chargeback_grade와 ga-core 동기화. 차등 환수 비즈니스 로직은 P1-3-c.

신규 패키지: com.ga.core.vo.chargeback, com.ga.core.mapper.chargeback

VO 세트:
- ChargebackGradeVO (BaseVO 상속, monthsFrom/monthsTo NULL=무한대)
- ChargebackGradeResp (productCategoryName + monthsRange 표시 문자열)
- ChargebackGradeSaveReq (@Valid: @Min(0) monthsFrom/percent)
- ChargebackGradeSearchParam (productCategory/monthsElapsed/effectiveDate)

ChargebackGradeMapper:
- 표준 CRUD
- selectByMonths(productCategory, monthsElapsed, baseDate)
  → 해당 경과월에 적용되는 환수 비율 1건 (months_from <= monthsElapsed
    AND (months_to IS NULL OR months_to >= monthsElapsed)) + 유효기간
    + product_category NULL 안전 비교 + LIMIT 1 최신 우선
- selectAll(baseDate) — months_from ASC + product_category NULLS FIRST
- selectByCategory(productCategory, baseDate)

XML 공통 activeCondition SQL fragment로 중복 제거.
2026-05-13 00:53:47 +09:00
GA Pro f6142a171f fix(api): RegulatoryLimitChecker TODO 해소 — 실제 누적 합산 적용 (P1 보강 후속)
P1-1-c에서 TODO로 두었던 누적 수수료 합산을 ga-core의 신규 메서드로 교체.

기존 (BigDecimal.ZERO 반환 — 모든 결과 한도 미만):
- sumPaidByContract()
- sumFirstYearPaidByContract()

교체:
- sumPaidByContract = recruit.sumByContract + maintain.sumByContract
- sumFirstYearPaidByContract = recruit.sumFirstYearByContract
  (maintain은 1차년 개념 없음)
- null-safe 헬퍼 추가

checkContractTotal / checkFirstYear가 이제 실제 누적값 기반 검증 동작.
2026-05-13 00:53:01 +09:00
GA Pro 1aa31deb77 feat(core): RecruitLedger/MaintainLedger sumByContract 메서드 추가 (P1 보강)
P1-1-c RegulatoryLimitChecker의 누적 수수료 합산용 TODO 해결.

추가 메서드:
- RecruitLedgerMapper.sumByContract(contractId)
- RecruitLedgerMapper.sumFirstYearByContract(contractId) — commission_year=1 한정
- MaintainLedgerMapper.sumByContract(contractId)

XML: COALESCE(SUM(agent_amount), 0) 패턴 (NULL 안전).
참고: V5 DDL 실제 컬럼명은 agent_amount (commission_amount 아님).

api-developer가 RegulatoryLimitChecker에서 이 메서드를 호출하면 TODO 해소.
2026-05-13 00:50:57 +09:00
GA Pro cc5d0e55f2 feat(db): V28 차등 환수 룰 (해약 경과월별 환수 비율) (도메인 P1-3-a)
도메인 P1 세번째 항목 — 차등 환수. DB 스키마/데이터만.

신규 테이블 chargeback_grade:
- (months_from, months_to) 범위형 룰
- product_category NULL=ALL (상품별 차등 가능)
- effective_from/to 개정 이력 추적
- CHECK 제약: months_from >= 0 AND months_to >= months_from

기존 V3 chargeback_rule(계약별 일반 룰)과 별개 — 단계별 환수 비율 전담.

기본 데이터 (2024-01-01, NULL=ALL, 표준 5단계):
- 1~12개월:  100% (계약 후 1년 이내 해약)
- 13~24개월: 50%
- 25~36개월: 25%
- 37~84개월: 10%
- 85개월 이상: 0% (환수 면제, 분급 7년 종료 후)

인덱스:
- (product_category, months_from, effective_from DESC) 환수율 조회
2026-05-13 00:49:30 +09:00
GA Pro 5029bc55d4 feat(core): Installment VO 세트 + Mapper + InstallmentStatus Enum (도메인 P1-2-b)
V27 분급 스키마와 ga-core 동기화. 분급 계획 생성 로직은 P1-2-c.

신규 패키지: com.ga.core.vo.installment, com.ga.core.mapper.installment

Enum:
- InstallmentStatus {SCHEDULED, PAID, DEFERRED, CANCELLED}

InstallmentRatio VO 세트 (분급 비율 마스터):
- VO/Resp/SaveReq/SearchParam 4종
- SaveReq: @Min(1)/@Max(7) planYear, @Positive ratioPercent

InstallmentPlan VO 세트 (계약별 분급):
- VO/Resp/SearchParam 3종
- Resp에 contract/product/agent 조인 표시
  (policyNo, productName, agentName, statusName)

Mapper 2종:
- InstallmentRatioMapper: selectActive(category, year, baseDate),
  selectAllActiveRatios(category, baseDate) — 1~7차년 비율 일괄 조회
- InstallmentPlanMapper: insertBatch(<foreach>), selectByContract,
  selectScheduledByMonth, updateStatus(paidAmount/paidAt/paymentId)

XML 핵심:
- 활성 비율: effective_from/to + is_active=TRUE + LIMIT 1 (최신 우선)
- selectAllActiveRatios: plan_year ASC (1~7년 순서 보장)
- selectScheduledByMonth: settle_month + status='SCHEDULED' (배치 진입점)
2026-05-13 00:45:53 +09:00
GA Pro 434e7783a9 feat(db): V27 분급 계획 + 분급 비율 마스터 (도메인 P1-2-a)
도메인 P1 두번째 항목 — 분급(installment_plan). DB 스키마/데이터만.

신규 테이블:
- installment_ratio (분급 비율 마스터, effective_from/to 이력 추적)
- installment_plan (계약별 분급 계획)

기본 비율 데이터 (2024-01-01, NULL=ALL):
- 1차년 35%, 2차년 25%, 3차년 15%, 4차년 10%,
  5차년 7%, 6차년 5%, 7차년 3% (합 100%)
- 시기/상품별 정책 변경 시 새 effective_from 추가

installment_plan 핵심:
- (contract_id, plan_year, plan_month) UNIQUE
- payment_id로 실제 지급 시 역참조
- status: SCHEDULED/PAID/DEFERRED/CANCELLED

인덱스:
- installment_plan (contract_id, plan_year, plan_month) 계약별 조회
- (settle_month, status) 월별 지급 예정 조회
- (payment_id) payment 역참조
- installment_ratio (product_category, plan_year, effective_from DESC)
2026-05-13 00:41:23 +09:00
GA Pro bba4a33143 feat(api): RegulatoryLimitChecker + Service + Controller (도메인 P1-1-c, 부분 TODO)
P1-1 마무리. 검증 인프라 + 마스터 CRUD 완성.

신규:
- service/regulatory/ViolationResult (record + ok/violated 팩토리)
- service/regulatory/RegulatoryLimitChecker (@Component)
  - checkContractTotal(contractId, additionalAmount, baseDate) — 1200% 한도
  - checkFirstYear(contractId, additionalAmount, baseDate) — 1차년 35% 한도
- service/regulatory/RegulatoryLimitService (CRUD + selectActive)
- controller/regulatory/RegulatoryLimitController
  - GET/POST /api/regulatory-limits, GET/PUT /{id}, PUT /{id}/deactivate
  - POST /check-contract (단건 검증)
- controller/regulatory/ContractLimitCheckReq

TODO (P1 마무리 단계에서 정리 예정):
- RegulatoryLimitChecker.sumPaidByContract / sumFirstYearPaidByContract
  현재 BigDecimal.ZERO 반환 (모든 결과가 한도 미만으로 나옴)
- ga-core RecruitLedgerMapper에 sumByContract / sumFirstYearByContract
  메서드 추가 후 실제 합산값으로 교체 필요
- 검증 인프라 자체는 완성, 데이터 연결만 보강 필요
2026-05-13 00:39:24 +09:00
GA Pro 11092537d7 feat(core): RegulatoryLimit VO 세트 + Mapper + Enum 3종 (도메인 P1-1-b)
V26 규제 한도 마스터와 ga-core 동기화. 검증 로직은 P1-1-c.

신규 패키지: com.ga.core.vo.regulatory

Enum 3종:
- LimitType {TOTAL_RATIO, FIRST_YEAR_RATIO, INSTALLMENT_YEARS}
- LimitUnit {PERCENT, YEAR}
- ProductCategory {ALL, LIFE, ANNUITY, SAVINGS, EXCLUDED_AUTO, EXCLUDED_HEALTH}

RegulatoryLimit VO 세트:
- RegulatoryLimitVO (BaseVO 상속)
- RegulatoryLimitResp (코드명 표시)
- RegulatoryLimitSaveReq (@Valid: limitType/unit @NotBlank, limitValue @Positive)
- RegulatoryLimitSearchParam (limitType/productCategory/effectiveDate)

RegulatoryLimitMapper (com.ga.core.mapper.regulatory):
- 표준 CRUD + 활성 한도 조회 메서드 2종:
  - selectActiveByType(limitType, productCategory, baseDate) — 특정 날짜 유효 1건
  - selectAllActive(baseDate) — 특정 날짜 유효 전체

XML:
- effective_from <= baseDate AND (effective_to IS NULL OR effective_to >= baseDate)
- is_active = TRUE
- 최신 개정본 우선 (ORDER BY effective_from DESC LIMIT 1)
- product_category NULL=ALL 처리 안전 비교
2026-05-13 00:33:25 +09:00
GA Pro 6a6ba9ff0e feat(db): V26 규제한도 마스터 + 1200%룰 기본 데이터 (도메인 P1-1-a)
도메인 P1 시작 — 1200%룰 검증의 한도 마스터. Java 동기화는 P1-1-b/c.

신규 테이블 regulatory_limit:
- 감독규정 개정 이력 추적 (effective_from/to)
- product_category NULL=ALL, 또는 LIFE/ANNUITY/SAVINGS/EXCLUDED_AUTO/EXCLUDED_HEALTH
- limit_type: TOTAL_RATIO(1200%), FIRST_YEAR_RATIO(1차년 35%), INSTALLMENT_YEARS(7년)
- is_active 부분 인덱스로 활성 한도만 빠르게 조회

기본 데이터 (2024-01-01 기준, 외부화 가능):
- TOTAL_RATIO ALL 1200% (보험업감독규정 제5-15조의5)
- FIRST_YEAR_RATIO ALL 35%
- INSTALLMENT_YEARS ALL 7년

코드그룹: LIMIT_TYPE, LIMIT_UNIT, PRODUCT_CATEGORY

인덱스:
- (limit_type, product_category, effective_from DESC) 한도 조회용
- (is_active) WHERE is_active = TRUE 부분 인덱스

ON CONFLICT DO NOTHING으로 재실행 안전.
2026-05-13 00:30:19 +09:00
GA Pro f5efcaa3ac feat(api): CommissionStatementService + ExcelStatementGenerator + Controller (도메인 P0-3-c)
도메인 P0(세무·공제·명세서) 마지막 항목. 이번 단계는 EXCEL 형식 우선,
PDF는 별도 향후 작업.

신규 도메인 클래스 (service/statement/):
- StatementSummary (record, 6필드: gross/incomeTax/localTax/vat/deduction/net)
- StatementAggregator (@Component, PaymentMapper.selectByAgentAndMonth로 집계)
- ExcelStatementGenerator (@Component, SXSSFWorkbook 직접)
- CommissionStatementService

CommissionStatementService:
- list/detail — readOnly + PageHelper
- generate(SaveReq) — @Transactional:
  1. aggregator.aggregate(agentId, settleMonth)
  2. excelGenerator.generate(agent, summary, payments) → byte[]
  3. fileService.saveBytes → fileId
  4. selectByAgentMonth → 기존 있으면 UPDATE(재발급), 없으면 INSERT
  5. issue_status=GENERATED, issued_at, issued_by 기록
- generateForMonth(settleMonth, statementType) — 월 일괄, payment의 distinct agentId 순회
- download(id) — file_path 조회 → byte[] 반환 + DOWNLOADED 전이
- cancel(id) — CANCELLED 전이

CommissionStatementController:
- GET /api/statements (페이지)
- GET /api/statements/{id}
- POST /api/statements/generate (@Valid, @DataChangeLog)
- POST /api/statements/generate-for-month (@DataChangeLog)
- GET /api/statements/{id}/download (binary 응답)
- PUT /api/statements/{id}/cancel (@DataChangeLog)

PDF 요청 시 INVALID_PARAMETER BizException — PDF는 별도 작업으로 미룸.

Mapper 추가 (ga-core):
- PaymentMapper.selectByAgentAndMonth
2026-05-13 00:28:08 +09:00
GA Pro 4f5a2f0cb9 feat(core): CommissionStatement VO 세트 + Mapper + StatementType/Status Enum (도메인 P0-3-b)
V25 명세서 스키마와 ga-core 동기화. 발급/생성 로직은 P0-3-c.

신규:
- vo/settle/StatementType {MONTHLY, ANNUAL, PROOF}
- vo/settle/StatementStatus {GENERATED, SENT, DOWNLOADED, CANCELLED}

CommissionStatement VO 세트:
- CommissionStatementVO (BaseVO 상속)
- CommissionStatementResp (agentName/statementTypeName/issueStatusName 표시)
- CommissionStatementSaveReq (@Valid: @NotNull agentId, @NotBlank settleMonth(@Size 6), statementType, fileFormat)
- CommissionStatementSearchParam (agentId/settleMonth/statementType/issueStatus + fromDate/toDate)

CommissionStatementMapper:
- selectList / selectDetailById / selectById / selectByAgentMonth /
  insert / update / updateStatus
- XML: VOMap/RespMap, STATEMENT_TYPE/STATUS 코드명 서브쿼리,
  날짜 범위 PostgreSQL 캐스트, COALESCE 기본값, <set> 동적 update
- selectByAgentMonth는 UNIQUE(agent_id, settle_month, statement_type)
  기반 — 재발급 시 기존 row 갱신 판단에 사용
2026-05-13 00:24:28 +09:00
GA Pro 930335ce3f feat(db): V25 수수료 명세서 스키마 + 코드그룹 (도메인 P0-3-a)
도메인 P0 마지막 항목 — 수수료 명세서. DB 스키마/데이터만.

신규 테이블 commission_statement:
- agent_id + settle_month + statement_type 유니크
- 발급 시점 집계 스냅샷 (gross/incomeTax/localTax/vat/deduction/net)
- 파일 경로/포맷/발급 상태
- 재발급은 PUT으로 row 갱신 (이력 분리보다 최신본 단순 관리)

코드그룹:
- STATEMENT_TYPE: MONTHLY(월별), ANNUAL(연간 누계), PROOF(소득증빙)
- STATEMENT_STATUS: GENERATED, SENT, DOWNLOADED, CANCELLED

인덱스:
- (agent_id, settle_month, statement_type DESC) 설계사 조회용
- (issued_at DESC) 최근 발급 조회용
2026-05-13 00:20:50 +09:00
GA Pro b8f33f13d1 feat(api): DeductionService + PaymentService 차감 통합 + DeductionController (도메인 P0-2-c)
P0-2 공제/차감 도메인 마무리. ga-batch 정산 step 통합은 도메인 P0 마무리에서.

DeductionService (신규):
- CRUD: list/detail/create/update/cancel
- create는 (agent_id, settle_month, deduction_type) 유니크 중복 검증
- cancel은 status를 CANCELLED로 전이
- 정산용: sumPendingByAgent, selectPendingByMonth, markAsApplied(batch)

PaymentService 확장:
- applyDeductions(paymentId, settleMonth) — 단건:
  1. PENDING 차감 조회
  2. payment_deduction_detail INSERT
  3. agent_deduction.status → APPLIED 일괄 전이
  4. payment.deduction_amount UPDATE
  5. net_amount 재계산 (newNet = currentNet - deductionTotal)
- applyDeductionsForMonth(settleMonth) — 월 일괄

net_amount 공식 갱신:
- 세금 적용(calculateTaxes): amount - incomeTax - localTax + vat
- 차감 적용(applyDeductions): currentNet - deductionTotal
- 최종: amount - incomeTax - localTax + vat - deduction

Mapper 추가 (ga-core):
- AgentDeductionMapper: selectPendingByAgent, updateStatusBatch, countByAgentMonthType
- PaymentMapper: updateDeduction

DeductionController + PaymentController 엔드포인트 추가:
- /api/deductions CRUD (5종)
- /api/payments/{id}/apply-deductions
- /api/payments/apply-deductions?settleMonth=YYYYMM
2026-05-13 00:18:53 +09:00
GA Pro a3ba7fa361 feat(core): 공제 VO 세트 + Mapper + DeductionType/Status Enum (도메인 P0-2-b)
V24 공제 스키마와 ga-core 동기화. 비즈니스 로직은 P0-2-c.

신규:
- vo/settle/DeductionType {LOAN, ADVANCE, GUARANTEE, DUES, AD, CHARGEBACK_PENDING, OTHER}
- vo/settle/DeductionStatus {PENDING, APPLIED, CANCELLED}

AgentDeduction VO 세트 (표준 5종):
- AgentDeductionVO (감사 4종 포함)
- AgentDeductionResp (agentName/deductionTypeName/statusName 조인 표시 필드)
- AgentDeductionSaveReq (@Valid)
- AgentDeductionSearchParam (settleMonth/agentId/deductionType/status)

PaymentDeductionDetailVO (이체 명세 단순 VO)

Mapper (2종 신규):
- AgentDeductionMapper: selectList/Detail/insert/update/updateStatus/
  sumPendingByAgent/selectPendingByMonth
- PaymentDeductionDetailMapper: selectByPaymentId/insert/insertBatch

PaymentVO.deductionAmount + PaymentMapper.xml VOMap 갱신.
2026-05-13 00:14:09 +09:00
GA Pro 0e73eb61a9 feat(db): V24 공제/차감 스키마 + 차감 코드그룹 (도메인 P0-2-a)
도메인 P0 두번째 항목 — 공제/차감. 본 커밋은 DB 스키마/데이터만.
ga-core/ga-api 동기화는 P0-2-b/c.

신규 테이블:
- agent_deduction (월별 차감 마스터)
  - UNIQUE (agent_id, settle_month, deduction_type)
  - PENDING/APPLIED/CANCELLED 상태
- payment_deduction_detail (이체 시 적용된 차감 명세)

payment 컬럼 추가:
- deduction_amount NUMERIC(15,2) DEFAULT 0 (차감 합계)
- net_amount 공식은 P0-2-c에서 (amount - tax - deduction + vat)로 갱신 예정

common_code 코드그룹:
- DEDUCTION_TYPE: LOAN, ADVANCE, GUARANTEE, DUES, AD,
  CHARGEBACK_PENDING, OTHER
- DEDUCTION_STATUS: PENDING, APPLIED, CANCELLED

인덱스:
- agent_deduction (settle_month, agent_id) 복합
- payment_deduction_detail (payment_id)

ON CONFLICT DO NOTHING으로 재실행 안전.
2026-05-13 00:02:05 +09:00
GA Pro f8c7d8459b feat(api): TaxCalculator 도메인 + PaymentService 세금 통합 + AgentService 세무 매핑 (도메인 P0-1-c)
P0-1 세무 처리 마무리. ga-batch 정산 step 통합은 도메인 P0 마무리 단계.

TaxCalculator (신규, ga-api/service/tax):
- @Component pure function: TaxBreakdown calculate(amount, taxType, vatType)
- record TaxBreakdown {incomeTax, localTax, vat, netAmount}
- 세율은 SystemConfigService로 외부화 (TAX_RATE_BUSINESS_INCOME 등)
- 공식:
  - incomeTax = amount × incomeRate / 100 (ROUND_HALF_UP, scale 0)
  - localTax = incomeTax × localRate / 100
  - vat = (TAXABLE) ? amount × vatRate / 100 : 0
  - netAmount = amount - incomeTax - localTax + vat

PaymentService:
- calculateTaxes(paymentId) — 단건 재계산 @Transactional
- calculateTaxesForMonth(settleMonth) — 월별 일괄 @Transactional + batch update

PaymentMapper (ga-core):
- updateTaxes / updateTaxesBatch / selectBySettleMonth 추가

AgentService.toVO():
- taxType/vatType/businessNo 매핑 + default 처리
- 사업소득자인데 businessNo 미입력 시 경고 로그

PaymentController:
- POST /api/payments/{id}/calculate-tax (@RequirePermission, @DataChangeLog)
- POST /api/payments/calculate-taxes?settleMonth=YYYYMM
2026-05-13 00:00:13 +09:00
GA Pro 92f32175ad feat(core): TaxType/VatType Enum + Agent/Payment 세무 필드 (도메인 P0-1-b)
V23 마이그레이션과 ga-core 동기화. 세금 계산 비즈니스 로직은 P0-1-c에서.

신규:
- vo/org/TaxType (BUSINESS_INCOME, OTHER_INCOME) — V23 CHECK 제약 근거
- vo/org/VatType (NONE, EXEMPT, TAXABLE) — V23 CHECK 제약 근거

AgentVO/AgentSaveReq/AgentResp 컬럼 추가:
- taxType (@NotBlank, default BUSINESS_INCOME)
- businessNo (@Size 20, 사업소득자만 필수 — 소프트 검증)
- vatType (@NotBlank, default NONE)
- AgentResp는 taxTypeName/vatTypeName 표시 필드 함께 노출

PaymentVO 컬럼 추가:
- incomeTaxAmount (원천소득세)
- localTaxAmount (지방소득세)
- vatAmount (부가세)
- netAmount (차감 후 실지급액)

Mapper XML:
- AgentMapper.xml — VOMap/agentJoinCols/insert/update 갱신, TAX_TYPE/VAT_TYPE
  코드명 스칼라 서브쿼리로 자동 매핑
- PaymentMapper.xml — VOMap에 세무 4개 컬럼 추가
2026-05-12 23:56:14 +09:00
GA Pro ad5d62ea07 feat(db): V23 세무처리 스키마 + 표준 세율 system_config (도메인 P0-1-a)
도메인 누락 P0 첫 항목 — 세무 처리. 본 커밋은 DB 스키마/데이터만.
다음 sub-step에서 ga-core VO/Enum, 그 다음 ga-api Service 통합.

agent 컬럼 추가:
- tax_type VARCHAR(20) DEFAULT 'BUSINESS_INCOME'
  (BUSINESS_INCOME 사업소득 3.3% / OTHER_INCOME 기타소득 8.8%)
- business_no VARCHAR(20) (사업자등록번호)
- vat_type VARCHAR(20) DEFAULT 'NONE'
  (NONE / EXEMPT 면세 / TAXABLE 일반과세)
- 두 컬럼 모두 CHECK 제약

payment 컬럼 추가:
- income_tax_amount (원천소득세)
- local_tax_amount (지방소득세)
- vat_amount (부가세)
- net_amount (차감 후 실지급액)

system_config 표준 세율(2024 기준, 외부화 가능):
- TAX_RATE_BUSINESS_INCOME = 3.3
- TAX_RATE_OTHER_INCOME = 8.8
- TAX_RATE_LOCAL_INCOME = 10.0
- TAX_RATE_VAT = 10.0

common_code 코드그룹 추가: TAX_TYPE, VAT_TYPE

모든 INSERT는 ON CONFLICT DO NOTHING으로 재실행 안전.
2026-05-12 23:51:57 +09:00
GA Pro b16e678e5f refactor(api): MapStruct 적용 + AgentOrgHistoryResp 적용 + PaymentService/UploadTemplateService 신설 (리뷰 5b)
코드 품질 자동 체인 마지막 단계. ga-api에 5a 산출물 적용 + 잔여 Controller
Mapper 직접 주입 제거.

MapStruct 적용 (수동 매핑 제거):
- ContractService.create/update — ContractMapStruct.toVO/updateVO
- LedgerService — ExceptionLedgerMapStruct.toVO
- UserService.create/update — UserMapStruct.toVO/updateVO

AgentOrgHistoryResp 적용:
- AgentService.history() 반환 List<AgentOrgHistoryVO> → List<AgentOrgHistoryResp>
- AgentMapper.selectHistoryResp(agentId) 사용
- AgentController history 시그니처 동반 변경 — 감사 필드 누설 + FK ID 노출 문제 해소

잔여 Controller Mapper 직접 주입 제거:
- AgentController/LedgerController — AgentService.exportCursor /
  LedgerService.exportRecruitCursor/exportMaintainCursor 위임
- PaymentService 신설 — PaymentController에서 Mapper/TransferFileService
  직접 주입 제거, PageHelper.startPage Service로 이전
- UploadTemplateService 신설 — 3개 Mapper(template/column/history) Service로 이전

PageHelper 위치 통일:
- grep -rn "startPage" controller/ → 결과 없음 (모두 Service로 이전)

남은 예외:
- BatchTriggerController.BatchJobLogMapper — 배치 트리거 특수 패턴이라 보류
- PaymentStatus Enum — 도메인 단계에서 처리 예정
2026-05-12 23:48:44 +09:00
GA Pro 4b211ecdfe feat(core): AgentOrgHistoryResp + MapStruct mapper 3종 추가 (리뷰 5a)
코드 품질 5단계 ga-core 준비물. ga-api 적용은 5b에서 진행.

AgentOrgHistoryResp 추가:
- AgentOrgHistoryVO 직접 노출 문제 해소 (감사 필드 누설 + FK ID만 노출)
- 표시용 조인 필드 포함: agentName, from/toOrgName, from/toGradeName,
  changeTypeName (CHANGE_TYPE 코드그룹 스칼라 서브쿼리)
- AgentMapper.selectHistoryResp + AgentMapper.xml 조인 쿼리 추가

MapStruct mapper 3종 (com.ga.core.mapstruct):
- ContractMapStruct (ContractSaveReq → ContractVO + update 패턴)
- ExceptionLedgerMapStruct (ExceptionLedgerSaveReq → ExceptionLedgerVO)
- UserMapStruct (UserSaveReq → UserVO)
- @Mapper(componentModel="spring"), 감사 필드 ignore
2026-05-12 23:42:58 +09:00