103 Commits

Author SHA1 Message Date
GA Pro 2a27aa31a0 fix: 모집정산 재실행 시 1200% clip 분급 UNIQUE 충돌 — 멱등성 보강
CalcRecruitStep(Step4)이 1200%룰 clip 초과분을 installment_plan(7년)으로 생성할 때,
재계산을 위해 recruit_ledger는 deleteBySettleMonth로 지우면서 분급은 정리하지 않아
같은 정산월을 재실행하면 동일 계약이 또 clip되어 동일 분급키(contract_id,plan_year,
plan_month)로 INSERT → UNIQUE 충돌로 Job 실패하던 버그.

수정: insertBatch 직전에 clip된 계약들의 SCHEDULED(미지급) 분급을 먼저 삭제
(InstallmentPlanMapper.deleteScheduledByContractIds). PAID 분급 이력은 보존하여
PayInstallmentStep 지급분과 충돌하지 않음.

검증: CalcRecruitStepTest 멱등 테스트(clip 시 delete→insert 순서/대상계약 검증) 추가,
전체 build SUCCESS. 라이브 E2E: 202605 정산을 연속 2회 실행 → 둘 다 COMPLETED,
분급 196건 불변(중복 0), settle_master gross 동일, 무결성 유지.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 00:09:35 +09:00
GA Pro 17ab1098ec feat: 1200%룰 이연 분급 도래월 지급(PayInstallmentStep) + 코드베이스 신입교육 주석
PayInstallmentStep(Step 4.5): 1200%룰로 이연된 분급(installment_plan SCHEDULED)을
도래월(settle_month=정산월)에 recruit_ledger로 편입해 실제 지급. 이전엔 이연만 되고
도래월 지급 절차가 없어 차액이 영구 미지급되던 갭을 메움.
- BatchConfig job flow에 step4b(calcRecruit→payInstallment→calcMaintain) 등록
- InstallmentPlanMapper.resetPaidToScheduledByMonth(+XML)로 재실행 멱등
  (진입 시 PAID→SCHEDULED 역산, Step4 deleteBySettleMonth가 원장 정리)
- RecruitLedgerMapper insert에 reconcile_status 컬럼, SettlementContext.installmentPaidCount
- 단위테스트 3건(정상지급/멱등/계약미존재) GREEN

신입교육 주석: 컨트롤러/서비스/공통/프론트 전반에 도메인·코드 설명 주석 추가
(동작 변경 없음 — 빌드/테스트/타입체크 전부 GREEN으로 무회귀 확인).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 23:40:28 +09:00
GA Pro 5f43c945f0 동기화 2026-06-08 21:34:10 +09:00
GA Pro 711f3b5e16 docs: 수수료 레퍼런스에 갭보완 4종(§3-7) 반영 + 간이지급명세서 주민번호 화면 마스킹
- 수수료_계산공식_전체레퍼런스.md: §3-7 감사 보완 4종(A 지급보류/B 환수원천세역분개/
  C 환수채권연체이자/D 간이지급명세서) 추가, schema v126 표기
- SimplifiedPaymentReport: 화면 표시용 주민번호 마스킹(생년월일+성별구분 이후 *).
  엑셀 export는 국세청 제출용이라 원본 유지

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 21:27:26 +09:00
GA Pro fbbabf2d04 feat: 수수료 갭 보완 4종 — 지급보류/환수원천세역분개/연체이자/간이지급명세서 (V124~V126)
감사(서브에이전트 3영역)로 발견한 실제 결함/규제 갭만 선별 보완.

- A 지급보류(HOLD) 지급배치 누락 버그: PaymentBatchItemMapper.insertFromSettleMaster
  status NOT IN('PAID','HOLD') — 보류건이 지급배치에 포함되어 실제 이체되던 결함 수정
- B 환수 원천세 역분개: withholding_tax_adjustment(V124) 멱등 적재 +
  분기 원천세신고 집계 netting(payment UNION ALL adjustment). settle_master 금액흐름 불변
