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>
This commit is contained in:
GA Pro
2026-06-06 17:32:25 +09:00
parent 00ead64c4c
commit ff6d399dd3
114 changed files with 6419 additions and 53 deletions
@@ -0,0 +1,156 @@
-- V116: MOD-1 슈퍼바이저/도입수수료 (recruiter/development commission)
-- agent.recruiter_agent_id 컬럼 추가
-- recruiter_commission_rule / recruiter_commission_ledger 테이블
-- 메뉴 RECRUITER_COMM (GRP_COMMISSION 하위)
-- 공통코드 RECRUITER_TYPE
-- ============================================================
-- 1. agent 테이블에 도입자 컬럼 추가
-- ============================================================
ALTER TABLE agent ADD COLUMN IF NOT EXISTS recruiter_agent_id BIGINT REFERENCES agent(agent_id);
COMMENT ON COLUMN agent.recruiter_agent_id IS '도입자 설계사 ID (NULL=도입자 없음)';
-- ============================================================
-- 2. recruiter_commission_rule (도입수수료 룰 마스터)
-- ============================================================
CREATE TABLE recruiter_commission_rule (
rule_id BIGSERIAL PRIMARY KEY,
recruiter_type VARCHAR(20) NOT NULL,
calc_type VARCHAR(10) NOT NULL,
commission_rate NUMERIC(7,4),
fixed_amount NUMERIC(15,2),
effective_from DATE NOT NULL,
effective_to DATE,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
updated_at TIMESTAMP,
CONSTRAINT chk_rcr_recruiter_type CHECK (recruiter_type IN ('NEW_RECRUIT','DEVELOPMENT','TRAINING')),
CONSTRAINT chk_rcr_calc_type CHECK (calc_type IN ('RATE','FIXED')),
CONSTRAINT chk_rcr_rate_range CHECK (commission_rate IS NULL OR (commission_rate >= 0 AND commission_rate <= 1))
);
COMMENT ON TABLE recruiter_commission_rule IS '도입수수료 룰 마스터';
COMMENT ON COLUMN recruiter_commission_rule.rule_id IS 'PK';
COMMENT ON COLUMN recruiter_commission_rule.recruiter_type IS '도입유형: NEW_RECRUIT/DEVELOPMENT/TRAINING';
COMMENT ON COLUMN recruiter_commission_rule.calc_type IS '계산방식: RATE(정률)/FIXED(정액)';
COMMENT ON COLUMN recruiter_commission_rule.commission_rate IS '정률 요율 (0~1)';
COMMENT ON COLUMN recruiter_commission_rule.fixed_amount IS '정액 금액';
COMMENT ON COLUMN recruiter_commission_rule.effective_from IS '적용 시작일';
COMMENT ON COLUMN recruiter_commission_rule.effective_to IS '적용 종료일 (NULL=무기한)';
CREATE INDEX idx_rcr_type_from ON recruiter_commission_rule(recruiter_type, effective_from);
-- ============================================================
-- 3. recruiter_commission_ledger (도입수수료 원장)
-- ============================================================
CREATE TABLE recruiter_commission_ledger (
ledger_id BIGSERIAL PRIMARY KEY,
recruiter_agent_id BIGINT NOT NULL REFERENCES agent(agent_id),
recruit_agent_id BIGINT NOT NULL REFERENCES agent(agent_id),
settle_month CHAR(6) NOT NULL,
recruiter_type VARCHAR(20) NOT NULL,
base_amount NUMERIC(15,2) NOT NULL,
commission_amount NUMERIC(15,2) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'CALCULATED',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
updated_at TIMESTAMP,
CONSTRAINT chk_rcl_status CHECK (status IN ('CALCULATED','CONFIRMED','PAID','CANCELLED'))
);
COMMENT ON TABLE recruiter_commission_ledger IS '도입수수료 원장';
COMMENT ON COLUMN recruiter_commission_ledger.ledger_id IS 'PK';
COMMENT ON COLUMN recruiter_commission_ledger.recruiter_agent_id IS '도입자 설계사 ID';
COMMENT ON COLUMN recruiter_commission_ledger.recruit_agent_id IS '피도입(신규) 설계사 ID';
COMMENT ON COLUMN recruiter_commission_ledger.settle_month IS '정산월 YYYYMM';
COMMENT ON COLUMN recruiter_commission_ledger.recruiter_type IS '도입유형';
COMMENT ON COLUMN recruiter_commission_ledger.base_amount IS '기준금액 (피도입 당월 모집수수료 합)';
COMMENT ON COLUMN recruiter_commission_ledger.commission_amount IS '도입수수료 금액';
COMMENT ON COLUMN recruiter_commission_ledger.status IS '상태: CALCULATED/CONFIRMED/PAID/CANCELLED';
CREATE INDEX idx_rcl_recruiter_month ON recruiter_commission_ledger(recruiter_agent_id, settle_month);
CREATE INDEX idx_rcl_recruit_month ON recruiter_commission_ledger(recruit_agent_id, settle_month);
-- ============================================================
-- 4. 메뉴 INSERT: RECRUITER_COMM (GRP_COMMISSION 하위 PAGE)
-- ============================================================
INSERT INTO menu (parent_menu_id, menu_code, menu_name, menu_type, menu_path, component_path, menu_level, sort_order)
SELECT p.menu_id, x.code, x.name, 'PAGE', x.path, x.cmpt, 2, x.sort
FROM menu p
JOIN (VALUES
('RECRUITER_COMM', '도입수수료', '/commission/recruiter', 'commission/RecruiterCommission', 150)
) AS x(code, name, path, cmpt, sort)
ON p.menu_code = 'GRP_COMMISSION'
WHERE NOT EXISTS (SELECT 1 FROM menu WHERE menu_code = x.code);
-- ============================================================
-- 5. 메뉴 권한 항목 등록 (READ/CREATE/UPDATE/DELETE/EXPORT)
-- ============================================================
INSERT INTO menu_permission (menu_id, perm_code, perm_name, sort_order)
SELECT m.menu_id, p.code, p.name, p.sort
FROM menu m
CROSS JOIN (VALUES
('READ', '조회', 10),
('CREATE', '등록', 20),
('UPDATE', '수정', 30),
('DELETE', '삭제', 40),
('EXPORT', '엑셀', 60)
) AS p(code, name, sort)
WHERE m.menu_code = 'RECRUITER_COMM'
ON CONFLICT (menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 6. 역할별 권한 부여
-- ============================================================
-- SUPER_ADMIN / ADMIN — 전체 권한
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code IN ('SUPER_ADMIN','ADMIN')
AND m.menu_code = 'RECRUITER_COMM'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- MANAGER — READ/CREATE/UPDATE/EXPORT
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'MANAGER'
AND mp.perm_code IN ('READ','CREATE','UPDATE','EXPORT')
AND m.menu_code = 'RECRUITER_COMM'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- AGENT — READ
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'AGENT'
AND mp.perm_code = 'READ'
AND m.menu_code = 'RECRUITER_COMM'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 7. 공통코드: RECRUITER_TYPE
-- ============================================================
INSERT INTO common_code_group (group_code, group_name, description, is_system, sort_order) VALUES
('RECRUITER_TYPE', '도입수수료유형', 'NEW_RECRUIT=신인도입/DEVELOPMENT=육성/TRAINING=교육', 'Y', 460)
ON CONFLICT (group_code) DO NOTHING;
INSERT INTO common_code (group_code, code, code_name, sort_order) VALUES
('RECRUITER_TYPE', 'NEW_RECRUIT', '신인도입', 10),
('RECRUITER_TYPE', 'DEVELOPMENT', '육성', 20),
('RECRUITER_TYPE', 'TRAINING', '교육', 30)
ON CONFLICT (group_code, code) DO NOTHING;
@@ -0,0 +1,142 @@
-- V117: MOD-2 선지급/차익정산 (advance payment recovery + delta settlement)
-- advance_payment / advance_recovery_ledger 테이블
-- 메뉴 ADVANCE_PAYMENT (GRP_SETTLE 하위)
-- 공통코드 ADVANCE_STATUS
-- ============================================================
-- 1. advance_payment (선지급금 마스터)
-- ============================================================
CREATE TABLE advance_payment (
advance_id BIGSERIAL PRIMARY KEY,
agent_id BIGINT NOT NULL REFERENCES agent(agent_id),
advance_month CHAR(6) NOT NULL,
principal_amount NUMERIC(15,2) NOT NULL,
recovered_amount NUMERIC(15,2) NOT NULL DEFAULT 0,
balance NUMERIC(15,2) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'OUTSTANDING',
memo VARCHAR(200),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
updated_at TIMESTAMP,
CONSTRAINT chk_adv_principal_pos CHECK (principal_amount > 0),
CONSTRAINT chk_adv_balance_nn CHECK (balance >= 0),
CONSTRAINT chk_adv_status CHECK (status IN ('OUTSTANDING','PARTIAL','CLEARED','WAIVED'))
);
COMMENT ON TABLE advance_payment IS '선지급금 마스터';
COMMENT ON COLUMN advance_payment.advance_id IS 'PK';
COMMENT ON COLUMN advance_payment.agent_id IS '설계사 ID';
COMMENT ON COLUMN advance_payment.advance_month IS '선지급 발생월 YYYYMM';
COMMENT ON COLUMN advance_payment.principal_amount IS '원금';
COMMENT ON COLUMN advance_payment.recovered_amount IS '회수 누계';
COMMENT ON COLUMN advance_payment.balance IS '잔액 (principal - recovered)';
COMMENT ON COLUMN advance_payment.status IS '상태: OUTSTANDING/PARTIAL/CLEARED/WAIVED';
COMMENT ON COLUMN advance_payment.memo IS '메모';
CREATE INDEX idx_adv_agent_status ON advance_payment(agent_id, status);
CREATE INDEX idx_adv_month ON advance_payment(advance_month);
-- ============================================================
-- 2. advance_recovery_ledger (선지급 상계 이력)
-- ============================================================
CREATE TABLE advance_recovery_ledger (
recovery_id BIGSERIAL PRIMARY KEY,
advance_id BIGINT NOT NULL REFERENCES advance_payment(advance_id),
settle_month CHAR(6) NOT NULL,
recovery_amount NUMERIC(15,2) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
CONSTRAINT chk_arl_recovery_pos CHECK (recovery_amount > 0)
);
COMMENT ON TABLE advance_recovery_ledger IS '선지급 상계 이력';
COMMENT ON COLUMN advance_recovery_ledger.recovery_id IS 'PK';
COMMENT ON COLUMN advance_recovery_ledger.advance_id IS '선지급금 ID';
COMMENT ON COLUMN advance_recovery_ledger.settle_month IS '상계 정산월 YYYYMM';
COMMENT ON COLUMN advance_recovery_ledger.recovery_amount IS '당월 상계 금액';
CREATE INDEX idx_arl_advance ON advance_recovery_ledger(advance_id);
CREATE INDEX idx_arl_settle_month ON advance_recovery_ledger(settle_month);
-- ============================================================
-- 3. 메뉴 INSERT: ADVANCE_PAYMENT (GRP_SETTLE 하위 PAGE)
-- ============================================================
INSERT INTO menu (parent_menu_id, menu_code, menu_name, menu_type, menu_path, component_path, menu_level, sort_order)
SELECT p.menu_id, x.code, x.name, 'PAGE', x.path, x.cmpt, 2, x.sort
FROM menu p
JOIN (VALUES
('ADVANCE_PAYMENT', '선지급정산', '/settle/advance-payment', 'settle/AdvancePayment', 130)
) AS x(code, name, path, cmpt, sort)
ON p.menu_code = 'GRP_SETTLE'
WHERE NOT EXISTS (SELECT 1 FROM menu WHERE menu_code = x.code);
-- ============================================================
-- 4. 메뉴 권한 항목 등록 (READ/CREATE/UPDATE/DELETE/EXPORT)
-- ============================================================
INSERT INTO menu_permission (menu_id, perm_code, perm_name, sort_order)
SELECT m.menu_id, p.code, p.name, p.sort
FROM menu m
CROSS JOIN (VALUES
('READ', '조회', 10),
('CREATE', '등록', 20),
('UPDATE', '수정', 30),
('DELETE', '삭제', 40),
('EXPORT', '엑셀', 60)
) AS p(code, name, sort)
WHERE m.menu_code = 'ADVANCE_PAYMENT'
ON CONFLICT (menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 5. 역할별 권한 부여
-- ============================================================
-- SUPER_ADMIN / ADMIN — 전체 권한
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code IN ('SUPER_ADMIN','ADMIN')
AND m.menu_code = 'ADVANCE_PAYMENT'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- MANAGER — READ/CREATE/UPDATE/EXPORT
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'MANAGER'
AND mp.perm_code IN ('READ','CREATE','UPDATE','EXPORT')
AND m.menu_code = 'ADVANCE_PAYMENT'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- AGENT — READ
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'AGENT'
AND mp.perm_code = 'READ'
AND m.menu_code = 'ADVANCE_PAYMENT'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 6. 공통코드: ADVANCE_STATUS
-- ============================================================
INSERT INTO common_code_group (group_code, group_name, description, is_system, sort_order) VALUES
('ADVANCE_STATUS', '선지급상태', 'OUTSTANDING=미상환/PARTIAL=부분/CLEARED=완료/WAIVED=면제', 'Y', 470)
ON CONFLICT (group_code) DO NOTHING;
INSERT INTO common_code (group_code, code, code_name, sort_order) VALUES
('ADVANCE_STATUS', 'OUTSTANDING', '미상환', 10),
('ADVANCE_STATUS', 'PARTIAL', '부분상환', 20),
('ADVANCE_STATUS', 'CLEARED', '상환완료', 30),
('ADVANCE_STATUS', 'WAIVED', '면제', 40)
ON CONFLICT (group_code, code) DO NOTHING;
@@ -0,0 +1,116 @@
-- V118: MOD-3 환수 분할상환 (installment clawback)
-- clawback_installment_plan 테이블
-- 메뉴 CLAWBACK_INSTALLMENT (GRP_COMMISSION 하위)
-- 공통코드 CB_INSTALLMENT_STATUS
-- ============================================================
-- 1. clawback_installment_plan (환수 분할상환 계획)
-- ============================================================
CREATE TABLE clawback_installment_plan (
plan_id BIGSERIAL PRIMARY KEY,
receivable_id BIGINT NOT NULL REFERENCES clawback_receivable(receivable_id),
installment_no INT NOT NULL,
installment_amount NUMERIC(15,2) NOT NULL,
due_month CHAR(6) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'SCHEDULED',
recovered_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
updated_at TIMESTAMP,
CONSTRAINT chk_cip_amount_pos CHECK (installment_amount > 0),
CONSTRAINT chk_cip_status CHECK (status IN ('SCHEDULED','RECOVERED','CANCELLED')),
CONSTRAINT uq_cip_receivable_no UNIQUE (receivable_id, installment_no)
);
COMMENT ON TABLE clawback_installment_plan IS '환수 분할상환 계획';
COMMENT ON COLUMN clawback_installment_plan.plan_id IS 'PK';
COMMENT ON COLUMN clawback_installment_plan.receivable_id IS '환수채권 ID';
COMMENT ON COLUMN clawback_installment_plan.installment_no IS '분할 회차 번호 (1부터)';
COMMENT ON COLUMN clawback_installment_plan.installment_amount IS '회차 납부 금액';
COMMENT ON COLUMN clawback_installment_plan.due_month IS '납부 예정월 YYYYMM';
COMMENT ON COLUMN clawback_installment_plan.status IS '상태: SCHEDULED/RECOVERED/CANCELLED';
COMMENT ON COLUMN clawback_installment_plan.recovered_at IS '실제 회수 일시';
CREATE INDEX idx_cip_due_status ON clawback_installment_plan(due_month, status);
CREATE INDEX idx_cip_receivable_id ON clawback_installment_plan(receivable_id);
-- ============================================================
-- 2. 메뉴 INSERT: CLAWBACK_INSTALLMENT (GRP_COMMISSION 하위 PAGE)
-- ============================================================
INSERT INTO menu (parent_menu_id, menu_code, menu_name, menu_type, menu_path, component_path, menu_level, sort_order)
SELECT p.menu_id, x.code, x.name, 'PAGE', x.path, x.cmpt, 2, x.sort
FROM menu p
JOIN (VALUES
('CLAWBACK_INSTALLMENT', '환수분할상환', '/commission/clawback-installment', 'commission/ClawbackInstallment', 160)
) AS x(code, name, path, cmpt, sort)
ON p.menu_code = 'GRP_COMMISSION'
WHERE NOT EXISTS (SELECT 1 FROM menu WHERE menu_code = x.code);
-- ============================================================
-- 3. 메뉴 권한 항목 등록 (READ/CREATE/UPDATE/DELETE/EXPORT)
-- ============================================================
INSERT INTO menu_permission (menu_id, perm_code, perm_name, sort_order)
SELECT m.menu_id, p.code, p.name, p.sort
FROM menu m
CROSS JOIN (VALUES
('READ', '조회', 10),
('CREATE', '등록', 20),
('UPDATE', '수정', 30),
('DELETE', '삭제', 40),
('EXPORT', '엑셀', 60)
) AS p(code, name, sort)
WHERE m.menu_code = 'CLAWBACK_INSTALLMENT'
ON CONFLICT (menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 4. 역할별 권한 부여
-- ============================================================
-- SUPER_ADMIN / ADMIN — 전체 권한
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code IN ('SUPER_ADMIN','ADMIN')
AND m.menu_code = 'CLAWBACK_INSTALLMENT'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- MANAGER — READ/CREATE/UPDATE/EXPORT
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'MANAGER'
AND mp.perm_code IN ('READ','CREATE','UPDATE','EXPORT')
AND m.menu_code = 'CLAWBACK_INSTALLMENT'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- AGENT — READ
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'AGENT'
AND mp.perm_code = 'READ'
AND m.menu_code = 'CLAWBACK_INSTALLMENT'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 5. 공통코드: CB_INSTALLMENT_STATUS
-- ============================================================
INSERT INTO common_code_group (group_code, group_name, description, is_system, sort_order) VALUES
('CB_INSTALLMENT_STATUS', '환수분할상환상태', 'SCHEDULED=예정/RECOVERED=회수/CANCELLED=취소', 'Y', 480)
ON CONFLICT (group_code) DO NOTHING;
INSERT INTO common_code (group_code, code, code_name, sort_order) VALUES
('CB_INSTALLMENT_STATUS', 'SCHEDULED', '예정', 10),
('CB_INSTALLMENT_STATUS', 'RECOVERED', '회수', 20),
('CB_INSTALLMENT_STATUS', 'CANCELLED', '취소', 30)
ON CONFLICT (group_code, code) DO NOTHING;
@@ -0,0 +1,117 @@
-- V119: MOD-4 익월 부활 재지급 (lapsed reinstatement repayment)
-- lapsed_reinstatement_recovery 테이블
-- 메뉴 LAPSED_REINSTATEMENT (GRP_COMMISSION 하위)
-- 공통코드 LAPSE_RECOVERY_STATUS
-- ============================================================
-- 1. lapsed_reinstatement_recovery (익월 부활 재지급 원장)
-- ============================================================
CREATE TABLE lapsed_reinstatement_recovery (
recovery_id BIGSERIAL PRIMARY KEY,
contract_id BIGINT NOT NULL REFERENCES contract(contract_id),
agent_id BIGINT NOT NULL REFERENCES agent(agent_id),
lapsed_month CHAR(6) NOT NULL,
reinstated_month CHAR(6) NOT NULL,
withheld_amount NUMERIC(15,2) NOT NULL,
repaid_amount NUMERIC(15,2) NOT NULL DEFAULT 0,
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
updated_at TIMESTAMP,
CONSTRAINT chk_lrr_withheld_nn CHECK (withheld_amount >= 0),
CONSTRAINT chk_lrr_status CHECK (status IN ('PENDING','REPAID','CANCELLED'))
);
COMMENT ON TABLE lapsed_reinstatement_recovery IS '익월 부활 재지급 원장';
COMMENT ON COLUMN lapsed_reinstatement_recovery.recovery_id IS 'PK';
COMMENT ON COLUMN lapsed_reinstatement_recovery.contract_id IS '계약 ID';
COMMENT ON COLUMN lapsed_reinstatement_recovery.agent_id IS '설계사 ID';
COMMENT ON COLUMN lapsed_reinstatement_recovery.lapsed_month IS '실효 발생월 YYYYMM';
COMMENT ON COLUMN lapsed_reinstatement_recovery.reinstated_month IS '부활 확정월 YYYYMM';
COMMENT ON COLUMN lapsed_reinstatement_recovery.withheld_amount IS '보류된 수수료 금액';
COMMENT ON COLUMN lapsed_reinstatement_recovery.repaid_amount IS '재지급 완료 금액';
COMMENT ON COLUMN lapsed_reinstatement_recovery.status IS '상태: PENDING/REPAID/CANCELLED';
CREATE INDEX idx_lrr_agent_reinstated ON lapsed_reinstatement_recovery(agent_id, reinstated_month);
CREATE INDEX idx_lrr_contract ON lapsed_reinstatement_recovery(contract_id);
-- ============================================================
-- 2. 메뉴 INSERT: LAPSED_REINSTATEMENT (GRP_COMMISSION 하위 PAGE)
-- ============================================================
INSERT INTO menu (parent_menu_id, menu_code, menu_name, menu_type, menu_path, component_path, menu_level, sort_order)
SELECT p.menu_id, x.code, x.name, 'PAGE', x.path, x.cmpt, 2, x.sort
FROM menu p
JOIN (VALUES
('LAPSED_REINSTATEMENT', '익월부활재지급', '/commission/lapsed-reinstatement', 'commission/LapsedReinstatement', 170)
) AS x(code, name, path, cmpt, sort)
ON p.menu_code = 'GRP_COMMISSION'
WHERE NOT EXISTS (SELECT 1 FROM menu WHERE menu_code = x.code);
-- ============================================================
-- 3. 메뉴 권한 항목 등록 (READ/CREATE/UPDATE/DELETE/EXPORT)
-- ============================================================
INSERT INTO menu_permission (menu_id, perm_code, perm_name, sort_order)
SELECT m.menu_id, p.code, p.name, p.sort
FROM menu m
CROSS JOIN (VALUES
('READ', '조회', 10),
('CREATE', '등록', 20),
('UPDATE', '수정', 30),
('DELETE', '삭제', 40),
('EXPORT', '엑셀', 60)
) AS p(code, name, sort)
WHERE m.menu_code = 'LAPSED_REINSTATEMENT'
ON CONFLICT (menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 4. 역할별 권한 부여
-- ============================================================
-- SUPER_ADMIN / ADMIN — 전체 권한
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code IN ('SUPER_ADMIN','ADMIN')
AND m.menu_code = 'LAPSED_REINSTATEMENT'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- MANAGER — READ/CREATE/UPDATE/EXPORT
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'MANAGER'
AND mp.perm_code IN ('READ','CREATE','UPDATE','EXPORT')
AND m.menu_code = 'LAPSED_REINSTATEMENT'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- AGENT — READ
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'AGENT'
AND mp.perm_code = 'READ'
AND m.menu_code = 'LAPSED_REINSTATEMENT'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 5. 공통코드: LAPSE_RECOVERY_STATUS
-- ============================================================
INSERT INTO common_code_group (group_code, group_name, description, is_system, sort_order) VALUES
('LAPSE_RECOVERY_STATUS', '부활재지급상태', 'PENDING=대기/REPAID=재지급완료/CANCELLED=취소', 'Y', 490)
ON CONFLICT (group_code) DO NOTHING;
INSERT INTO common_code (group_code, code, code_name, sort_order) VALUES
('LAPSE_RECOVERY_STATUS', 'PENDING', '대기', 10),
('LAPSE_RECOVERY_STATUS', 'REPAID', '재지급완료', 20),
('LAPSE_RECOVERY_STATUS', 'CANCELLED', '취소', 30)
ON CONFLICT (group_code, code) DO NOTHING;
@@ -0,0 +1,146 @@
-- V120: MOD-5 정산대사 입수 (carrier reconciliation IN)
-- carrier_reconciliation_in / reconciliation_variance 테이블
-- 메뉴 CARRIER_RECON_IN (GRP_SETTLE 하위)
-- 공통코드 RECON_IN_STATUS
-- insurance_company PK: company_id (SERIAL, V2 확인)
-- ============================================================
-- 1. carrier_reconciliation_in (보험사 정산대사 입수 마스터)
-- ============================================================
CREATE TABLE carrier_reconciliation_in (
recon_id BIGSERIAL PRIMARY KEY,
company_id BIGINT NOT NULL REFERENCES insurance_company(company_id),
settle_month CHAR(6) NOT NULL,
file_name VARCHAR(200),
record_count INT NOT NULL DEFAULT 0,
reported_total NUMERIC(15,2) NOT NULL DEFAULT 0,
system_total NUMERIC(15,2) NOT NULL DEFAULT 0,
variance_amount NUMERIC(15,2) NOT NULL DEFAULT 0,
status VARCHAR(20) NOT NULL DEFAULT 'RECEIVED',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
updated_at TIMESTAMP,
CONSTRAINT chk_cri_status CHECK (status IN ('RECEIVED','MATCHED','VARIANCE','APPROVED')),
CONSTRAINT uq_cri_company_month UNIQUE (company_id, settle_month)
);
COMMENT ON TABLE carrier_reconciliation_in IS '보험사 정산대사 입수 마스터';
COMMENT ON COLUMN carrier_reconciliation_in.recon_id IS 'PK';
COMMENT ON COLUMN carrier_reconciliation_in.company_id IS '보험사 ID';
COMMENT ON COLUMN carrier_reconciliation_in.settle_month IS '정산월 YYYYMM';
COMMENT ON COLUMN carrier_reconciliation_in.file_name IS '입수 파일명';
COMMENT ON COLUMN carrier_reconciliation_in.record_count IS '보험사 통보 건수';
COMMENT ON COLUMN carrier_reconciliation_in.reported_total IS '보험사 통보 금액 합계';
COMMENT ON COLUMN carrier_reconciliation_in.system_total IS 'GA 시스템 집계 금액';
COMMENT ON COLUMN carrier_reconciliation_in.variance_amount IS '차이 금액 (reported - system)';
COMMENT ON COLUMN carrier_reconciliation_in.status IS '상태: RECEIVED/MATCHED/VARIANCE/APPROVED';
CREATE INDEX idx_cri_settle_month ON carrier_reconciliation_in(settle_month);
CREATE INDEX idx_cri_company_month ON carrier_reconciliation_in(company_id, settle_month);
-- ============================================================
-- 2. reconciliation_variance (대사 차이 상세)
-- ============================================================
CREATE TABLE reconciliation_variance (
variance_id BIGSERIAL PRIMARY KEY,
recon_id BIGINT NOT NULL REFERENCES carrier_reconciliation_in(recon_id),
policy_no VARCHAR(50),
expected_amount NUMERIC(15,2),
reported_amount NUMERIC(15,2),
variance_reason VARCHAR(100),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT
);
COMMENT ON TABLE reconciliation_variance IS '대사 차이 상세';
COMMENT ON COLUMN reconciliation_variance.variance_id IS 'PK';
COMMENT ON COLUMN reconciliation_variance.recon_id IS '대사 입수 ID';
COMMENT ON COLUMN reconciliation_variance.policy_no IS '증권번호';
COMMENT ON COLUMN reconciliation_variance.expected_amount IS 'GA 시스템 예상 금액';
COMMENT ON COLUMN reconciliation_variance.reported_amount IS '보험사 통보 금액';
COMMENT ON COLUMN reconciliation_variance.variance_reason IS '차이 사유';
CREATE INDEX idx_rv_recon_id ON reconciliation_variance(recon_id);
-- ============================================================
-- 3. 메뉴 INSERT: CARRIER_RECON_IN (GRP_SETTLE 하위 PAGE)
-- ============================================================
INSERT INTO menu (parent_menu_id, menu_code, menu_name, menu_type, menu_path, component_path, menu_level, sort_order)
SELECT p.menu_id, x.code, x.name, 'PAGE', x.path, x.cmpt, 2, x.sort
FROM menu p
JOIN (VALUES
('CARRIER_RECON_IN', '정산대사입수', '/settle/carrier-recon-in', 'settle/CarrierReconIn', 140)
) AS x(code, name, path, cmpt, sort)
ON p.menu_code = 'GRP_SETTLE'
WHERE NOT EXISTS (SELECT 1 FROM menu WHERE menu_code = x.code);
-- ============================================================
-- 4. 메뉴 권한 항목 등록 (READ/CREATE/UPDATE/DELETE/EXPORT)
-- ============================================================
INSERT INTO menu_permission (menu_id, perm_code, perm_name, sort_order)
SELECT m.menu_id, p.code, p.name, p.sort
FROM menu m
CROSS JOIN (VALUES
('READ', '조회', 10),
('CREATE', '등록', 20),
('UPDATE', '수정', 30),
('DELETE', '삭제', 40),
('EXPORT', '엑셀', 60)
) AS p(code, name, sort)
WHERE m.menu_code = 'CARRIER_RECON_IN'
ON CONFLICT (menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 5. 역할별 권한 부여
-- ============================================================
-- SUPER_ADMIN / ADMIN — 전체 권한
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code IN ('SUPER_ADMIN','ADMIN')
AND m.menu_code = 'CARRIER_RECON_IN'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- MANAGER — READ/CREATE/UPDATE/EXPORT
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'MANAGER'
AND mp.perm_code IN ('READ','CREATE','UPDATE','EXPORT')
AND m.menu_code = 'CARRIER_RECON_IN'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- AGENT — READ
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'AGENT'
AND mp.perm_code = 'READ'
AND m.menu_code = 'CARRIER_RECON_IN'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 6. 공통코드: RECON_IN_STATUS
-- ============================================================
INSERT INTO common_code_group (group_code, group_name, description, is_system, sort_order) VALUES
('RECON_IN_STATUS', '정산대사상태', 'RECEIVED=입수/MATCHED=일치/VARIANCE=차이/APPROVED=승인', 'Y', 500)
ON CONFLICT (group_code) DO NOTHING;
INSERT INTO common_code (group_code, code, code_name, sort_order) VALUES
('RECON_IN_STATUS', 'RECEIVED', '입수', 10),
('RECON_IN_STATUS', 'MATCHED', '일치', 20),
('RECON_IN_STATUS', 'VARIANCE', '차이', 30),
('RECON_IN_STATUS', 'APPROVED', '승인', 40)
ON CONFLICT (group_code, code) DO NOTHING;
@@ -0,0 +1,102 @@
-- V121: MOD-6 환수 시효/소멸 (statute of limitations)
-- statute_config 테이블 + 시드 1행
-- 메뉴 STATUTE_CONFIG (GRP_COMMISSION 하위)
-- clawback_receivable.status CHECK: WRITTEN_OFF 이미 존재 (V111 확인) → 제약 변경 불필요
-- ============================================================
-- 1. statute_config (환수 시효 설정)
-- ============================================================
CREATE TABLE statute_config (
config_id BIGSERIAL PRIMARY KEY,
entity_type VARCHAR(30) NOT NULL,
expiration_months INT NOT NULL,
is_active CHAR(1) NOT NULL DEFAULT 'Y',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
updated_at TIMESTAMP,
CONSTRAINT chk_sc_entity_type CHECK (entity_type IN ('CLAWBACK_RECEIVABLE')),
CONSTRAINT chk_sc_expiration_pos CHECK (expiration_months > 0),
CONSTRAINT chk_sc_is_active CHECK (is_active IN ('Y','N')),
CONSTRAINT uq_sc_entity_type UNIQUE (entity_type)
);
COMMENT ON TABLE statute_config IS '환수 시효 설정';
COMMENT ON COLUMN statute_config.config_id IS 'PK';
COMMENT ON COLUMN statute_config.entity_type IS '대상 엔티티 타입 (CLAWBACK_RECEIVABLE)';
COMMENT ON COLUMN statute_config.expiration_months IS '시효 만료 개월수';
COMMENT ON COLUMN statute_config.is_active IS '활성 여부 (Y/N)';
-- ============================================================
-- 2. 시드 데이터: CLAWBACK_RECEIVABLE 36개월
-- ============================================================
INSERT INTO statute_config (entity_type, expiration_months, is_active)
VALUES ('CLAWBACK_RECEIVABLE', 36, 'Y')
ON CONFLICT (entity_type) DO NOTHING;
-- ============================================================
-- 3. 메뉴 INSERT: STATUTE_CONFIG (GRP_COMMISSION 하위 PAGE)
-- ============================================================
INSERT INTO menu (parent_menu_id, menu_code, menu_name, menu_type, menu_path, component_path, menu_level, sort_order)
SELECT p.menu_id, x.code, x.name, 'PAGE', x.path, x.cmpt, 2, x.sort
FROM menu p
JOIN (VALUES
('STATUTE_CONFIG', '환수시효관리', '/commission/statute', 'commission/StatuteConfig', 180)
) AS x(code, name, path, cmpt, sort)
ON p.menu_code = 'GRP_COMMISSION'
WHERE NOT EXISTS (SELECT 1 FROM menu WHERE menu_code = x.code);
-- ============================================================
-- 4. 메뉴 권한 항목 등록 (READ/CREATE/UPDATE/DELETE/EXPORT)
-- ============================================================
INSERT INTO menu_permission (menu_id, perm_code, perm_name, sort_order)
SELECT m.menu_id, p.code, p.name, p.sort
FROM menu m
CROSS JOIN (VALUES
('READ', '조회', 10),
('CREATE', '등록', 20),
('UPDATE', '수정', 30),
('DELETE', '삭제', 40),
('EXPORT', '엑셀', 60)
) AS p(code, name, sort)
WHERE m.menu_code = 'STATUTE_CONFIG'
ON CONFLICT (menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 5. 역할별 권한 부여
-- ============================================================
-- SUPER_ADMIN / ADMIN — 전체 권한
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code IN ('SUPER_ADMIN','ADMIN')
AND m.menu_code = 'STATUTE_CONFIG'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- MANAGER — READ/CREATE/UPDATE/EXPORT
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'MANAGER'
AND mp.perm_code IN ('READ','CREATE','UPDATE','EXPORT')
AND m.menu_code = 'STATUTE_CONFIG'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- AGENT — READ (시효 설정은 읽기만)
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'AGENT'
AND mp.perm_code = 'READ'
AND m.menu_code = 'STATUTE_CONFIG'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
@@ -0,0 +1,123 @@
-- V122: MOD-7 환수 감면/면제 (chargeback waiver/reduction)
-- chargeback_waiver_request 테이블
-- 메뉴 CHARGEBACK_WAIVER (GRP_COMMISSION 하위)
-- 공통코드 WAIVER_STATUS
-- ============================================================
-- 1. chargeback_waiver_request (환수 감면 신청)
-- ============================================================
CREATE TABLE chargeback_waiver_request (
request_id BIGSERIAL PRIMARY KEY,
contract_id BIGINT NOT NULL REFERENCES contract(contract_id),
agent_id BIGINT NOT NULL REFERENCES agent(agent_id),
settle_month CHAR(6) NOT NULL,
original_chargeback NUMERIC(15,2) NOT NULL,
waiver_amount NUMERIC(15,2) NOT NULL,
reason VARCHAR(200),
approval_status VARCHAR(20) NOT NULL DEFAULT 'REQUESTED',
approver_id BIGINT,
approved_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
updated_at TIMESTAMP,
CONSTRAINT chk_cwr_original_nn CHECK (original_chargeback >= 0),
CONSTRAINT chk_cwr_waiver_nn CHECK (waiver_amount >= 0),
CONSTRAINT chk_cwr_waiver_lte_orig CHECK (waiver_amount <= original_chargeback),
CONSTRAINT chk_cwr_status CHECK (approval_status IN ('REQUESTED','APPROVED','REJECTED'))
);
COMMENT ON TABLE chargeback_waiver_request IS '환수 감면 신청';
COMMENT ON COLUMN chargeback_waiver_request.request_id IS 'PK';
COMMENT ON COLUMN chargeback_waiver_request.contract_id IS '계약 ID';
COMMENT ON COLUMN chargeback_waiver_request.agent_id IS '설계사 ID';
COMMENT ON COLUMN chargeback_waiver_request.settle_month IS '정산월 YYYYMM';
COMMENT ON COLUMN chargeback_waiver_request.original_chargeback IS '원래 환수액';
COMMENT ON COLUMN chargeback_waiver_request.waiver_amount IS '감면 신청 금액 (<=original_chargeback)';
COMMENT ON COLUMN chargeback_waiver_request.reason IS '감면 사유';
COMMENT ON COLUMN chargeback_waiver_request.approval_status IS '승인 상태: REQUESTED/APPROVED/REJECTED';
COMMENT ON COLUMN chargeback_waiver_request.approver_id IS '승인자 사용자 ID';
COMMENT ON COLUMN chargeback_waiver_request.approved_at IS '승인/반려 일시';
CREATE INDEX idx_cwr_agent_month ON chargeback_waiver_request(agent_id, settle_month);
CREATE INDEX idx_cwr_status ON chargeback_waiver_request(approval_status);
-- ============================================================
-- 2. 메뉴 INSERT: CHARGEBACK_WAIVER (GRP_COMMISSION 하위 PAGE)
-- ============================================================
INSERT INTO menu (parent_menu_id, menu_code, menu_name, menu_type, menu_path, component_path, menu_level, sort_order)
SELECT p.menu_id, x.code, x.name, 'PAGE', x.path, x.cmpt, 2, x.sort
FROM menu p
JOIN (VALUES
('CHARGEBACK_WAIVER', '환수감면신청', '/commission/chargeback-waiver', 'commission/ChargebackWaiver', 190)
) AS x(code, name, path, cmpt, sort)
ON p.menu_code = 'GRP_COMMISSION'
WHERE NOT EXISTS (SELECT 1 FROM menu WHERE menu_code = x.code);
-- ============================================================
-- 3. 메뉴 권한 항목 등록 (READ/CREATE/UPDATE/DELETE/EXPORT)
-- ============================================================
INSERT INTO menu_permission (menu_id, perm_code, perm_name, sort_order)
SELECT m.menu_id, p.code, p.name, p.sort
FROM menu m
CROSS JOIN (VALUES
('READ', '조회', 10),
('CREATE', '등록', 20),
('UPDATE', '수정', 30),
('DELETE', '삭제', 40),
('EXPORT', '엑셀', 60)
) AS p(code, name, sort)
WHERE m.menu_code = 'CHARGEBACK_WAIVER'
ON CONFLICT (menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 4. 역할별 권한 부여
-- ============================================================
-- SUPER_ADMIN / ADMIN — 전체 권한
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code IN ('SUPER_ADMIN','ADMIN')
AND m.menu_code = 'CHARGEBACK_WAIVER'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- MANAGER — READ/CREATE/UPDATE/EXPORT
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'MANAGER'
AND mp.perm_code IN ('READ','CREATE','UPDATE','EXPORT')
AND m.menu_code = 'CHARGEBACK_WAIVER'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- AGENT — READ (본인 감면 신청 현황 조회)
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
SELECT r.role_id, mp.menu_id, mp.perm_code, 'Y'
FROM role r
CROSS JOIN menu_permission mp
JOIN menu m ON m.menu_id = mp.menu_id
WHERE r.role_code = 'AGENT'
AND mp.perm_code = 'READ'
AND m.menu_code = 'CHARGEBACK_WAIVER'
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
-- ============================================================
-- 5. 공통코드: WAIVER_STATUS
-- ============================================================
INSERT INTO common_code_group (group_code, group_name, description, is_system, sort_order) VALUES
('WAIVER_STATUS', '환수감면상태', 'REQUESTED=신청/APPROVED=승인/REJECTED=반려', 'Y', 510)
ON CONFLICT (group_code) DO NOTHING;
INSERT INTO common_code (group_code, code, code_name, sort_order) VALUES
('WAIVER_STATUS', 'REQUESTED', '신청', 10),
('WAIVER_STATUS', 'APPROVED', '승인', 20),
('WAIVER_STATUS', 'REJECTED', '반려', 30)
ON CONFLICT (group_code, code) DO NOTHING;
@@ -0,0 +1,51 @@
-- V123: MOD-8 채널별 수수료 차등 (channel-specific commission)
-- contract.sales_channel 컬럼 추가
-- commission_rate.sales_channel 컬럼 추가 (V3 확인: product_id 기반 룰 테이블)
-- payout_rule.sales_channel 컬럼 추가 (V3 확인: grade_id 기반 룰 테이블)
-- 공통코드 SALES_CHANNEL
-- 하위호환: sales_channel NULL = DIRECT 취급, NULL 룰이 폴백
-- ============================================================
-- 1. contract 테이블에 판매채널 컬럼 추가
-- ============================================================
ALTER TABLE contract ADD COLUMN IF NOT EXISTS sales_channel VARCHAR(20);
COMMENT ON COLUMN contract.sales_channel IS '판매채널 (NULL=DIRECT 취급): DIRECT/BANCASSURANCE/HOME_SHOPPING/PARTNERSHIP';
-- ============================================================
-- 2. commission_rate 테이블에 sales_channel 컬럼 추가
-- NULL = 모든 채널 적용 (폴백 룰)
-- ============================================================
ALTER TABLE commission_rate ADD COLUMN IF NOT EXISTS sales_channel VARCHAR(20);
COMMENT ON COLUMN commission_rate.sales_channel IS '판매채널 조건 (NULL=모든 채널 폴백)';
CREATE INDEX idx_cr_product_channel ON commission_rate(product_id, sales_channel, effective_from);
-- ============================================================
-- 3. payout_rule 테이블에 sales_channel 컬럼 추가
-- NULL = 모든 채널 적용 (폴백 룰)
-- ============================================================
ALTER TABLE payout_rule ADD COLUMN IF NOT EXISTS sales_channel VARCHAR(20);
COMMENT ON COLUMN payout_rule.sales_channel IS '판매채널 조건 (NULL=모든 채널 폴백)';
CREATE INDEX idx_pr_grade_channel ON payout_rule(grade_id, insurance_type, sales_channel, effective_from);
-- ============================================================
-- 4. 공통코드: SALES_CHANNEL
-- ============================================================
INSERT INTO common_code_group (group_code, group_name, description, is_system, sort_order) VALUES
('SALES_CHANNEL', '판매채널', 'DIRECT=직판/BANCASSURANCE=방카/HOME_SHOPPING=홈쇼핑/PARTNERSHIP=제휴', 'Y', 520)
ON CONFLICT (group_code) DO NOTHING;
INSERT INTO common_code (group_code, code, code_name, sort_order) VALUES
('SALES_CHANNEL', 'DIRECT', '직판', 10),
('SALES_CHANNEL', 'BANCASSURANCE', '방카', 20),
('SALES_CHANNEL', 'HOME_SHOPPING', '홈쇼핑', 30),
('SALES_CHANNEL', 'PARTNERSHIP', '제휴', 40)
ON CONFLICT (group_code, code) DO NOTHING;