From 914667738b8e389182954d909c87f9ebce294b74 Mon Sep 17 00:00:00 2001 From: hskwon Date: Mon, 8 Dec 2025 20:26:01 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20clients=20is=5Factive=20Boolean=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - client-policy.md: TINYINT(1), true/false 상태값 반영, Hard Delete 명시 - client-api-analysis.md: 스키마/타입 Boolean 업데이트, Soft Delete → Hard Delete 수정 --- front/[API-2025-12-04] client-api-analysis.md | 8 +- rules/client-policy.md | 498 ++++++++++++++++++ 2 files changed, 502 insertions(+), 4 deletions(-) create mode 100644 rules/client-policy.md diff --git a/front/[API-2025-12-04] client-api-analysis.md b/front/[API-2025-12-04] client-api-analysis.md index 5a2a801..91fad47 100644 --- a/front/[API-2025-12-04] client-api-analysis.md +++ b/front/[API-2025-12-04] client-api-analysis.md @@ -97,7 +97,7 @@ CREATE TABLE clients ( -- 기타 memo TEXT NULL, - is_active CHAR(1) DEFAULT 'Y', -- 'Y' 또는 'N' + is_active TINYINT(1) DEFAULT 1, -- 1=활성, 0=비활성 -- 감사 컬럼 created_at TIMESTAMP, @@ -173,7 +173,7 @@ CREATE TABLE client_groups ( "tax_agreement": false, "bad_debt": false, "memo": null, - "is_active": "Y", + "is_active": true, "client_group": { "id": 1, "name": "VIP" } } ], @@ -234,7 +234,7 @@ CREATE TABLE client_groups ( ### 3.5 거래처 삭제 `DELETE /api/v1/clients/{id}` ✅ -Soft Delete 적용 +Hard Delete 적용 (연관 주문이 없는 경우만 삭제 가능) ### 3.6 활성/비활성 토글 `PATCH /api/v1/clients/{id}/toggle` ✅ @@ -359,7 +359,7 @@ export interface ClientApiResponse { bad_debt_end_date?: string | null; bad_debt_progress?: BadDebtProgress; memo?: string | null; - is_active: "Y" | "N"; + is_active: boolean; created_at: string; updated_at: string; } diff --git a/rules/client-policy.md b/rules/client-policy.md new file mode 100644 index 0000000..ce46882 --- /dev/null +++ b/rules/client-policy.md @@ -0,0 +1,498 @@ +# 거래처 관리 정책 (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