- C 환수채권 연체이자: clawback_receivable.accrued_interest + clawback_interest(V125) +
  ClawbackInterestCalculator(연율/1200), config 기본 0 = 안전폴백, 기존 FIFO 상계가 이자 회수
- D 사업소득 간이지급명세서(월별): commission_statement(MONTHLY) 위 읽기전용 리포트 +
  메뉴 REPORT_SIMPLIFIED_PAYMENT(V126) + 프론트 SimplifiedPaymentReport

검증: ./gradlew build 전모듈 GREEN, 신규 단위테스트 6건, ga-frontend tsc -b PASS,
Flyway V124~V126 운영DB 적용(v126), 적대리뷰 APPROVED(LOW 1건 GREATEST 클램프 즉수정),
B/C/D 라이브DB 검증(롤백) + D admin e2e list/export 200.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 21:22:13 +09:00
GA Pro ff6d399dd3 feat: 수수료 공식 갭 보완 + 8종 신규모듈 풀스택 (V116~V123)
수수료 공식 갭분석(5 병렬감사) 후 확정 버그 3건 수정 + 개념적으로 빠진 모듈 8종 풀스택 구현.

[확정 버그 3건]
- 1200% 누적한도(checkContractTotal)를 배치 CalcRecruitStep에 연결(기존 1차년 한도만 적용
  → 두 한도 중 더 제한적인 값으로 clip+이연). +CalcRecruitStepTest
- BatchInstallmentPlanGenerator planMonth 반영(하드코딩 1 제거, API판과 동일). +단위테스트
- PersistencyBonus 임계유지율 게이트 + FIXED 정액 룰 도출. +PersistencyBonusServiceTest

[신규 모듈 8종]
- MOD-1 도입수수료(recruiter/development) / MOD-2 선지급·차익정산 / MOD-3 환수 분할상환
- MOD-4 익월부활 재지급 / MOD-5 정산대사 입수 / MOD-6 환수 시효 / MOD-7 환수 감면
- MOD-8 채널별 수수료차등(sales_channel)
각 DB(테이블+메뉴+권한+공통코드)→VO/Mapper/Enum→Service/Controller→화면 풀스택.
배치 정산연계: AggregateStep에 도입/익월부활 가산(11종), 선지급 FIFO 상계(멱등 reverse),
MOD-8 채널 우선 요율조회(NULL 폴백 하위호환).

[검증]
- 전체 ./gradlew build(전 모듈+전 테스트) GREEN
- Flyway V116~V123 운영DB 적용(schema v115→v123)
- 라이브 스모크 8 GET=200, MOD-1 RATE공식 50000 실측, 액션 POST 5xx 0건
- 적대적 코드리뷰 APPROVED(MOD-8 계산기 미연결 버그 발견·수정)

