Files
sam-docs/rules/client-policy.md

499 lines
19 KiB
Markdown
Raw Permalink Normal View History

# 거래처 관리 정책 (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