# 거래처 관리 정책 (Client Management Policy) > **작성일**: 2025-12-08 > **상태**: 설계 확정 > **관련 요청**: `docs/front/[API-2025-12-04] client-api-analysis.md` --- ## 1. 개요 ### 1.1 목적 - **거래처 마스터** 통합 관리 (매입처/매출처/매입매출) - **고객그룹** 기반 단가 정책 연계 - 세금계산서 합의 및 **불량 채권** 관리 - 주문/발주 연동을 위한 거래처 기반 시스템 ### 1.2 핵심 원칙 | 원칙 | 설명 | |------|------| | **거래처 유형 분류** | 매입, 매출, 매입매출로 구분 관리 | | **코드 유일성** | tenant 내 client_code 중복 불가 | | **그룹 기반 단가** | 고객그룹별 차등 판매가 적용 | | **연관 데이터 보호** | 주문 존재 시 삭제 불가 | --- ## 2. 테이블 구조 ### 2.1 ERD 개요 ``` ┌─────────────────┐ ┌─────────────────────┐ │ client_groups │──────<│ clients │ │ (고객 그룹) │ │ (거래처) │ └────────┬────────┘ └─────────┬───────────┘ │ │ │ price_rate │ client_id │ │ ▼ ▼ ┌─────────────────┐ ┌─────────────────────┐ │ prices │ │ orders │ │ (차등 단가) │ │ (주문) │ └─────────────────┘ └─────────────────────┘ ``` ### 2.2 clients 테이블 (거래처 마스터) ```sql CREATE TABLE clients ( id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID', client_group_id BIGINT UNSIGNED NULL COMMENT '고객그룹 ID', -- 기본 정보 -- client_code VARCHAR(50) NOT NULL COMMENT '거래처 코드', name VARCHAR(100) NOT NULL COMMENT '거래처명', client_type VARCHAR(20) NULL COMMENT '거래처 유형 (매입/매출/매입매출)', is_active TINYINT(1) DEFAULT 1 COMMENT '활성여부(1=활성,0=비활성)', -- 사업자 정보 -- business_no VARCHAR(20) NULL COMMENT '사업자등록번호', business_type VARCHAR(50) NULL COMMENT '업태', business_item VARCHAR(100) NULL COMMENT '업종', -- 연락처 정보 -- contact_person VARCHAR(50) NULL COMMENT '담당자명', phone VARCHAR(30) NULL COMMENT '전화번호', mobile VARCHAR(20) NULL COMMENT '휴대폰', fax VARCHAR(20) NULL COMMENT '팩스', email VARCHAR(80) NULL COMMENT '이메일', address VARCHAR(255) NULL COMMENT '주소', -- 담당자 정보 -- manager_name VARCHAR(50) NULL COMMENT '당사 담당자명', manager_tel VARCHAR(20) NULL COMMENT '당사 담당자 연락처', system_manager VARCHAR(50) NULL COMMENT '시스템 담당자', -- 계정 정보 -- account_id VARCHAR(50) NULL COMMENT '계정 ID', account_password VARCHAR(255) NULL COMMENT '계정 비밀번호 (hidden)', -- 발주/결제 설정 -- purchase_payment_day VARCHAR(20) NULL COMMENT '매입 결제일', sales_payment_day VARCHAR(20) NULL COMMENT '매출 결제일', -- 세금계산서 합의 -- tax_agreement BOOLEAN DEFAULT FALSE COMMENT '세금계산서 합의 여부', tax_amount DECIMAL(15,2) NULL COMMENT '합의 금액', tax_start_date DATE NULL COMMENT '합의 시작일', tax_end_date DATE NULL COMMENT '합의 종료일', -- 불량 채권 관리 -- bad_debt BOOLEAN DEFAULT FALSE COMMENT '불량 채권 여부', bad_debt_amount DECIMAL(15,2) NULL COMMENT '불량 채권 금액', bad_debt_receive_date DATE NULL COMMENT '채권 발생일', bad_debt_end_date DATE NULL COMMENT '채권 종료일', bad_debt_progress VARCHAR(20) NULL COMMENT '진행 상태', -- 기타 -- memo TEXT NULL COMMENT '메모', -- 감사 컬럼 -- created_by BIGINT UNSIGNED NULL COMMENT '생성자 ID', updated_by BIGINT UNSIGNED NULL COMMENT '수정자 ID', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- 인덱스 -- INDEX idx_clients_tenant (tenant_id), INDEX idx_clients_code (tenant_id, client_code), INDEX idx_clients_group (tenant_id, client_group_id), INDEX idx_clients_type (tenant_id, client_type), INDEX idx_clients_active (tenant_id, is_active), UNIQUE idx_clients_unique (tenant_id, client_code), FOREIGN KEY (client_group_id) REFERENCES client_groups(id) ON DELETE SET NULL ) COMMENT='거래처 마스터'; ``` ### 2.3 client_groups 테이블 (고객 그룹) ```sql CREATE TABLE client_groups ( id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID', -- 그룹 정보 -- group_code VARCHAR(50) NOT NULL COMMENT '그룹 코드', group_name VARCHAR(100) NOT NULL COMMENT '그룹명', price_rate DECIMAL(5,4) DEFAULT 1.0000 COMMENT '단가 비율 (1.0 = 100%)', is_active BOOLEAN DEFAULT TRUE COMMENT '활성 상태', -- 감사 컬럼 -- created_by BIGINT UNSIGNED NULL COMMENT '생성자 ID', updated_by BIGINT UNSIGNED NULL COMMENT '수정자 ID', deleted_by BIGINT UNSIGNED NULL COMMENT '삭제자 ID', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted_at TIMESTAMP NULL COMMENT 'Soft Delete', -- 인덱스 -- INDEX idx_groups_tenant (tenant_id), INDEX idx_groups_code (tenant_id, group_code), INDEX idx_groups_active (tenant_id, is_active), UNIQUE idx_groups_unique (tenant_id, group_code, deleted_at) ) COMMENT='고객 그룹'; ``` --- ## 3. 거래처 유형 정책 ### 3.1 거래처 유형 (client_type) | 유형 | 설명 | 용도 | |------|------|------| | **매입** | 자재/상품 구매처 | 발주, 입고, 매입 관리 | | **매출** | 제품/상품 판매처 | 주문, 출고, 매출 관리 | | **매입매출** | 양방향 거래처 | 매입+매출 모두 가능 | ### 3.2 유형별 업무 흐름 ``` ┌───────────────────────────────────────────────────────────────┐ │ 매입 거래처 │ ├───────────────────────────────────────────────────────────────┤ │ 발주요청 → 발주서 → 입고 → 수입검사 → 매입확정 → 대금지급 │ └───────────────────────────────────────────────────────────────┘ ┌───────────────────────────────────────────────────────────────┐ │ 매출 거래처 │ ├───────────────────────────────────────────────────────────────┤ │ 견적 → 수주 → 생산 → 출고 → 매출확정 → 대금수금 │ └───────────────────────────────────────────────────────────────┘ ``` --- ## 4. 고객그룹과 단가 연계 ### 4.1 단가 적용 우선순위 ``` ┌─────────────────────────────────────────────────────────┐ │ 판매가 조회 로직 │ ├─────────────────────────────────────────────────────────┤ │ 1순위: 고객그룹별 특별 단가 │ │ prices WHERE client_group_id = [거래처.그룹ID] │ │ │ │ 2순위: 기본 판매가 │ │ prices WHERE client_group_id IS NULL │ └─────────────────────────────────────────────────────────┘ ``` ### 4.2 단가 비율 적용 (price_rate) ``` 최종 판매가 = 기본 판매가 × price_rate 예시: - 기본 판매가: 10,000원 - VIP 그룹 price_rate: 0.90 (10% 할인) - 최종 판매가: 10,000 × 0.90 = 9,000원 ``` ### 4.3 그룹 활용 예시 | 그룹 코드 | 그룹명 | price_rate | 설명 | |----------|--------|------------|------| | `DEFAULT` | 일반 고객 | 1.0000 | 정가 적용 | | `VIP` | VIP 고객 | 0.9000 | 10% 할인 | | `WHOLESALE` | 도매 거래처 | 0.8000 | 20% 할인 | | `PARTNER` | 협력 업체 | 0.8500 | 15% 할인 | --- ## 5. 세금계산서 합의 관리 ### 5.1 합의 정보 구조 | 필드 | 설명 | 용도 | |------|------|------| | `tax_agreement` | 합의 여부 | 세금계산서 발행 방식 결정 | | `tax_amount` | 합의 금액 | 월정액 또는 고정 금액 | | `tax_start_date` | 시작일 | 합의 유효 기간 시작 | | `tax_end_date` | 종료일 | 합의 유효 기간 종료 | ### 5.2 합의 유형 ``` ┌─────────────────────────────────────────────────────────┐ │ tax_agreement = FALSE │ │ → 실제 거래 금액으로 세금계산서 발행 │ └─────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────┐ │ tax_agreement = TRUE │ │ → 합의 금액(tax_amount)으로 세금계산서 발행 │ │ → 유효 기간: tax_start_date ~ tax_end_date │ └─────────────────────────────────────────────────────────┘ ``` --- ## 6. 불량 채권 관리 ### 6.1 불량 채권 정보 구조 | 필드 | 설명 | |------|------| | `bad_debt` | 불량 채권 여부 | | `bad_debt_amount` | 불량 채권 금액 | | `bad_debt_receive_date` | 채권 발생일 | | `bad_debt_end_date` | 채권 종료(해결)일 | | `bad_debt_progress` | 진행 상태 | ### 6.2 진행 상태 (bad_debt_progress) ``` ┌──────────┐ 협의 ┌──────────┐ 법적조치 ┌──────────┐ │ 협의중 │ ──────────> │ 소송중 │ ─────────── │ 회수완료 │ └──────────┘ └────┬─────┘ └──────────┘ │ │ 회수 불가 ▼ ┌──────────┐ │ 대손처리 │ └──────────┘ ``` ### 6.3 불량 채권 상태값 | 상태 | 설명 | 후속 조치 | |------|------|----------| | `협의중` | 채권 회수 협의 진행 | 정기 연락, 분할 상환 협의 | | `소송중` | 법적 절차 진행 | 법무팀 연계, 소송 진행 | | `회수완료` | 채권 전액 회수 | bad_debt_end_date 기록 | | `대손처리` | 회수 불가 판정 | 회계 처리, 거래 중단 검토 | --- ## 7. 상태 관리 ### 7.1 활성 상태 (is_active) ``` ┌──────────────┐ 비활성화 ┌──────────────┐ │ true (활성) │ ───────────── │ false (비활성)│ │ 거래가능 │ <───────── │ 거래중단 │ └──────────────┘ 재활성화 └──────────────┘ ``` ### 7.2 상태별 제약 | 상태 | 주문 생성 | 발주 생성 | 조회 | 수정 | |------|----------|----------|------|------| | `true` (활성) | ✅ | ✅ | ✅ | ✅ | | `false` (비활성) | ❌ | ❌ | ✅ | ✅ | ### 7.3 비활성화 고려 사항 - **불량 채권 거래처**: bad_debt = true 시 비활성화 검토 - **장기 미거래**: 12개월 이상 거래 없을 시 비활성화 검토 - **사업자 폐업**: 사업자등록 말소 확인 시 비활성화 --- ## 8. API 엔드포인트 ### 8.1 엔드포인트 목록 | Method | Endpoint | 설명 | 우선순위 | |--------|----------|------|---------| | GET | `/api/v1/clients` | 거래처 목록 조회 | 🔴 필수 | | GET | `/api/v1/clients/{id}` | 거래처 상세 조회 | 🔴 필수 | | POST | `/api/v1/clients` | 거래처 등록 | 🔴 필수 | | PUT | `/api/v1/clients/{id}` | 거래처 수정 | 🔴 필수 | | DELETE | `/api/v1/clients/{id}` | 거래처 삭제 | 🔴 필수 | | POST | `/api/v1/clients/{id}/toggle` | 활성/비활성 토글 | 🟡 중요 | | GET | `/api/v1/client-groups` | 고객그룹 목록 | 🟡 중요 | | POST | `/api/v1/client-groups` | 고객그룹 등록 | 🟢 권장 | ### 8.2 목록 조회 API ``` GET /api/v1/clients?page=1&size=20&q=검색어&only_active=true ``` **Query Parameters:** | 파라미터 | 타입 | 설명 | |---------|------|------| | `page` | int | 페이지 번호 (기본: 1) | | `size` | int | 페이지당 개수 (기본: 20) | | `q` | string | 검색어 (거래처명, 코드, 담당자) | | `only_active` | bool | 활성 거래처만 조회 | **Response:** ```json { "success": true, "message": "message.fetched", "data": { "current_page": 1, "data": [ { "id": 1, "client_code": "C001", "name": "ABC 상사", "client_type": "매입매출", "business_no": "123-45-67890", "contact_person": "홍길동", "is_active": true, "client_group": { "id": 1, "group_name": "VIP" } } ], "total": 100 } } ``` --- ## 9. 비즈니스 규칙 ### 9.1 검증 규칙 | 규칙 | 설명 | |------|------| | **R1** | client_code는 tenant 내 유일해야 함 | | **R2** | client_code, name은 필수 값 | | **R3** | client_type은 '매입', '매출', '매입매출' 중 하나 | | **R4** | 주문이 존재하는 거래처는 삭제 불가 | | **R5** | email 형식 검증 | | **R6** | bad_debt_progress는 지정된 값만 허용 | | **R7** | tax_start_date ≤ tax_end_date | ### 9.2 코드 중복 처리 ``` 신규 등록 시: └─ tenant_id + client_code 조합 중복 검사 └─ 중복 시 error.duplicate_code 반환 수정 시: └─ client_code 변경 시에만 중복 검사 수행 └─ 자기 자신 제외하고 검사 ``` ### 9.3 삭제 정책 | 테이블 | 삭제 방식 | 설명 | |--------|----------|------| | `clients` | **Hard Delete** | 연관 주문이 없는 경우만 삭제 가능 | | `client_groups` | **Soft Delete** | `deleted_at` 컬럼 사용 | - **연관 데이터 보호**: orders 존재 시 `error.has_orders` 반환 - **주문 있는 거래처**: 비활성화(`is_active = false`)로 처리 권장 ### 9.4 비밀번호 보호 ```php protected $hidden = ['account_password']; ``` - `account_password`는 API 응답에서 자동 제외 - 저장 시 평문 저장 (필요시 암호화 적용 검토) --- ## 10. 필드 분류 ### 10.1 기본 정보 (4개) | 필드 | 타입 | 필수 | 설명 | |------|------|------|------| | `client_code` | string(50) | ✅ | 거래처 코드 | | `name` | string(100) | ✅ | 거래처명 | | `client_type` | enum | ❌ | 매입/매출/매입매출 | | `is_active` | boolean | ❌ | 활성 상태 (기본: true) | ### 10.2 사업자 정보 (3개) | 필드 | 타입 | 필수 | 설명 | |------|------|------|------| | `business_no` | string(20) | ❌ | 사업자등록번호 | | `business_type` | string(50) | ❌ | 업태 | | `business_item` | string(100) | ❌ | 업종 | ### 10.3 연락처 정보 (6개) | 필드 | 타입 | 필수 | 설명 | |------|------|------|------| | `contact_person` | string(50) | ❌ | 담당자명 | | `phone` | string(30) | ❌ | 전화번호 | | `mobile` | string(20) | ❌ | 휴대폰 | | `fax` | string(20) | ❌ | 팩스 | | `email` | string(80) | ❌ | 이메일 | | `address` | string(255) | ❌ | 주소 | ### 10.4 담당자 정보 (3개) | 필드 | 타입 | 필수 | 설명 | |------|------|------|------| | `manager_name` | string(50) | ❌ | 당사 담당자명 | | `manager_tel` | string(20) | ❌ | 당사 담당자 연락처 | | `system_manager` | string(50) | ❌ | 시스템 담당자 | ### 10.5 계정/발주 정보 (4개) | 필드 | 타입 | 필수 | 설명 | |------|------|------|------| | `account_id` | string(50) | ❌ | 계정 ID | | `account_password` | string(255) | ❌ | 계정 비밀번호 (hidden) | | `purchase_payment_day` | string(20) | ❌ | 매입 결제일 | | `sales_payment_day` | string(20) | ❌ | 매출 결제일 | ### 10.6 세금계산서 합의 (4개) | 필드 | 타입 | 필수 | 설명 | |------|------|------|------| | `tax_agreement` | boolean | ❌ | 합의 여부 | | `tax_amount` | decimal(15,2) | ❌ | 합의 금액 | | `tax_start_date` | date | ❌ | 합의 시작일 | | `tax_end_date` | date | ❌ | 합의 종료일 | ### 10.7 불량 채권 (5개) | 필드 | 타입 | 필수 | 설명 | |------|------|------|------| | `bad_debt` | boolean | ❌ | 불량 채권 여부 | | `bad_debt_amount` | decimal(15,2) | ❌ | 불량 채권 금액 | | `bad_debt_receive_date` | date | ❌ | 채권 발생일 | | `bad_debt_end_date` | date | ❌ | 채권 종료일 | | `bad_debt_progress` | enum | ❌ | 진행 상태 | ### 10.8 기타 (2개) | 필드 | 타입 | 필수 | 설명 | |------|------|------|------| | `client_group_id` | bigint | ❌ | 고객그룹 ID | | `memo` | text | ❌ | 메모 | --- ## 11. 관련 문서 - [단가 정책](./pricing-policy.md) - 고객그룹별 단가 적용 - [API 개발 규칙](../standards/api-rules.md) - [프론트엔드 요청서](../front/[API-2025-12-04]%20client-api-analysis.md) --- **최종 업데이트**: 2025-12-08