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>
This commit is contained in:
GA Pro
2026-06-05 01:10:50 +09:00
parent 934e45d295
commit 00ead64c4c
19 changed files with 1675 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
import api, { PageResponse, unwrap } from './request';
// ── Types ──────────────────────────────────────────────────────────────────
export type PaymentBatchStatus = 'DRAFT' | 'FINALIZED' | 'CANCELLED';
export interface PaymentBatchResp extends Record<string, unknown> {
batchId: number;
settleMonth: string;
batchName: string;
status: PaymentBatchStatus;
statusName: string;
totalCount: number;
totalPayable: number;
finalizedAt?: string;
createdAt: string;
}
export interface PaymentBatchItemResp extends Record<string, unknown> {
itemId: number;
settleId: number;
agentId: number;
agentName: string;
payableAmount: number;
included: boolean;
excludeReason?: string;
}
export interface PaymentBatchDetailResp extends PaymentBatchResp {
items: PaymentBatchItemResp[];
}
export interface PaymentBatchCreateReq {
settleMonth: string;
batchName: string;
}
export interface PaymentBatchItemUpdateReq {
included: boolean;
excludeReason?: string;
}
export interface PaymentBatchSearchParam {
settleMonth?: string;
status?: string;
pageNum?: number;
pageSize?: number;
}
// ── API ────────────────────────────────────────────────────────────────────
export const paymentBatchApi = {
list: (p: PaymentBatchSearchParam) =>
unwrap<PageResponse<PaymentBatchResp>>(
api.get('/api/payment-batches', { params: p }),
),
detail: (id: number) =>
unwrap<PaymentBatchDetailResp>(
api.get(`/api/payment-batches/${id}`),
),
create: (req: PaymentBatchCreateReq) =>
unwrap<number>(api.post('/api/payment-batches', req)),
updateItem: (id: number, itemId: number, req: PaymentBatchItemUpdateReq) =>
unwrap<void>(api.put(`/api/payment-batches/${id}/items/${itemId}`, req)),
finalize: (id: number) =>
unwrap<void>(api.put(`/api/payment-batches/${id}/finalize`)),
cancel: (id: number) =>
unwrap<void>(api.put(`/api/payment-batches/${id}/cancel`)),
export: (id: number) =>
api.get(`/api/payment-batches/${id}/export`, { responseType: 'blob' }),
};