# P12 — 지급대상건 구성·수정 (#2) 작성: PL, 2026-06-04. 후속 갭 3종 중 마지막. 근거: incar `지급대상건구성`/`지급대상건수정`/`지급대상건조회`. GA는 settle_master를 건별 confirm/hold/release만 가능, **정산월 단위 지급대상 배치 구성(일괄 포함/제외 후 확정)** 레이어 부재. 범위: 풀스택. 기존 settle_master 위의 **구성/스냅샷 레이어**(settle_master 상태는 본 범위에서 변경 안 함 — 결합 최소화). Flyway: **V114 최신 → V115**. 금액 NUMERIC(15,2). ## 1. 개념 - 정산월의 지급대상(settle_master, payable_amount>0)을 모아 **배치(payment_batch)** 로 구성. - 각 건(payment_batch_item)을 **포함/제외**(사유) 편집 → 합계 재계산. - **확정(finalize)**: 배치 잠금(FINALIZED). 확정된 포함건 목록 = 지급대상 확정본(스냅샷). - AccountingClose(V75 DRAFT/마감/재오픈) 패턴 준용. **settle_master.status는 본 범위에서 미변경**(향후 finalize→지급실행 연계는 별도). ## 2. 스키마 (V115) ### payment_batch (지급대상 배치) | 컬럼 | 타입 | 설명 | |---|---|---| | batch_id | BIGSERIAL PK | | | settle_month | CHAR(6) NN | | | batch_name | VARCHAR(100) NN | | | status | VARCHAR(20) NN DEFAULT 'DRAFT' | DRAFT/FINALIZED/CANCELLED | | total_count | INT NN DEFAULT 0 | 포함건수 | | total_payable | NUMERIC(15,2) NN DEFAULT 0 | 포함건 payable 합 | | created_at/created_by/finalized_at/finalized_by/updated_at | | | - idx(settle_month, status). CHECK status IN('DRAFT','FINALIZED','CANCELLED'). ### payment_batch_item (지급대상건) | 컬럼 | 타입 | 설명 | |---|---|---| | item_id | BIGSERIAL PK | | | batch_id | BIGINT NN FK payment_batch | | | settle_id | BIGINT NN FK settle_master | | | agent_id | BIGINT NN FK agent | | | payable_amount | NUMERIC(15,2) NN | settle_master.payable_amount 스냅샷 | | included | BOOLEAN NN DEFAULT true | 포함여부 | | exclude_reason | VARCHAR(300) | 제외사유 | | created_at | | | - UNIQUE(batch_id, settle_id). idx(batch_id). ### 메뉴/권한/코드 - 메뉴 `PAYMENT_TARGET`(지급대상관리), GRP_SETTLE, `/settle/payment-target`, `settle/PaymentTarget`. - 권한 READ/CREATE/UPDATE/EXECUTE(확정) + 역할(SUPER_ADMIN·ADMIN 전체, MANAGER READ). - 공통코드 PAYMENT_BATCH_STATUS(DRAFT=작성중/FINALIZED=확정/CANCELLED=취소). ## 3. API (PaymentBatchService, `/api/payment-batches`) - `POST` create(settleMonth, batchName) @Transactional: payment_batch(DRAFT) INSERT → settle_master 조회(settle_month=, payable_amount>0, status != 'PAID') → 각 행 payment_batch_item(included=true, payable 스냅샷) INSERT → total_count/total_payable 집계 UPDATE. 동일월 DRAFT 중복 생성 허용(이름으로 구분). - `GET` list(settleMonth/status 필터, PageResponse) / `GET /{id}` detail(배치 + items). - `PUT /{id}/items/{itemId}` updateItem(included, excludeReason) @Transactional: DRAFT 배치만(FINALIZED/CANCELLED 시 E411). 항목 갱신 후 배치 total 재집계. - `PUT /{id}/finalize` @Transactional, @RequirePermission EXECUTE: DRAFT→FINALIZED, finalized_at/by. 포함건 0이면 E411. - `PUT /{id}/cancel`: DRAFT/FINALIZED→CANCELLED. - `GET /{id}/export` 엑셀(@Transactional). - @RequirePermission(PAYMENT_TARGET, *) + @DataChangeLog(쓰기). 총계 재집계: `total_count = COUNT(included=true)`, `total_payable = SUM(payable WHERE included)` — Mapper에 recalcTotals(batchId) 제공. ## 4. Frontend (PaymentTarget.tsx) - 배치 목록(정산월/상태 필터) + "구성" 버튼(정산월+배치명 입력 → create). - 배치 행 클릭 → 상세: 배치 헤더(상태/포함건수/합계) + items AG Grid(설계사/payable/포함체크/제외사유). DRAFT일 때 포함토글·사유 편집(PermissionButton UPDATE) → updateItem. 합계행. - "확정" 버튼(PermissionButton EXECUTE, DRAFT만) → finalize. "취소" 버튼. 엑셀. - React Query, 변경 후 invalidate. ## 5. 검증(PL) build+test, Flyway V115, 라이브: create(특정월) → items 자동생성 확인 → updateItem 제외 → total 재집계 확인 → finalize → 잠금(이후 updateItem E411) → cancel. 테스트 데이터 정리.