Files
sam-docs/rules/client-policy.md
hskwon 914667738b docs: clients is_active Boolean 변경 반영
- client-policy.md: TINYINT(1), true/false 상태값 반영, Hard Delete 명시
- client-api-analysis.md: 스키마/타입 Boolean 업데이트, Soft Delete → Hard Delete 수정
2025-12-08 20:26:01 +09:00

499 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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