스펙: docs/DOMAIN_GAP_MOD1-8.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 17:33:13 +09:00
GA Pro 00ead64c4c feat: P12(#2) 지급대상건 구성·수정 — 정산월 지급대상 배치 구성/확정 (PL 자율진행)
incar 지급대상건구성/수정/조회 대응. 기존 settle_master 건별 confirm/hold 위에
정산월 단위 지급대상 배치를 구성→포함/제외 편집→확정하는 워크플로우 레이어 추가
(AccountingClose DRAFT/마감 패턴 준용, settle_master 상태는 미변경=결합 최소화).

- DB(V115): payment_batch(배치 마스터, status DRAFT/FINALIZED/CANCELLED, total_count/
  total_payable) + payment_batch_item(건, settle_id FK, payable 스냅샷, included/exclude_reason,
  UNIQUE(batch_id,settle_id)) + 메뉴 PAYMENT_TARGET(GRP_SETTLE) + 권한 READ/CREATE/UPDATE/
  EXECUTE + 공통코드 PAYMENT_BATCH_STATUS.
- core: PaymentBatch/Item VO/Resp/SaveReq/SearchParam/Enum + Mapper. insertFromSettleMaster
  (INSERT-SELECT 자동채움: settle_month·payable>0·status!=PAID) + recalcTotals + updateInclude.
- api: PaymentBatchService create(자동채움+집계)/list/detail/updateItem(DRAFT만,재집계)/
  finalize(포함0건 거부)/cancel/export. Controller /api/payment-batches.
- frontend: PaymentTarget 화면(배치 목록 + 구성 모달 + 상세 Drawer items 포함/제외 토글 +
  합계 + 확정/취소/엑셀) + api 모듈 + App 라우트. export 권한 READ로 정렬.

검증: build+test SUCCESSFUL, Flyway V115(schema v115). 라이브 e2e: 202603 create→39건
자동채움(합 15,438,386.9) → 1건 제외 → 38건 재집계(정확히 −35,079) → finalize → 확정후
수정 400 차단 → cancel. 테스트 배치 정리. 스펙 docs/DOMAIN_GAP_P12.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 01:10:50 +09:00
GA Pro 934e45d295 feat: P11(#1) 시상/신인시상 환수 — award_ledger 환수 처리 (PL 자율진행)
incar 유지율시상환수/신인도입유치자시상환수 대응. award_ledger(V92)는 지급만 있고
환수가 없던 갭. 정착지원금/생보운영지원/지점장수당의 clawback_amount+net_amount 패턴으로
통일(신인시상=award_type NEW_RECRUIT로 기존재 → 별도 테이블 불필요).

- DB(V114): award_ledger에 clawback_amount/net_amount 컬럼 추가 + net=award 백필 +
  CHECK(clawback>=0) + status CHECK에 CLAWED_BACK 추가(pg_constraint 동적 탐색 DROP/재생성).
- core: AwardLedgerVO/Resp/SaveReq에 clawbackAmount/netAmount. AwardLedgerMapper
  aggregateByAgent SUM(award_amount)→SUM(net_amount), insert에 두 컬럼, applyClawback UPDATE.
- api: AwardService.createLedger가 net=award−clawback 계산(clawback≤award 검증). 신규
  applyClawback(ledgerId, {clawbackAmount,reason}) — 초과/취소상태 E411, status=CLAWED_BACK.
  PUT /api/awards/ledgers/{id}/clawback.
- frontend: Award 원장 그리드에 환수액/실지급 컬럼 + 합계 + 환수처리 모달(PermissionButton UPDATE).
- AggregateStep 무변경: aggregateByAgent가 net 반환 → 환수가 other_commission 자동 차감,
  net<0이면 P9 환수채권 이월에 자연 연계.

검증: build+test SUCCESSFUL, Flyway V114(schema v114). 라이브: award 100000 원장 →
clawback 30000 → net 70000·CLAWED_BACK, 초과환수 200000 → E411, aggregate net=70000 반영
확인. 테스트 데이터 정리. 스펙 docs/DOMAIN_GAP_P11.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 00:59:48 +09:00
GA Pro 284ecc4203 feat: P10(#3) 환수 보험사자료 업로드 — 보험사 통보 기반 chargeback 인입 (PL 자율진행)
incar '환수보험사자료 업로드' 대응. GA는 LAPSE 계약 기반 자동환수만 있고 보험사가
통보한 환수자료(증권번호+환수금액)를 직접 인입하는 경로가 없던 갭. 신규 테이블 없이
기존 chargeback(V6) 재사용.

- DB(V113): 메뉴 CHARGEBACK_DATA(환수자료관리, GRP_SETTLE, /settle/chargeback-data)
  + 권한 READ/CREATE/EXPORT + 역할부여.
- core: ChargebackUploadExcelVO(@ExcelColumn 증권번호/환수금액/실효월/환수율) +
  ChargebackResp/SearchParam + ChargebackMapper.selectList/countByCondition/
  existsByContractAndMonth + ContractMapper.selectByPolicyNo(contract_date 포함).
- api: ChargebackUploadService.upload(settleMonth,file) — ExcelService.importLargeExcel
  로 SAX 파싱, policy_no→contract/agent 해소(미존재=에러행), 중복(contract+월) skip,
  실효월 YYYYMM→경과월 변환(ChronoUnit), ChargebackVO status=NEW·remain=cb_amount
  insertBatch. ChargebackService.list/export. ChargebackController(/api/chargebacks
  /upload, GET 목록, export).
- frontend: ChargebackData 화면(정산월+파일 업로드, 결과 성공/스킵/에러행 표시, AG Grid
  목록 합계행, 엑셀) + api/chargeback.ts + App 라우트.

검증: build SUCCESSFUL, Flyway V113(schema v113). 라이브 업로드 e2e — 정상 2건 INSERT
(NEW, remain=cb_amount) + 미존재 증권번호 1건 에러행 리포트 + 중복 skip, GET 목록 200,
경과월 변환 동작 확인. 테스트 chargeback 전건 정리. 스펙 docs/DOMAIN_GAP_P10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 00:51:08 +09:00
GA Pro 90256a19a1 feat: P9 #4 — 출금 가용한도를 환수채권 상계 후 실지급액(payable_amount)으로 전환
P9에서 산출하던 payable_amount(=환수 상계 후 실지급액)가 출금 한도에 미반영
(cosmetic)이던 것을 연결. 이제 환수채권 회수가 모바일 출금 가용액을 정확히 줄인다.

- V112: 기존 settle_master.payable_amount 백필 = GREATEST(net_amount,0)
  (V111에서 DEFAULT 0 추가됨 → 구 데이터 보정. clawback 무활동 행만 대상=멱등).
- core: SettleMasterMapper.xml cols에 clawback_recovered/carryover/payable_amount 추가,
  SettleMasterResp 3필드 노출.
- api: MobileMeService.withdrawableBalance 한도 산정을 net→payable_amount 기준으로
  (payable null이면 max(net,0) 폴백). WithdrawableBalance DTO에 payableAmount 추가,
  remainingAmount = payable − used. createWithdrawRequest 가드도 자동 반영.
- PWA: withdraw.ts 타입 + WithdrawCreate 드롭다운/카드를 '실지급가능(payable)' 표기로.

검증: ./gradlew build(test포함) SUCCESSFUL, PWA tsc 0오류. Flyway V112 적용(schema v112).
백필 150/150 행 payable==max(net,0). 라이브 agent01 withdrawable-balance가 payableAmount
노출 + remaining=payable−used(13492−151000=-137508) 정확.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 00:29:50 +09:00
GA Pro 51ee03453c feat: P9 환수채권 이월/미수금 관리 — 수수료 갭 풀스택 (PL 자율진행)
incar CMS 메뉴(환수이월관리/환수계약리스트/위촉중환수) ↔ GA 부재 + 코드 갭
(AggregateStep net<0 시 경고만, 미회수 환수 채권화·차월상계·대손 생애주기 전무)
을 도출해 풀스택 구현. 미회수 환수가 소실되던 재무 누락을 폐쇄.

- DB(V111): clawback_receivable(채권 마스터, UNIQUE agent+origin월) +
  clawback_recovery(상계 회수 이력) + settle_master 3컬럼(clawback_recovered/
  clawback_carryover/payable_amount) + 메뉴 CLAWBACK_RECEIVABLE + 권한 + 공통코드.
- core: VO/Resp/SaveReq/SearchParam/Enum/Mapper(.java/.xml) 2테이블 + MapStruct.
  outstandingBalance(origin<settleMonth 자기상계방지) / FIFO / upsertCarryover(멱등).
- api: ClawbackReceivableService/Controller(/api/clawback-receivables) — list/detail/
  summary/write-off(OUTSTANDING·RECOVERING만, CLEARED/WRITTEN_OFF 전이)/export(@Transactional).
- batch: AggregateStep 통합 — net<0 → carryover 채권화, net>0 → FIFO 상계회수,
  reverse-먼저 방식으로 동일월 재실행 멱등. net_amount 의미 불변(무결성 보존).
  payable_amount = max(0,net) − recovered.
- fix(잠복): SettleMasterMapper.xml upsert에 신규 3컬럼 누락 → INSERT/ON CONFLICT 추가
  (배치가 VO에 set해도 DEFAULT 0으로 미영속되던 버그, PL 검증서 발견).
- frontend: ClawbackReceivable 화면(AG Grid 합계행 + 회수이력 타임라인 Drawer +
  대손/상환 모달) + api 모듈 + App 라우트.

검증(PL 직접): ./gradlew build(test포함) SUCCESSFUL. Flyway V109~V111 운영DB 적용
(schema v111). 라이브 API: GET 목록/summary 200, write-off WRITTEN_OFF 200(한글사유
영속), 종결 재처리·잘못된 전이상태 E411 차단. 배치 이월/상계/멱등/부분상계 4시나리오
실 스키마 롤백 트랜잭션 검증 PASS(CHECK balance>=0 포함). 잔여 데이터 0.

스펙: docs/DOMAIN_GAP_P9_환수채권이월.md. 후속 갭(시상/인정실적 환수 ledger,
지급대상건 구성, 환수 보험사자료 업로드) 문서화.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 00:18:58 +09:00
GA Pro b12858c991 docs: 수수료 종류별 계산공식 전체 정리 + 신입교육 문서 보완
- docs/수수료_계산공식_전체레퍼런스.md 신규: 모든 수수료 종류의 실제 구현
  계산식(코드 기준, 감사 수정 반영) — 모집/유지 3곱, 지급성 11종, 조직·실적,
  횡단(1200%clip/분급/환수/세금/집계net/회계예수금분개).
- 신입교육_보험과수수료_완전기초.md 보완:
  - 6-A 수수료 종류별 계산식 하나씩(미니 숫자예시) — 생애주기/성과조직/소개지원/회사레벨
  - 6-B 기타수수료(예외 가감 PLUS/MINUS, 시책, 해촉정산, 본사정산, 보증보험)
  - 목차 갱신

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 16:41:36 +09:00
GA Pro e2fdc88754 fix: 잔여 백로그 전건 처리 — 회계 예수금/1200%clip/환수가드/net경보 (PL 자율진행)
- fix(a, MED): 회계 분개에 원천세 예수금 leg 분리. (차)8350 지급수수료 gross /
  (대)2530 미지급금 net + (대)2540 예수금 원천세. 기존엔 미지급금에 gross 계상 +
  원천세 분개 누락. account_code '2540' 예수금 추가(V110), 분개쿼리 2-leg 전환.
- fix(b, HIGH): 1200%룰 1차년 한도초과 시 clip+이연. 한도잔여까지만 당월 지급,
  초과분만 분급(7년)으로 이연. 전액 이연 시 당월 원장 미생성 + 세액 재계산.
  (기존: 전액지급 + 전액 분급계획 → 설계 불일치)
- fix(e/f, HIGH/MED): 정착지원금/생보운영지원 환수액 ≤ 지원금 검증으로 net 음수 차단.
  AggregateStep net 음수(공제>지급) 경보 로깅 추가.
- (g) 검토결과 무변경: TaxCalculator/BatchTaxCalculator는 의도된 동일로직 쌍둥이,
  V80 매직팩터는 샘플시드일 뿐 계산경로 아님. 두 쌍둥이 모두 음수가드 적용됨.
- (d) 무변경: 승계 "최신 to_agent=현재귀속자"는 다단계체인에 정합, from==agent_id
  가드는 체인 회귀 유발하므로 정책확정 전 보류(문서화).
- test: CalcRecruitStepTest(clip+이연), SettlingSupportServiceTest(환수>지원 거부).
- 전체 build+test GREEN: 155건 0실패.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 11:27:53 +09:00
GA Pro 60c4cbae78 fix: 오버라이드 insurance_type_cond 미적용 과지급 수정 (PL 우선순위 진행)
- fix(HIGH): CalcOverrideStep base 가 override_rule.insurance_type_cond 를 무시하고
  하위 설계사의 전 보험종류 실적 합계에 율을 곱해 과지급. 룰에 보험종류 조건이
  지정되면(LIFE 등) 해당 종류 실적만 base 로 집계하도록 수정.
  (현 시드는 insurance_type_cond NULL 이라 잠복상태였으나 종류한정 룰 도입 시 과지급)
- add: RecruitLedgerMapper/MaintainLedgerMapper.sumByAgentMonthAndType + XML
- test: CalcOverrideStepTest — LIFE 한정 룰 base 필터 검증
- 전체 build+test GREEN: 153건 0실패

잔여 백로그(정책결정 필요 — 자동수정 보류): 1200%룰 초과분 clip/이연(지급정책),
계약승계 from검증(다단계 체인+contract.agent_id 갱신정책), 정착지원금 환수공식(입력
스키마), 회계 원천세 예수금 분개(회계정책), net음수 클램프(이월저장), 세금 3중경로 통일.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 10:38:09 +09:00
GA Pro eac0b813d6 fix: 수수료 계산 전수감사 — 1200%룰 단위버그 외 5건 수정
적대적 코드감사(4영역)에서 검증된 정확성 결함 수정:
- fix(HIGH): 1200%룰 단위 불일치. regulatory_limit.limit_value(PERCENT:1200,35)를
  KRW 금액과 직접 비교 → 사실상 "1200원/35원 룰"로 망가져 거의 모든 계약이 위반
  처리. 연환산보험료×비율/100로 KRW 환산 후 비교. RegulatoryLimitChecker(api/batch)
  양쪽, ContractMapper 주입(시그니처 무변경).
- fix(HIGH): 음수 gross(환수>지급) 세금 계산이 음수 원천세 생성 → net 왜곡 +
  분기 원천세신고 SUM 잠식. gross<=0 시 세금0·net=원금 가드. Tax/BatchTaxCalculator.
- fix(HIGH): AwardService 시상 threshold_amount 미적용 → 기준 미달자에게 시상
  전액 지급. threshold 미만 차단 + FIXED award_fixed null 가드.
- fix(MED): 차등환수 최저구간 months_from 1→0(V109). 체결 1개월내 즉시해지가
  grade=null로 환수 0원 처리되던 누락 보정.
- fix(MED): company_rate/payout_rate NULL→0 폴백 무경보. log.warn 추가로
  데이터 공백에 의한 과소지급 추적 가능.

테스트 4건 추가(단위변환/음수세금/시상threshold 경계). 전체 build+test GREEN: 152건 0실패.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 10:19:53 +09:00
GA Pro 3d50286f4a test: E2E 수수료 정산 시나리오 테스트 + 엑셀업로드/분급 버그 수정
- 시나리오 문서: 보험 인입→수수료 지급 8단계 (docs/시나리오_E2E_보험인입_수수료정산.md)
- SettlementScenarioTest(9건): 계약1건 생애주기를 실제 계산기로 단계별 검증
- ExcelServiceImportTest(2건): 실 .xlsx 업로드 라운드트립 검증
- fix(CRITICAL): 엑셀 업로드 런타임 깨짐 — xlsx-streamer 2.2.0 ↔ poi 5.2.5
  비호환(NoSuchMethodError). excel-streaming-reader 4.3.0(유지보수 fork)로 교체.
  영향: ExcelService.importLargeExcel + UploadService(/api/upload 실엔드포인트)
- fix: 분급 계획 반올림 잔차로 분급합계≠원금(정산 무결성 위반). 비율합 100%
  정규스케줄일 때 마지막 회차가 잔차 흡수. ga-api/ga-batch 양쪽.
- 전체 build+test GREEN: 148건 0실패

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 10:02:55 +09:00
GA Pro 1d77671ea0 feat: 리포트 3종 백엔드 구현(실적/조직별/환수위험) + Excel export 트랜잭션 수정
- ReportController/Service/Mapper(.xml) + VO 3종(PerfReportRow/OrgReportRow/ChargebackRiskRow)
  - /api/report/performance: settle_month별 settle_master 집계(설계사수/모집/유지/실지급)
  - /api/report/org: 정산월 기준 organization 조인 조직별 집계
  - /api/report/chargeback-risk: recruit_ledger 계약별 경과월 + chargeback_grade 환수율/위험등급
  - 각 read(List) + export(Cursor→xlsx). 신규 테이블 없음(읽기전용 집계)
- ExcelService.exportLargeExcel @Transactional(readOnly=true) 추가 — Cursor 가 세션 밖에서
  닫혀 모든 export(기존 agent 포함)가 500 나던 잠복버그 수정. 1줄로 export 인프라 전체 복구

검증: 3 read 200(실데이터), 3 export 200(유효 xlsx), agent export 복구 확인.
Playwright 84화면 재실행 FAIL 0 (PASS59/WARN25, WARN은 차트/폼 등 탐지한계)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 00:51:37 +09:00
GA Pro 81871615cd fix: Playwright 전메뉴 기능테스트로 발견한 화면 버그 4건 수정
- products: /api/products 가 bare 배열 반환 → PageResponse 정렬(PageHelper).
  contracts/rules-commission 의 products?.list.map 크래시 + products 빈목록 동시 해결
- grade-eval: gradeEvalRules 가 PageResponse(.list) 추출하도록 수정 (rowData.map 크래시)
- batch: batch.ts 를 실제 백엔드(/settlement/run body, /jobs/{id}, /jobs)로 정렬 +
  VO필드(jobId/currentStep/processedCount/finishedAt/durationSec) 어댑터 매핑

검증: Playwright 84화면 재실행 FAIL 7→3 (잔여 3건은 미구현 /api/report/*)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 00:32:45 +09:00
GA Pro 93ac94558f feat: PWA FCM 클라이언트 실통합 + 어댑터 키 주입 런처
- firebase SDK 통합: initPush getToken 활성화(동적 import) + isSupported 가드
- public/firebase-messaging-sw.js 백그라운드 수신 SW(웹 config 쿼리스트링 전달)
- run-api-with-adapters.sh: adapter.env.local 로드 후 ga-api bootRun
- 키 파일(adapter.env.local / .env.local)은 git-ignored 템플릿으로 별도 제공

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 22:56:44 +09:00
GA Pro 5d5c2d53aa fix: 적대적 코드리뷰 후속 — 신규 도메인 5건 버그 수정
서브에이전트 4영역 적대적 리뷰(P7/P8 서비스·매퍼·배치·어댑터)에서 확정된 결함 수정.

- [HIGH] HqSettleItem update 시 status가 NULL로 덮어써짐(SaveReq에 status 없음)
  → status = COALESCE(#{status}, status) 로 수정 불변 보장.
- [MED] 소개·이관수수료 RATE/FIXED 분기를 요청 commissionRate 유무로 판정 → 활성 룰의
  calc_type 을 권위로 사용(FIXED=fixed_amount, RATE=요청rate||룰rate), 룰 없을 때만 요청rate 폴백.
- [MED] 지점장수당 cap 미적용(주석-코드 불일치) → SaveReq.capAmount 추가 + allowance = min(base×rate, cap).
- [MED] PUSH 외부호출 감사로그 요약 누락 → ExternalCallLoggingAspect applyRequest/applyResponse 에
  PushRequest/PushResponse 분기 추가(토큰 마스킹, messageId/sent/resultCode).
- [LOW] 공동계약 update 지분율 검증 키 불일치 → 기존 레코드 contractId 기준으로 검증/갱신(계약 이동 불가).

배치 통합 리뷰 결과 CLEAN(무결성·집계·upsert 정확). 전체 ./gradlew build(test 포함) SUCCESSFUL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:27:14 +09:00
GA Pro 51f340e36e chore: incar 14도메인 데모 샘플데이터(V108) + 외부연동 활성화 가이드
- V108__incar도메인_샘플데이터.sql: P7/P8 14개 도메인 대표 샘플 시드(멱등 NOT EXISTS,
  기준월 202605). 신규 화면이 빈 화면으로 보이지 않도록 — 기존 V65/V69/V80 패턴.
  ROLLBACK 트랜잭션 사전검증(오류 0) 후 Flyway V108 success.
  라이브 확인: 12개 신규 엔드포인트 전부 200 + 샘플 rows 반환.
- docs/외부연동_활성화_가이드.md: KFTC/Toast/FCM 어댑터 운영 활성화 절차
  (전환 스위치 app.adapter.*, 환경변수 매트릭스, 사전조건, 검증법).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:09:12 +09:00
GA Pro 204e3b6975 feat: 푸시 알림 파이프라인 — PushAdapter(FCM HTTP v1) + 디바이스토큰 등록 + 디스패치
마지막 외부연동 영역(푸시)을 채널=FCM HTTP v1 로 확정해 서버측 완전 구현.
Toast/KFTC 와 동일 패턴: 실 API·config-gated(app.adapter.push, 기본 mock)·fail-fast.

- ga-common: PushAdapter 인터페이스 + PushRequest/PushResponse
- ga-external-adapter: MockPushAdapter(기본) + FcmPushAdapter(POST /v1/projects/{id}/messages:send,
  Bearer, notification+data 매핑, name→messageId) + FcmProperties. 의존성 추가 없음.
- V107 user_device_token(user_id/token/platform/is_active, UNIQUE token) + UserDeviceTokenVO/Mapper(upsert/selectActiveByUser/deactivate)
- ga-api:
  - PushDispatchService.sendToUser — 사용자 활성 토큰 전부에 best-effort 푸시(예외 흡수)
  - MobileMe: POST /api/mobile/me/device-tokens (등록 upsert) + /unregister (본인검증 deactivate)
  - WithdrawBankSettleService.notify 에 푸시 디스패치 연동(출금 SETTLED/FAILED 시 인앱+푸시)
  - ExternalCallLoggingAspect 에 PushAdapter 포인트컷 추가(외부호출 감사로그)
  - application.yml app.adapter.push + app.adapter.fcm.* 설정
- ga-mobile-pwa: api/push.ts(register/unregister) + push/initPush.ts(config-gated, VITE_FCM_* 미설정 시 no-op) + 로그인 성공 시 initPush() 훅
- AdapterFailFastTest 에 FCM fail-fast 케이스 추가

검증: 전체 ./gradlew build(test) SUCCESSFUL, PWA 빌드 OK, Flyway V107 success,
디바이스토큰 등록/해제/검증 라이브 e2e PASS(등록200·해제200·is_active=N·빈토큰400).
실 푸시 발송은 Firebase 프로젝트(서버 FCM_PROJECT_ID/ACCESS_TOKEN + PWA VITE_FCM_* + firebase SDK) 주입 후 가능.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:01:58 +09:00
GA Pro 78ffd5a56a feat: 외부연동 어댑터 실구현 — NHN Toast(SMS/알림톡) + KFTC 오픈뱅킹 입금이체
공개 문서화된 실제 API 스펙에 맞춰 스켈레톤을 RestClient 기반 실구현으로 교체.
모두 @ConditionalOnProperty 로 config-gated, 기본은 Mock 유지 → 운영 영향 0.

- ToastMessageAdapter: SMS v3.0(/sms/v3.0/appKeys/{k}/sender/sms) + 알림톡 v2.3
  (/alimtalk/v2.3/appkeys/{k}/messages). X-Secret-Key 인증, MessageChannel 분기.
  응답 header.isSuccessful/resultCode + requestId 매핑. ToastProperties 외부화.
- KftcBankTransferAdapter: 입금이체(/v2.0/transfer/deposit/acnt_num) + 이체결과조회.
  Bearer 토큰, 문서화된 KFTC 필드(bank_tran_id/req_list/rsp_code) 매핑. KftcProperties 외부화.
- 키/토큰 누락 시 fail-fast(IllegalStateException) — 잘못된 운영 적용 즉시 감지.
- application.yml app.adapter.{kftc,toast}.* 설정(env placeholder, 빈 기본값) 추가.
- 의존성 추가 없음(ga-common 의 spring-web RestClient + Jackson 재사용).
- AdapterFailFastTest 3건(설정누락 차단/알림톡 templateCode 필수) 추가.

제외: FCM 푸시 — MessageAdapter 인터페이스/PWA 연동 계약 미정으로 추측구현 회피.
실거래 검증은 운영 키(계약·AppKey·약정계좌·토큰) 주입 후 테스트베드에서 수행 필요.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:50:44 +09:00
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