Files
sam-docs/dev/dev_plans/dummy-data-seeding-plan.md
권혁성 db63fcff85 refactor: [docs] 팀별 폴더 구조 재편 (공유/개발/프론트/기획)
- 개발팀 전용 폴더 dev/ 생성 (standards, guides, quickstart, changes, deploys, data, history, dev_plans 이동)
- 프론트엔드 전용 폴더 frontend/ 생성 (api/ → frontend/api-specs/)
- 기획팀 폴더 requests/ 생성
- plans/ → dev/dev_plans/ 이름 변경
- README.md 신규 (사람용 안내), INDEX.md 재작성 (Claude Code용)
- resources.md 신규 (노션 링크용, assets/brochure 이관 예정)
- CURRENT_WORKS.md 삭제, TODO.md → dev/ 이동
- 전체 참조 경로 업데이트

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:46:03 +09:00

1574 lines
76 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.

# 더미 데이터 시딩 계획
> **작성일**: 2025-12-23
> **목적**: React API 연동 테스트를 위한 더미 데이터 생성
> **참고 문서**: `react-mock-to-api-migration-plan.md`
---
## 1. 현황 분석
### 1.1 기존 데이터 현황
| 테이블 | 현재 개수 | 추가 목표 | 최종 목표 |
|--------|----------|----------|----------|
| tenants | 5 | - | 기존 활용 |
| users | 13 | - | 기존 활용 |
| clients | 4 (tenant 287) | +20 | 24개 |
| client_groups | 0 | +5 | 5개 |
| bank_accounts | 0 | +5 | 5개 |
| sales | 0 (tenant 287) | +80 | 80개 |
| purchases | 0 | +70 | 70개 |
| deposits | 0 | +60 | 60개 |
| withdrawals | 0 | +60 | 60개 |
| bills | 0 | +30 | 30개 |
| bill_installments | 0 | +15 | 15개 |
| **총계** | - | **~345개** | - |
### 1.2 대상 테넌트
**Target: ID 287 (프론트_테스트회사)**
- Code: `JTKKPNNG6D`
- 기존 거래처 4개 보유 (ID: 9, 10, 11, 12)
- 테스트 목적으로 적합
### 1.3 데이터 기간
**2025년 1월 ~ 12월 (1년간)**
- 월별 균등 분포
- 최근월(11~12월)은 draft 상태 포함
---
## 2. 테이블 의존성 분석
### 2.1 의존성 다이어그램
```
┌─────────────────────────────────────────────────────────────────┐
│ Level 0: 기반 데이터 (이미 존재) │
├─────────────────────────────────────────────────────────────────┤
│ tenants (ID: 287) ──┬── users (ID: 1) │
│ │ │
└──────────────────────┼──────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Level 1: 마스터 데이터 (5 + 5 = 10개) │
├─────────────────────────────────────────────────────────────────┤
│ ├── client_groups (5개) - 거래처 그룹 │
│ └── bank_accounts (5개) - 은행 계좌 │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Level 2: 거래처 데이터 (20개) │
├─────────────────────────────────────────────────────────────────┤
│ clients (20개) ← client_group_id (optional) │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Level 3: 입출금/어음 데이터 (60 + 60 + 30 = 150개) │
├─────────────────────────────────────────────────────────────────┤
│ ├── deposits (60개) ← client_id, bank_account_id │
│ ├── withdrawals (60개) ← client_id, bank_account_id │
│ └── bills (30개) ← client_id, bank_account_id │
│ └── bill_installments (15개) ← bill_id │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Level 4: 매출/매입 데이터 (80 + 70 = 150개) │
├─────────────────────────────────────────────────────────────────┤
│ ├── sales (80개) ← client_id, deposit_id (optional) │
│ └── purchases (70개) ← client_id, withdrawal_id (optional) │
└─────────────────────────────────────────────────────────────────┘
```
### 2.2 삽입 순서 및 수량
```
1. client_groups (5개) - 거래처 그룹
2. bank_accounts (5개) - 은행 계좌
3. clients (20개) - 거래처 (매출처/매입처)
4. deposits (60개) - 입금 (월 5건)
5. withdrawals (60개) - 출금 (월 5건)
6. bills (30개) - 어음 (수취 15건 + 발행 15건)
7. sales (80개) - 매출 (월 6~7건)
8. purchases (70개) - 매입 (월 5~6건)
────────────────────────────
총계 ~345개
```
---
## 3. 더미 데이터 스키마
### 3.1 client_groups (거래처 그룹) - 5개
```php
[
['group_code' => 'VIP', 'group_name' => 'VIP 고객', 'price_rate' => 0.95],
['group_code' => 'GOLD', 'group_name' => '골드 고객', 'price_rate' => 0.97],
['group_code' => 'SILVER', 'group_name' => '실버 고객', 'price_rate' => 0.98],
['group_code' => 'NORMAL', 'group_name' => '일반 고객', 'price_rate' => 1.00],
['group_code' => 'NEW', 'group_name' => '신규 고객', 'price_rate' => 1.00],
]
```
### 3.2 bank_accounts (은행 계좌) - 5개
```php
[
['bank_code' => '004', 'bank_name' => 'KB국민은행', 'account_number' => '123-45-6789012', 'account_holder' => '프론트테스트', 'account_name' => '운영계좌', 'is_primary' => true],
['bank_code' => '088', 'bank_name' => '신한은행', 'account_number' => '110-123-456789', 'account_holder' => '프론트테스트', 'account_name' => '급여계좌', 'is_primary' => false],
['bank_code' => '020', 'bank_name' => '우리은행', 'account_number' => '1002-123-456789', 'account_holder' => '프론트테스트', 'account_name' => '예비계좌', 'is_primary' => false],
['bank_code' => '081', 'bank_name' => '하나은행', 'account_number' => '123-456789-12345','account_holder' => '프론트테스트', 'account_name' => '법인카드', 'is_primary' => false],
['bank_code' => '011', 'bank_name' => 'NH농협은행', 'account_number' => '351-1234-5678-12','account_holder' => '프론트테스트', 'account_name' => '비상금', 'is_primary' => false],
]
```
### 3.3 clients (거래처) - 20개
#### 매출처 (SALES) - 10개
| Code | 회사명 | 사업자번호 | 그룹 | 담당자 |
|------|--------|-----------|------|--------|
| S001 | 삼성전자 | 124-81-00998 | VIP | 김철수 |
| S002 | LG전자 | 107-86-14075 | VIP | 이영희 |
| S003 | SK하이닉스 | 204-81-17169 | GOLD | 박민수 |
| S004 | 현대자동차 | 101-81-05765 | GOLD | 정은지 |
| S005 | 네이버 | 220-81-62517 | GOLD | 최준호 |
| S006 | 카카오 | 120-87-65763 | SILVER | 강미래 |
| S007 | 쿠팡 | 120-88-00767 | SILVER | 임도현 |
| S008 | 토스 | 120-87-83139 | NORMAL | 윤서연 |
| S009 | 배달의민족 | 220-87-93847 | NORMAL | 한지민 |
| S010 | 당근마켓 | 815-87-01234 | NEW | 오태양 |
#### 매입처 (PURCHASE) - 7개
| Code | 회사명 | 사업자번호 | 그룹 | 담당자 |
|------|--------|-----------|------|--------|
| P001 | 한화솔루션 | 138-81-00610 | - | 김재원 |
| P002 | 포스코 | 506-81-08754 | - | 이현석 |
| P003 | 롯데케미칼 | 301-81-07123 | - | 박서준 |
| P004 | GS칼텍스 | 104-81-23858 | - | 정해인 |
| P005 | 대한항공 | 110-81-14794 | - | 송민호 |
| P006 | 현대제철 | 130-81-12345 | - | 강동원 |
| P007 | SK이노베이션 | 110-81-67890 | - | 유재석 |
#### 매출매입처 (BOTH) - 3개
| Code | 회사명 | 사업자번호 | 그룹 | 담당자 |
|------|--------|-----------|------|--------|
| B001 | 두산에너빌리티 | 124-81-08628 | GOLD | 조인성 |
| B002 | CJ대한통운 | 104-81-39849 | SILVER | 공유 |
| B003 | 삼성SDS | 124-81-34567 | VIP | 이정재 |
### 3.4 월별 데이터 분포
| 월 | 입금 | 출금 | 매출 | 매입 | 상태 |
|----|------|------|------|------|------|
| 1월 | 5건 | 5건 | 6건 | 5건 | confirmed/invoiced |
| 2월 | 5건 | 5건 | 6건 | 5건 | confirmed/invoiced |
| 3월 | 5건 | 5건 | 7건 | 6건 | confirmed/invoiced |
| 4월 | 5건 | 5건 | 6건 | 5건 | confirmed/invoiced |
| 5월 | 5건 | 5건 | 7건 | 6건 | confirmed/invoiced |
| 6월 | 5건 | 5건 | 6건 | 6건 | confirmed/invoiced |
| 7월 | 5건 | 5건 | 7건 | 6건 | confirmed/invoiced |
| 8월 | 5건 | 5건 | 6건 | 6건 | confirmed/invoiced |
| 9월 | 5건 | 5건 | 7건 | 6건 | confirmed/invoiced |
| 10월 | 5건 | 5건 | 7건 | 6건 | confirmed/invoiced |
| 11월 | 5건 | 5건 | 8건 | 7건 | confirmed + draft |
| 12월 | 5건 | 5건 | 7건 | 6건 | draft 위주 |
| **합계** | **60건** | **60건** | **80건** | **70건** | |
### 3.5 금액 범위 및 분포
```
┌─────────────────────────────────────────────────────────────────┐
│ 금액 분포 (공급가액 기준) │
├─────────────────────────────────────────────────────────────────┤
│ 소액 (30%): 1,000,000 ~ 5,000,000원 │
│ 중액 (50%): 5,000,000 ~ 30,000,000원 │
│ 대액 (20%): 30,000,000 ~ 100,000,000원 │
├─────────────────────────────────────────────────────────────────┤
│ 세액: 공급가액 × 10% │
│ 합계: 공급가액 × 110% │
└─────────────────────────────────────────────────────────────────┘
```
### 3.6 deposits (입금) - 60개 예시
```php
// 월별 5건씩, 총 60건
// 결제수단: transfer(70%), card(15%), cash(10%), check(5%)
$deposits = [
// 1월
['date' => '2025-01-05', 'client' => '삼성전자', 'amount' => 55000000, 'method' => 'transfer'],
['date' => '2025-01-10', 'client' => 'LG전자', 'amount' => 28000000, 'method' => 'transfer'],
['date' => '2025-01-15', 'client' => '현대자동차', 'amount' => 42000000, 'method' => 'transfer'],
['date' => '2025-01-20', 'client' => '네이버', 'amount' => 15000000, 'method' => 'card'],
['date' => '2025-01-25', 'client' => '카카오', 'amount' => 8500000, 'method' => 'transfer'],
// 2월
['date' => '2025-02-05', 'client' => 'SK하이닉스', 'amount' => 68000000, 'method' => 'transfer'],
['date' => '2025-02-10', 'client' => '쿠팡', 'amount' => 22000000, 'method' => 'transfer'],
['date' => '2025-02-15', 'client' => '토스', 'amount' => 9800000, 'method' => 'card'],
['date' => '2025-02-20', 'client' => '삼성SDS', 'amount' => 35000000, 'method' => 'transfer'],
['date' => '2025-02-25', 'client' => '두산에너빌리티','amount' => 48000000, 'method' => 'check'],
// ... 3월 ~ 12월 (패턴 반복, Seeder에서 자동 생성)
];
```
### 3.7 withdrawals (출금) - 60개 예시
```php
// 월별 5건씩, 총 60건
$withdrawals = [
// 1월
['date' => '2025-01-03', 'client' => '한화솔루션', 'amount' => 32000000, 'method' => 'transfer'],
['date' => '2025-01-08', 'client' => '포스코', 'amount' => 45000000, 'method' => 'transfer'],
['date' => '2025-01-15', 'client' => '롯데케미칼', 'amount' => 18000000, 'method' => 'transfer'],
['date' => '2025-01-22', 'client' => 'GS칼텍스', 'amount' => 12500000, 'method' => 'card'],
['date' => '2025-01-28', 'client' => '대한항공', 'amount' => 5800000, 'method' => 'transfer'],
// 2월
['date' => '2025-02-05', 'client' => '현대제철', 'amount' => 52000000, 'method' => 'transfer'],
['date' => '2025-02-12', 'client' => 'SK이노베이션', 'amount' => 28000000, 'method' => 'transfer'],
['date' => '2025-02-18', 'client' => 'CJ대한통운', 'amount' => 8500000, 'method' => 'transfer'],
['date' => '2025-02-22', 'client' => '한화솔루션', 'amount' => 25000000, 'method' => 'check'],
['date' => '2025-02-28', 'client' => '포스코', 'amount' => 38000000, 'method' => 'transfer'],
// ... 3월 ~ 12월 (패턴 반복)
];
```
### 3.8 sales (매출) - 80개 예시
```php
// 월별 6~7건, 총 80건
// 상태: 1~10월 invoiced/confirmed, 11~12월 draft 포함
$sales = [
// 1월 (6건)
['number' => 'SAL-202501-0001', 'date' => '2025-01-05', 'client' => '삼성전자', 'supply' => 50000000, 'status' => 'invoiced'],
['number' => 'SAL-202501-0002', 'date' => '2025-01-08', 'client' => 'LG전자', 'supply' => 25454545, 'status' => 'invoiced'],
['number' => 'SAL-202501-0003', 'date' => '2025-01-12', 'client' => '현대자동차', 'supply' => 38181818, 'status' => 'invoiced'],
['number' => 'SAL-202501-0004', 'date' => '2025-01-18', 'client' => '네이버', 'supply' => 13636364, 'status' => 'confirmed'],
['number' => 'SAL-202501-0005', 'date' => '2025-01-22', 'client' => '카카오', 'supply' => 7727273, 'status' => 'confirmed'],
['number' => 'SAL-202501-0006', 'date' => '2025-01-28', 'client' => '삼성SDS', 'supply' => 31818182, 'status' => 'invoiced'],
// ... 2월 ~ 10월 (invoiced/confirmed)
// 11월 (8건 - draft 포함)
['number' => 'SAL-202511-0001', 'date' => '2025-11-03', 'client' => '삼성전자', 'supply' => 45000000, 'status' => 'invoiced'],
['number' => 'SAL-202511-0002', 'date' => '2025-11-06', 'client' => 'SK하이닉스', 'supply' => 62000000, 'status' => 'invoiced'],
['number' => 'SAL-202511-0003', 'date' => '2025-11-10', 'client' => '쿠팡', 'supply' => 18181818, 'status' => 'confirmed'],
['number' => 'SAL-202511-0004', 'date' => '2025-11-14', 'client' => '두산에너빌리티','supply' => 55000000, 'status' => 'confirmed'],
['number' => 'SAL-202511-0005', 'date' => '2025-11-18', 'client' => '당근마켓', 'supply' => 8909091, 'status' => 'confirmed'],
['number' => 'SAL-202511-0006', 'date' => '2025-11-22', 'client' => '토스', 'supply' => 12727273, 'status' => 'draft'],
['number' => 'SAL-202511-0007', 'date' => '2025-11-26', 'client' => '배달의민족', 'supply' => 15454545, 'status' => 'draft'],
['number' => 'SAL-202511-0008', 'date' => '2025-11-29', 'client' => 'LG전자', 'supply' => 28000000, 'status' => 'draft'],
// 12월 (7건 - draft 위주)
['number' => 'SAL-202512-0001', 'date' => '2025-12-02', 'client' => '삼성전자', 'supply' => 48000000, 'status' => 'confirmed'],
['number' => 'SAL-202512-0002', 'date' => '2025-12-05', 'client' => '현대자동차', 'supply' => 35000000, 'status' => 'draft'],
['number' => 'SAL-202512-0003', 'date' => '2025-12-10', 'client' => '네이버', 'supply' => 22727273, 'status' => 'draft'],
['number' => 'SAL-202512-0004', 'date' => '2025-12-13', 'client' => '카카오', 'supply' => 16363636, 'status' => 'draft'],
['number' => 'SAL-202512-0005', 'date' => '2025-12-17', 'client' => '삼성SDS', 'supply' => 42000000, 'status' => 'draft'],
['number' => 'SAL-202512-0006', 'date' => '2025-12-20', 'client' => 'SK하이닉스', 'supply' => 58000000, 'status' => 'draft'],
['number' => 'SAL-202512-0007', 'date' => '2025-12-23', 'client' => '쿠팡', 'supply' => 20000000, 'status' => 'draft'],
];
```
### 3.9 purchases (매입) - 70개 예시
```php
// 월별 5~6건, 총 70건
$purchases = [
// 1월 (5건)
['number' => 'PUR-202501-0001', 'date' => '2025-01-03', 'client' => '한화솔루션', 'supply' => 29090909, 'status' => 'confirmed'],
['number' => 'PUR-202501-0002', 'date' => '2025-01-10', 'client' => '포스코', 'supply' => 40909091, 'status' => 'confirmed'],
['number' => 'PUR-202501-0003', 'date' => '2025-01-15', 'client' => '롯데케미칼', 'supply' => 16363636, 'status' => 'confirmed'],
['number' => 'PUR-202501-0004', 'date' => '2025-01-22', 'client' => 'GS칼텍스', 'supply' => 11363636, 'status' => 'confirmed'],
['number' => 'PUR-202501-0005', 'date' => '2025-01-28', 'client' => '대한항공', 'supply' => 5272727, 'status' => 'confirmed'],
// ... 2월 ~ 10월 (confirmed)
// 11월 (7건 - draft 포함)
['number' => 'PUR-202511-0001', 'date' => '2025-11-03', 'client' => '현대제철', 'supply' => 48000000, 'status' => 'confirmed'],
['number' => 'PUR-202511-0002', 'date' => '2025-11-08', 'client' => 'SK이노베이션', 'supply' => 32000000, 'status' => 'confirmed'],
['number' => 'PUR-202511-0003', 'date' => '2025-11-12', 'client' => 'CJ대한통운', 'supply' => 9090909, 'status' => 'confirmed'],
['number' => 'PUR-202511-0004', 'date' => '2025-11-18', 'client' => '한화솔루션', 'supply' => 35000000, 'status' => 'confirmed'],
['number' => 'PUR-202511-0005', 'date' => '2025-11-22', 'client' => '포스코', 'supply' => 42000000, 'status' => 'draft'],
['number' => 'PUR-202511-0006', 'date' => '2025-11-26', 'client' => '롯데케미칼', 'supply' => 18181818, 'status' => 'draft'],
['number' => 'PUR-202511-0007', 'date' => '2025-11-29', 'client' => '두산에너빌리티','supply' => 55000000, 'status' => 'draft'],
// 12월 (6건 - draft 위주)
['number' => 'PUR-202512-0001', 'date' => '2025-12-02', 'client' => 'GS칼텍스', 'supply' => 15454545, 'status' => 'confirmed'],
['number' => 'PUR-202512-0002', 'date' => '2025-12-06', 'client' => '대한항공', 'supply' => 7272727, 'status' => 'draft'],
['number' => 'PUR-202512-0003', 'date' => '2025-12-11', 'client' => '현대제철', 'supply' => 52000000, 'status' => 'draft'],
['number' => 'PUR-202512-0004', 'date' => '2025-12-15', 'client' => 'SK이노베이션', 'supply' => 28000000, 'status' => 'draft'],
['number' => 'PUR-202512-0005', 'date' => '2025-12-19', 'client' => '한화솔루션', 'supply' => 38000000, 'status' => 'draft'],
['number' => 'PUR-202512-0006', 'date' => '2025-12-23', 'client' => '포스코', 'supply' => 45000000, 'status' => 'draft'],
];
```
### 3.10 bills (어음) - 30개 예시
```php
// 수취 어음 15건 + 발행 어음 15건, 총 30건
// bill_type: received(수취), issued(발행)
// status (수취): stored, maturityAlert, maturityResult, paymentComplete, dishonored
// status (발행): stored, maturityAlert, collectionRequest, collectionComplete, suing, dishonored
$bills = [
// 수취 어음 (received) - 15건
['bill_number' => '202501000001', 'type' => 'received', 'client' => '삼성전자', 'amount' => 50000000, 'issue_date' => '2025-01-15', 'maturity_date' => '2025-04-15', 'status' => 'paymentComplete'],
['bill_number' => '202501000002', 'type' => 'received', 'client' => 'LG전자', 'amount' => 35000000, 'issue_date' => '2025-02-10', 'maturity_date' => '2025-05-10', 'status' => 'paymentComplete'],
['bill_number' => '202502000001', 'type' => 'received', 'client' => 'SK하이닉스', 'amount' => 80000000, 'issue_date' => '2025-02-20', 'maturity_date' => '2025-05-20', 'status' => 'paymentComplete'],
['bill_number' => '202503000001', 'type' => 'received', 'client' => '현대자동차', 'amount' => 45000000, 'issue_date' => '2025-03-05', 'maturity_date' => '2025-06-05', 'status' => 'maturityResult'],
['bill_number' => '202504000001', 'type' => 'received', 'client' => '네이버', 'amount' => 25000000, 'issue_date' => '2025-04-12', 'maturity_date' => '2025-07-12', 'status' => 'maturityResult'],
['bill_number' => '202505000001', 'type' => 'received', 'client' => '카카오', 'amount' => 18000000, 'issue_date' => '2025-05-08', 'maturity_date' => '2025-08-08', 'status' => 'stored'],
['bill_number' => '202506000001', 'type' => 'received', 'client' => '쿠팡', 'amount' => 32000000, 'issue_date' => '2025-06-15', 'maturity_date' => '2025-09-15', 'status' => 'stored'],
['bill_number' => '202507000001', 'type' => 'received', 'client' => '삼성SDS', 'amount' => 65000000, 'issue_date' => '2025-07-20', 'maturity_date' => '2025-10-20', 'status' => 'stored'],
['bill_number' => '202508000001', 'type' => 'received', 'client' => '토스', 'amount' => 15000000, 'issue_date' => '2025-08-10', 'maturity_date' => '2025-11-10', 'status' => 'stored'],
['bill_number' => '202509000001', 'type' => 'received', 'client' => '두산에너빌리티','amount' => 55000000, 'issue_date' => '2025-09-05', 'maturity_date' => '2025-12-05', 'status' => 'maturityAlert'],
['bill_number' => '202510000001', 'type' => 'received', 'client' => '삼성전자', 'amount' => 42000000, 'issue_date' => '2025-10-15', 'maturity_date' => '2026-01-15', 'status' => 'stored'],
['bill_number' => '202511000001', 'type' => 'received', 'client' => 'LG전자', 'amount' => 28000000, 'issue_date' => '2025-11-08', 'maturity_date' => '2026-02-08', 'status' => 'stored'],
['bill_number' => '202511000002', 'type' => 'received', 'client' => '네이버', 'amount' => 38000000, 'issue_date' => '2025-11-20', 'maturity_date' => '2026-02-20', 'status' => 'stored'],
['bill_number' => '202512000001', 'type' => 'received', 'client' => '현대자동차', 'amount' => 52000000, 'issue_date' => '2025-12-10', 'maturity_date' => '2026-03-10', 'status' => 'stored'],
['bill_number' => '202512000002', 'type' => 'received', 'client' => 'SK하이닉스', 'amount' => 70000000, 'issue_date' => '2025-12-18', 'maturity_date' => '2026-03-18', 'status' => 'stored'],
// 발행 어음 (issued) - 15건
['bill_number' => '202501100001', 'type' => 'issued', 'client' => '한화솔루션', 'amount' => 40000000, 'issue_date' => '2025-01-20', 'maturity_date' => '2025-04-20', 'status' => 'collectionComplete'],
['bill_number' => '202502100001', 'type' => 'issued', 'client' => '포스코', 'amount' => 55000000, 'issue_date' => '2025-02-15', 'maturity_date' => '2025-05-15', 'status' => 'collectionComplete'],
['bill_number' => '202503100001', 'type' => 'issued', 'client' => '롯데케미칼', 'amount' => 30000000, 'issue_date' => '2025-03-10', 'maturity_date' => '2025-06-10', 'status' => 'collectionComplete'],
['bill_number' => '202504100001', 'type' => 'issued', 'client' => 'GS칼텍스', 'amount' => 22000000, 'issue_date' => '2025-04-18', 'maturity_date' => '2025-07-18', 'status' => 'collectionComplete'],
['bill_number' => '202505100001', 'type' => 'issued', 'client' => '대한항공', 'amount' => 18000000, 'issue_date' => '2025-05-12', 'maturity_date' => '2025-08-12', 'status' => 'collectionRequest'],
['bill_number' => '202506100001', 'type' => 'issued', 'client' => '현대제철', 'amount' => 48000000, 'issue_date' => '2025-06-20', 'maturity_date' => '2025-09-20', 'status' => 'collectionRequest'],
['bill_number' => '202507100001', 'type' => 'issued', 'client' => 'SK이노베이션', 'amount' => 35000000, 'issue_date' => '2025-07-15', 'maturity_date' => '2025-10-15', 'status' => 'stored'],
['bill_number' => '202508100001', 'type' => 'issued', 'client' => 'CJ대한통운', 'amount' => 25000000, 'issue_date' => '2025-08-22', 'maturity_date' => '2025-11-22', 'status' => 'stored'],
['bill_number' => '202509100001', 'type' => 'issued', 'client' => '두산에너빌리티','amount' => 60000000, 'issue_date' => '2025-09-10', 'maturity_date' => '2025-12-10', 'status' => 'maturityAlert'],
['bill_number' => '202510100001', 'type' => 'issued', 'client' => '한화솔루션', 'amount' => 45000000, 'issue_date' => '2025-10-08', 'maturity_date' => '2026-01-08', 'status' => 'stored'],
['bill_number' => '202511100001', 'type' => 'issued', 'client' => '포스코', 'amount' => 58000000, 'issue_date' => '2025-11-05', 'maturity_date' => '2026-02-05', 'status' => 'stored'],
['bill_number' => '202511100002', 'type' => 'issued', 'client' => '롯데케미칼', 'amount' => 32000000, 'issue_date' => '2025-11-18', 'maturity_date' => '2026-02-18', 'status' => 'stored'],
['bill_number' => '202512100001', 'type' => 'issued', 'client' => 'GS칼텍스', 'amount' => 28000000, 'issue_date' => '2025-12-05', 'maturity_date' => '2026-03-05', 'status' => 'stored'],
['bill_number' => '202512100002', 'type' => 'issued', 'client' => '현대제철', 'amount' => 42000000, 'issue_date' => '2025-12-15', 'maturity_date' => '2026-03-15', 'status' => 'stored'],
['bill_number' => '202512100003', 'type' => 'issued', 'client' => 'SK이노베이션', 'amount' => 38000000, 'issue_date' => '2025-12-22', 'maturity_date' => '2026-03-22', 'status' => 'stored'],
];
// 일부 어음에 차수 관리 내역 추가 (15건)
$billInstallments = [
// 수취 어음 차수
['bill_number' => '202501000001', 'installments' => [
['date' => '2025-02-15', 'amount' => 25000000, 'note' => '1차 분할 입금'],
['date' => '2025-03-15', 'amount' => 25000000, 'note' => '2차 분할 입금'],
]],
['bill_number' => '202502000001', 'installments' => [
['date' => '2025-03-20', 'amount' => 40000000, 'note' => '1차 분할 입금'],
['date' => '2025-04-20', 'amount' => 40000000, 'note' => '2차 분할 입금'],
]],
['bill_number' => '202507000001', 'installments' => [
['date' => '2025-08-20', 'amount' => 30000000, 'note' => '1차 분할 입금'],
['date' => '2025-09-20', 'amount' => 35000000, 'note' => '2차 분할 입금'],
]],
// 발행 어음 차수
['bill_number' => '202501100001', 'installments' => [
['date' => '2025-02-20', 'amount' => 20000000, 'note' => '1차 분할 지급'],
['date' => '2025-03-20', 'amount' => 20000000, 'note' => '2차 분할 지급'],
]],
['bill_number' => '202502100001', 'installments' => [
['date' => '2025-03-15', 'amount' => 27500000, 'note' => '1차 분할 지급'],
['date' => '2025-04-15', 'amount' => 27500000, 'note' => '2차 분할 지급'],
]],
['bill_number' => '202506100001', 'installments' => [
['date' => '2025-07-20', 'amount' => 24000000, 'note' => '1차 분할 지급'],
['date' => '2025-08-20', 'amount' => 24000000, 'note' => '2차 분할 지급'],
]],
];
```
---
## 4. Laravel Seeder 구현 전략
### 4.1 Seeder 파일 구조
```
database/seeders/
├── DummyDataSeeder.php ← 메인 Seeder (순서 제어)
└── Dummy/
├── DummyClientGroupSeeder.php ← 거래처 그룹 (5개)
├── DummyBankAccountSeeder.php ← 은행 계좌 (5개)
├── DummyClientSeeder.php ← 거래처 (20개)
├── DummyDepositSeeder.php ← 입금 (60개)
├── DummyWithdrawalSeeder.php ← 출금 (60개)
├── DummyBillSeeder.php ← 어음 (30개) + 차수 (15개)
├── DummySaleSeeder.php ← 매출 (80개)
└── DummyPurchaseSeeder.php ← 매입 (70개)
```
### 4.2 메인 Seeder
```php
<?php
// database/seeders/DummyDataSeeder.php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DummyDataSeeder extends Seeder
{
// 대상 테넌트 ID
public const TENANT_ID = 287;
// 생성자 사용자 ID
public const USER_ID = 1;
public function run(): void
{
$this->command->info('🌱 더미 데이터 시딩 시작...');
$this->command->info(' 대상 테넌트: ID ' . self::TENANT_ID);
$this->call([
Dummy\DummyClientGroupSeeder::class,
Dummy\DummyBankAccountSeeder::class,
Dummy\DummyClientSeeder::class,
Dummy\DummyDepositSeeder::class,
Dummy\DummyWithdrawalSeeder::class,
Dummy\DummyBillSeeder::class,
Dummy\DummySaleSeeder::class,
Dummy\DummyPurchaseSeeder::class,
]);
$this->command->info('');
$this->command->info('✅ 더미 데이터 시딩 완료!');
$this->command->table(
['테이블', '생성 수량'],
[
['client_groups', '5'],
['bank_accounts', '5'],
['clients', '20'],
['deposits', '60'],
['withdrawals', '60'],
['bills', '30'],
['bill_installments', '15'],
['sales', '80'],
['purchases', '70'],
['총계', '~345'],
]
);
}
}
```
### 4.3 실행 방법
```bash
# 더미 데이터 시딩
php artisan db:seed --class=DummyDataSeeder
# 특정 Seeder만 실행
php artisan db:seed --class=Database\\Seeders\\Dummy\\DummyClientSeeder
```
### 4.4 DB 스키마 상세 정의
#### 4.4.1 client_groups 테이블
```sql
CREATE TABLE client_groups (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
group_code VARCHAR(30) NOT NULL COMMENT '그룹 코드',
group_name VARCHAR(100) NOT NULL COMMENT '그룹명',
price_rate DECIMAL(5,4) DEFAULT 1.0000 COMMENT '가격 배율',
is_active TINYINT DEFAULT 1 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 NULL,
updated_at TIMESTAMP NULL,
deleted_at TIMESTAMP NULL,
UNIQUE INDEX uq_client_groups_tenant_code (tenant_id, group_code),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
);
```
#### 4.4.2 bank_accounts 테이블
```sql
CREATE TABLE bank_accounts (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
bank_code VARCHAR(10) NOT NULL COMMENT '은행 코드',
bank_name VARCHAR(50) NOT NULL COMMENT '은행명',
account_number VARCHAR(30) NOT NULL COMMENT '계좌번호',
account_holder VARCHAR(50) NOT NULL COMMENT '예금주',
account_name VARCHAR(100) NOT NULL COMMENT '계좌 별칭',
status VARCHAR(20) DEFAULT 'active' COMMENT '상태: active/inactive',
assigned_user_id BIGINT UNSIGNED NULL COMMENT '담당자 ID',
is_primary BOOLEAN DEFAULT FALSE 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 NULL,
updated_at TIMESTAMP NULL,
deleted_at TIMESTAMP NULL
);
```
#### 4.4.3 clients 테이블 (주요 컬럼)
```sql
-- 핵심 필드만 기재 (전체 스키마는 마이그레이션 참조)
CREATE TABLE clients (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
tenant_id BIGINT UNSIGNED NOT NULL,
client_group_id BIGINT UNSIGNED NULL COMMENT '그룹 ID (FK)',
client_code VARCHAR(20) NOT NULL COMMENT '거래처 코드',
name VARCHAR(100) NOT NULL COMMENT '거래처명',
client_type VARCHAR(20) NULL COMMENT 'SALES/PURCHASE/BOTH',
contact_person VARCHAR(50) NULL COMMENT '담당자명',
phone VARCHAR(20) NULL,
mobile VARCHAR(20) NULL,
email VARCHAR(100) NULL,
address TEXT NULL,
business_no VARCHAR(20) NULL COMMENT '사업자번호',
business_type VARCHAR(50) NULL COMMENT '업종',
business_item VARCHAR(100) NULL COMMENT '업태',
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP NULL,
updated_at TIMESTAMP NULL,
deleted_at TIMESTAMP NULL
);
```
#### 4.4.4 deposits 테이블
```sql
CREATE TABLE deposits (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
deposit_date DATE NOT NULL COMMENT '입금일',
client_id BIGINT UNSIGNED NULL COMMENT '거래처 ID',
client_name VARCHAR(100) NULL COMMENT '비회원 거래처명',
bank_account_id BIGINT UNSIGNED NULL COMMENT '입금 계좌 ID',
amount DECIMAL(15,2) NOT NULL COMMENT '금액',
payment_method VARCHAR(20) NOT NULL COMMENT 'cash/transfer/card/check',
account_code VARCHAR(20) NULL COMMENT '계정과목',
description TEXT NULL COMMENT '적요',
reference_type VARCHAR(50) NULL COMMENT '참조 유형',
reference_id BIGINT UNSIGNED NULL COMMENT '참조 ID',
created_by BIGINT UNSIGNED NULL,
updated_by BIGINT UNSIGNED NULL,
deleted_by BIGINT UNSIGNED NULL,
created_at TIMESTAMP NULL,
updated_at TIMESTAMP NULL,
deleted_at TIMESTAMP NULL
);
```
#### 4.4.5 withdrawals 테이블
```sql
CREATE TABLE withdrawals (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
withdrawal_date DATE NOT NULL COMMENT '출금일',
client_id BIGINT UNSIGNED NULL COMMENT '거래처 ID',
client_name VARCHAR(100) NULL COMMENT '비회원 거래처명',
bank_account_id BIGINT UNSIGNED NULL COMMENT '출금 계좌 ID',
amount DECIMAL(15,2) NOT NULL COMMENT '금액',
payment_method VARCHAR(20) NOT NULL COMMENT 'cash/transfer/card/check',
account_code VARCHAR(20) NULL COMMENT '계정과목',
description TEXT NULL COMMENT '적요',
reference_type VARCHAR(50) NULL COMMENT '참조 유형',
reference_id BIGINT UNSIGNED NULL COMMENT '참조 ID',
created_by BIGINT UNSIGNED NULL,
updated_by BIGINT UNSIGNED NULL,
deleted_by BIGINT UNSIGNED NULL,
created_at TIMESTAMP NULL,
updated_at TIMESTAMP NULL,
deleted_at TIMESTAMP NULL
);
```
#### 4.4.6 sales 테이블
```sql
CREATE TABLE sales (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
sale_number VARCHAR(30) NOT NULL COMMENT '매출번호',
sale_date DATE NOT NULL COMMENT '매출일자',
client_id BIGINT UNSIGNED NOT NULL COMMENT '거래처 ID',
supply_amount DECIMAL(15,2) NOT NULL COMMENT '공급가액',
tax_amount DECIMAL(15,2) NOT NULL COMMENT '세액',
total_amount DECIMAL(15,2) NOT NULL COMMENT '합계',
description TEXT NULL COMMENT '적요',
status VARCHAR(20) DEFAULT 'draft' COMMENT 'draft/confirmed/invoiced',
tax_invoice_id BIGINT UNSIGNED NULL COMMENT '세금계산서 ID',
deposit_id BIGINT UNSIGNED NULL COMMENT '입금 연결 ID',
created_by BIGINT UNSIGNED NULL,
updated_by BIGINT UNSIGNED NULL,
deleted_by BIGINT UNSIGNED NULL,
created_at TIMESTAMP NULL,
updated_at TIMESTAMP NULL,
deleted_at TIMESTAMP NULL,
UNIQUE INDEX uk_tenant_sale_number (tenant_id, sale_number)
);
```
#### 4.4.7 purchases 테이블
```sql
CREATE TABLE purchases (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
purchase_number VARCHAR(30) NOT NULL COMMENT '매입번호',
purchase_date DATE NOT NULL COMMENT '매입일자',
client_id BIGINT UNSIGNED NOT NULL COMMENT '거래처 ID',
supply_amount DECIMAL(15,2) NOT NULL COMMENT '공급가액',
tax_amount DECIMAL(15,2) NOT NULL COMMENT '세액',
total_amount DECIMAL(15,2) NOT NULL COMMENT '합계',
description TEXT NULL COMMENT '적요',
status VARCHAR(20) DEFAULT 'draft' COMMENT 'draft/confirmed',
withdrawal_id BIGINT UNSIGNED NULL COMMENT '출금 연결 ID',
created_by BIGINT UNSIGNED NULL,
updated_by BIGINT UNSIGNED NULL,
deleted_by BIGINT UNSIGNED NULL,
created_at TIMESTAMP NULL,
updated_at TIMESTAMP NULL,
deleted_at TIMESTAMP NULL,
UNIQUE INDEX uk_tenant_purchase_number (tenant_id, purchase_number)
);
```
### 4.5 모델 경로 및 네임스페이스
| 테이블 | 모델 클래스 | 네임스페이스 |
|--------|------------|-------------|
| client_groups | ClientGroup | `App\Models\Orders\ClientGroup` |
| bank_accounts | BankAccount | `App\Models\Tenants\BankAccount` |
| clients | Client | `App\Models\Orders\Client` |
| deposits | Deposit | `App\Models\Tenants\Deposit` |
| withdrawals | Withdrawal | `App\Models\Tenants\Withdrawal` |
| sales | Sale | `App\Models\Tenants\Sale` |
| purchases | Purchase | `App\Models\Tenants\Purchase` |
### 4.6 필수 상수값
#### 결제수단 (payment_method)
```php
// Deposit::PAYMENT_METHODS, Withdrawal::PAYMENT_METHODS
[
'cash' => '현금',
'transfer' => '계좌이체',
'card' => '카드',
'check' => '수표',
]
```
#### 매출 상태 (Sale::STATUSES)
```php
[
'draft' => '임시저장',
'confirmed' => '확정',
'invoiced' => '세금계산서발행',
]
```
#### 매입 상태 (Purchase::STATUSES)
```php
[
'draft' => '임시저장',
'confirmed' => '확정',
]
```
#### 거래처 유형 (client_type)
```php
// common_codes 테이블에서 관리
// group: 'client_type'
[
'SALES' => '매출처',
'PURCHASE' => '매입처',
'BOTH' => '매출매입처',
]
```
### 4.7 완전한 Seeder 코드 구현
#### 4.7.1 DummyClientGroupSeeder.php
```php
<?php
namespace Database\Seeders\Dummy;
use App\Models\Orders\ClientGroup;
use Database\Seeders\DummyDataSeeder;
use Illuminate\Database\Seeder;
class DummyClientGroupSeeder extends Seeder
{
public function run(): void
{
$tenantId = DummyDataSeeder::TENANT_ID;
$userId = DummyDataSeeder::USER_ID;
$groups = [
['group_code' => 'VIP', 'group_name' => 'VIP 고객', 'price_rate' => 0.95],
['group_code' => 'GOLD', 'group_name' => '골드 고객', 'price_rate' => 0.97],
['group_code' => 'SILVER', 'group_name' => '실버 고객', 'price_rate' => 0.98],
['group_code' => 'NORMAL', 'group_name' => '일반 고객', 'price_rate' => 1.00],
['group_code' => 'NEW', 'group_name' => '신규 고객', 'price_rate' => 1.00],
];
foreach ($groups as $group) {
ClientGroup::create([
'tenant_id' => $tenantId,
'group_code' => $group['group_code'],
'group_name' => $group['group_name'],
'price_rate' => $group['price_rate'],
'is_active' => true,
'created_by' => $userId,
]);
}
$this->command->info(' ✓ client_groups: ' . count($groups) . '건 생성');
}
}
```
#### 4.7.2 DummyBankAccountSeeder.php
```php
<?php
namespace Database\Seeders\Dummy;
use App\Models\Tenants\BankAccount;
use Database\Seeders\DummyDataSeeder;
use Illuminate\Database\Seeder;
class DummyBankAccountSeeder extends Seeder
{
public function run(): void
{
$tenantId = DummyDataSeeder::TENANT_ID;
$userId = DummyDataSeeder::USER_ID;
$accounts = [
['bank_code' => '004', 'bank_name' => 'KB국민은행', 'account_number' => '123-45-6789012', 'account_holder' => '프론트테스트', 'account_name' => '운영계좌', 'is_primary' => true],
['bank_code' => '088', 'bank_name' => '신한은행', 'account_number' => '110-123-456789', 'account_holder' => '프론트테스트', 'account_name' => '급여계좌', 'is_primary' => false],
['bank_code' => '020', 'bank_name' => '우리은행', 'account_number' => '1002-123-456789', 'account_holder' => '프론트테스트', 'account_name' => '예비계좌', 'is_primary' => false],
['bank_code' => '081', 'bank_name' => '하나은행', 'account_number' => '123-456789-12345','account_holder' => '프론트테스트', 'account_name' => '법인카드', 'is_primary' => false],
['bank_code' => '011', 'bank_name' => 'NH농협은행', 'account_number' => '351-1234-5678-12','account_holder' => '프론트테스트', 'account_name' => '비상금', 'is_primary' => false],
];
foreach ($accounts as $account) {
BankAccount::create([
'tenant_id' => $tenantId,
'bank_code' => $account['bank_code'],
'bank_name' => $account['bank_name'],
'account_number' => $account['account_number'],
'account_holder' => $account['account_holder'],
'account_name' => $account['account_name'],
'status' => 'active',
'is_primary' => $account['is_primary'],
'created_by' => $userId,
]);
}
$this->command->info(' ✓ bank_accounts: ' . count($accounts) . '건 생성');
}
}
```
#### 4.7.3 DummyClientSeeder.php
```php
<?php
namespace Database\Seeders\Dummy;
use App\Models\Orders\Client;
use App\Models\Orders\ClientGroup;
use Database\Seeders\DummyDataSeeder;
use Illuminate\Database\Seeder;
class DummyClientSeeder extends Seeder
{
public function run(): void
{
$tenantId = DummyDataSeeder::TENANT_ID;
$userId = DummyDataSeeder::USER_ID;
// 그룹 ID 조회
$groups = ClientGroup::where('tenant_id', $tenantId)
->pluck('id', 'group_code')
->toArray();
// 매출처 (SALES) - 10개
$salesClients = [
['code' => 'S001', 'name' => '삼성전자', 'business_no' => '124-81-00998', 'group' => 'VIP', 'contact' => '김철수', 'phone' => '02-1234-5678', 'email' => 'kim@samsung.com'],
['code' => 'S002', 'name' => 'LG전자', 'business_no' => '107-86-14075', 'group' => 'VIP', 'contact' => '이영희', 'phone' => '02-2345-6789', 'email' => 'lee@lg.com'],
['code' => 'S003', 'name' => 'SK하이닉스', 'business_no' => '204-81-17169', 'group' => 'GOLD', 'contact' => '박민수', 'phone' => '031-123-4567', 'email' => 'park@skhynix.com'],
['code' => 'S004', 'name' => '현대자동차', 'business_no' => '101-81-05765', 'group' => 'GOLD', 'contact' => '정은지', 'phone' => '02-3456-7890', 'email' => 'jung@hyundai.com'],
['code' => 'S005', 'name' => '네이버', 'business_no' => '220-81-62517', 'group' => 'GOLD', 'contact' => '최준호', 'phone' => '031-234-5678', 'email' => 'choi@naver.com'],
['code' => 'S006', 'name' => '카카오', 'business_no' => '120-87-65763', 'group' => 'SILVER', 'contact' => '강미래', 'phone' => '02-4567-8901', 'email' => 'kang@kakao.com'],
['code' => 'S007', 'name' => '쿠팡', 'business_no' => '120-88-00767', 'group' => 'SILVER', 'contact' => '임도현', 'phone' => '02-5678-9012', 'email' => 'lim@coupang.com'],
['code' => 'S008', 'name' => '토스', 'business_no' => '120-87-83139', 'group' => 'NORMAL', 'contact' => '윤서연', 'phone' => '02-6789-0123', 'email' => 'yoon@toss.im'],
['code' => 'S009', 'name' => '배달의민족', 'business_no' => '220-87-93847', 'group' => 'NORMAL', 'contact' => '한지민', 'phone' => '02-7890-1234', 'email' => 'han@woowahan.com'],
['code' => 'S010', 'name' => '당근마켓', 'business_no' => '815-87-01234', 'group' => 'NEW', 'contact' => '오태양', 'phone' => '02-8901-2345', 'email' => 'oh@daangn.com'],
];
// 매입처 (PURCHASE) - 7개
$purchaseClients = [
['code' => 'P001', 'name' => '한화솔루션', 'business_no' => '138-81-00610', 'group' => null, 'contact' => '김재원', 'phone' => '02-1111-2222', 'email' => 'kim@hanwha.com'],
['code' => 'P002', 'name' => '포스코', 'business_no' => '506-81-08754', 'group' => null, 'contact' => '이현석', 'phone' => '054-111-2222', 'email' => 'lee@posco.com'],
['code' => 'P003', 'name' => '롯데케미칼', 'business_no' => '301-81-07123', 'group' => null, 'contact' => '박서준', 'phone' => '02-2222-3333', 'email' => 'park@lottechem.com'],
['code' => 'P004', 'name' => 'GS칼텍스', 'business_no' => '104-81-23858', 'group' => null, 'contact' => '정해인', 'phone' => '02-3333-4444', 'email' => 'jung@gscaltex.com'],
['code' => 'P005', 'name' => '대한항공', 'business_no' => '110-81-14794', 'group' => null, 'contact' => '송민호', 'phone' => '02-4444-5555', 'email' => 'song@koreanair.com'],
['code' => 'P006', 'name' => '현대제철', 'business_no' => '130-81-12345', 'group' => null, 'contact' => '강동원', 'phone' => '032-555-6666', 'email' => 'kang@hyundaisteel.com'],
['code' => 'P007', 'name' => 'SK이노베이션', 'business_no' => '110-81-67890', 'group' => null, 'contact' => '유재석', 'phone' => '02-6666-7777', 'email' => 'yoo@skinnovation.com'],
];
// 매출매입처 (BOTH) - 3개
$bothClients = [
['code' => 'B001', 'name' => '두산에너빌리티', 'business_no' => '124-81-08628', 'group' => 'GOLD', 'contact' => '조인성', 'phone' => '02-7777-8888', 'email' => 'cho@doosan.com'],
['code' => 'B002', 'name' => 'CJ대한통운', 'business_no' => '104-81-39849', 'group' => 'SILVER', 'contact' => '공유', 'phone' => '02-8888-9999', 'email' => 'gong@cjlogistics.com'],
['code' => 'B003', 'name' => '삼성SDS', 'business_no' => '124-81-34567', 'group' => 'VIP', 'contact' => '이정재', 'phone' => '02-9999-0000', 'email' => 'lee@samsungsds.com'],
];
$count = 0;
// 매출처 생성
foreach ($salesClients as $client) {
$this->createClient($client, 'SALES', $tenantId, $userId, $groups);
$count++;
}
// 매입처 생성
foreach ($purchaseClients as $client) {
$this->createClient($client, 'PURCHASE', $tenantId, $userId, $groups);
$count++;
}
// 매출매입처 생성
foreach ($bothClients as $client) {
$this->createClient($client, 'BOTH', $tenantId, $userId, $groups);
$count++;
}
$this->command->info(' ✓ clients: ' . $count . '건 생성');
}
private function createClient(array $data, string $type, int $tenantId, int $userId, array $groups): void
{
Client::create([
'tenant_id' => $tenantId,
'client_group_id' => $data['group'] ? ($groups[$data['group']] ?? null) : null,
'client_code' => $data['code'],
'name' => $data['name'],
'client_type' => $type,
'contact_person' => $data['contact'],
'phone' => $data['phone'],
'email' => $data['email'],
'business_no' => $data['business_no'],
'business_type' => '제조업',
'business_item' => '전자제품',
'is_active' => true,
]);
}
}
```
#### 4.7.4 DummyDepositSeeder.php
```php
<?php
namespace Database\Seeders\Dummy;
use App\Models\Orders\Client;
use App\Models\Tenants\BankAccount;
use App\Models\Tenants\Deposit;
use Database\Seeders\DummyDataSeeder;
use Illuminate\Database\Seeder;
class DummyDepositSeeder extends Seeder
{
public function run(): void
{
$tenantId = DummyDataSeeder::TENANT_ID;
$userId = DummyDataSeeder::USER_ID;
// 거래처 매핑 (SALES, BOTH만)
$clients = Client::where('tenant_id', $tenantId)
->whereIn('client_type', ['SALES', 'BOTH'])
->get()
->keyBy('name');
// 은행계좌 (대표계좌 우선)
$bankAccounts = BankAccount::where('tenant_id', $tenantId)
->where('status', 'active')
->orderByDesc('is_primary')
->get();
$primaryBankId = $bankAccounts->first()?->id;
// 결제수단 분포: transfer(70%), card(15%), cash(10%), check(5%)
$methods = array_merge(
array_fill(0, 14, 'transfer'),
array_fill(0, 3, 'card'),
array_fill(0, 2, 'cash'),
array_fill(0, 1, 'check')
);
// 거래처 순환 목록
$clientNames = [
'삼성전자', 'LG전자', 'SK하이닉스', '현대자동차', '네이버',
'카카오', '쿠팡', '토스', '배달의민족', '당근마켓',
'두산에너빌리티', 'CJ대한통운', '삼성SDS',
];
// 금액 범위
$amounts = [
'small' => [1000000, 5000000], // 30%
'medium' => [5000000, 30000000], // 50%
'large' => [30000000, 100000000], // 20%
];
$count = 0;
$year = 2025;
// 월별 5건씩, 총 60건
for ($month = 1; $month <= 12; $month++) {
$daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
for ($i = 0; $i < 5; $i++) {
$day = rand(1, $daysInMonth);
$clientName = $clientNames[($month * 5 + $i) % count($clientNames)];
$client = $clients->get($clientName);
// 금액 결정 (분포에 따라)
$rand = rand(1, 100);
if ($rand <= 30) {
$amount = rand($amounts['small'][0], $amounts['small'][1]);
} elseif ($rand <= 80) {
$amount = rand($amounts['medium'][0], $amounts['medium'][1]);
} else {
$amount = rand($amounts['large'][0], $amounts['large'][1]);
}
Deposit::create([
'tenant_id' => $tenantId,
'deposit_date' => sprintf('%04d-%02d-%02d', $year, $month, $day),
'client_id' => $client?->id,
'client_name' => $client ? null : $clientName,
'bank_account_id' => $primaryBankId,
'amount' => $amount,
'payment_method' => $methods[array_rand($methods)],
'description' => $clientName . ' 입금',
'created_by' => $userId,
]);
$count++;
}
}
$this->command->info(' ✓ deposits: ' . $count . '건 생성');
}
}
```
#### 4.7.5 DummyWithdrawalSeeder.php
```php
<?php
namespace Database\Seeders\Dummy;
use App\Models\Orders\Client;
use App\Models\Tenants\BankAccount;
use App\Models\Tenants\Withdrawal;
use Database\Seeders\DummyDataSeeder;
use Illuminate\Database\Seeder;
class DummyWithdrawalSeeder extends Seeder
{
public function run(): void
{
$tenantId = DummyDataSeeder::TENANT_ID;
$userId = DummyDataSeeder::USER_ID;
// 거래처 매핑 (PURCHASE, BOTH만)
$clients = Client::where('tenant_id', $tenantId)
->whereIn('client_type', ['PURCHASE', 'BOTH'])
->get()
->keyBy('name');
// 은행계좌
$primaryBankId = BankAccount::where('tenant_id', $tenantId)
->where('is_primary', true)
->value('id');
// 결제수단 분포
$methods = array_merge(
array_fill(0, 14, 'transfer'),
array_fill(0, 3, 'card'),
array_fill(0, 2, 'cash'),
array_fill(0, 1, 'check')
);
// 매입처 순환 목록
$clientNames = [
'한화솔루션', '포스코', '롯데케미칼', 'GS칼텍스', '대한항공',
'현대제철', 'SK이노베이션', 'CJ대한통운', '두산에너빌리티',
];
$amounts = [
'small' => [1000000, 5000000],
'medium' => [5000000, 30000000],
'large' => [30000000, 80000000],
];
$count = 0;
$year = 2025;
for ($month = 1; $month <= 12; $month++) {
$daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
for ($i = 0; $i < 5; $i++) {
$day = rand(1, $daysInMonth);
$clientName = $clientNames[($month * 5 + $i) % count($clientNames)];
$client = $clients->get($clientName);
$rand = rand(1, 100);
if ($rand <= 30) {
$amount = rand($amounts['small'][0], $amounts['small'][1]);
} elseif ($rand <= 80) {
$amount = rand($amounts['medium'][0], $amounts['medium'][1]);
} else {
$amount = rand($amounts['large'][0], $amounts['large'][1]);
}
Withdrawal::create([
'tenant_id' => $tenantId,
'withdrawal_date' => sprintf('%04d-%02d-%02d', $year, $month, $day),
'client_id' => $client?->id,
'client_name' => $client ? null : $clientName,
'bank_account_id' => $primaryBankId,
'amount' => $amount,
'payment_method' => $methods[array_rand($methods)],
'description' => $clientName . ' 지급',
'created_by' => $userId,
]);
$count++;
}
}
$this->command->info(' ✓ withdrawals: ' . $count . '건 생성');
}
}
```
#### 4.7.6 DummySaleSeeder.php
```php
<?php
namespace Database\Seeders\Dummy;
use App\Models\Orders\Client;
use App\Models\Tenants\Sale;
use Database\Seeders\DummyDataSeeder;
use Illuminate\Database\Seeder;
class DummySaleSeeder extends Seeder
{
public function run(): void
{
$tenantId = DummyDataSeeder::TENANT_ID;
$userId = DummyDataSeeder::USER_ID;
// 거래처 매핑 (SALES, BOTH만)
$clients = Client::where('tenant_id', $tenantId)
->whereIn('client_type', ['SALES', 'BOTH'])
->get()
->keyBy('name');
$clientNames = [
'삼성전자', 'LG전자', 'SK하이닉스', '현대자동차', '네이버',
'카카오', '쿠팡', '토스', '배달의민족', '당근마켓',
'두산에너빌리티', 'CJ대한통운', '삼성SDS',
];
$amounts = [
'small' => [1000000, 5000000],
'medium' => [5000000, 30000000],
'large' => [30000000, 100000000],
];
// 월별 건수: 1~10월 6~7건, 11월 8건, 12월 7건 = 80건
$monthlyCount = [6, 6, 7, 6, 7, 6, 7, 6, 7, 7, 8, 7];
$count = 0;
$year = 2025;
for ($month = 1; $month <= 12; $month++) {
$daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
$salesCount = $monthlyCount[$month - 1];
for ($i = 0; $i < $salesCount; $i++) {
$day = (int) (($i + 1) * $daysInMonth / ($salesCount + 1));
$day = max(1, min($day, $daysInMonth));
$clientName = $clientNames[($count) % count($clientNames)];
$client = $clients->get($clientName);
// 금액 결정
$rand = rand(1, 100);
if ($rand <= 30) {
$supply = rand($amounts['small'][0], $amounts['small'][1]);
} elseif ($rand <= 80) {
$supply = rand($amounts['medium'][0], $amounts['medium'][1]);
} else {
$supply = rand($amounts['large'][0], $amounts['large'][1]);
}
// 상태 결정: 1~10월 invoiced/confirmed, 11~12월 draft 포함
if ($month <= 10) {
$status = rand(0, 1) ? 'invoiced' : 'confirmed';
} elseif ($month == 11) {
$status = $i < 5 ? (rand(0, 1) ? 'invoiced' : 'confirmed') : 'draft';
} else {
$status = $i < 1 ? 'confirmed' : 'draft';
}
$tax = round($supply * 0.1);
$total = $supply + $tax;
Sale::create([
'tenant_id' => $tenantId,
'sale_number' => sprintf('SAL-%04d%02d-%04d', $year, $month, $i + 1),
'sale_date' => sprintf('%04d-%02d-%02d', $year, $month, $day),
'client_id' => $client->id,
'supply_amount' => $supply,
'tax_amount' => $tax,
'total_amount' => $total,
'description' => $clientName . ' 매출',
'status' => $status,
'created_by' => $userId,
]);
$count++;
}
}
$this->command->info(' ✓ sales: ' . $count . '건 생성');
}
}
```
#### 4.7.7 DummyPurchaseSeeder.php
```php
<?php
namespace Database\Seeders\Dummy;
use App\Models\Orders\Client;
use App\Models\Tenants\Purchase;
use Database\Seeders\DummyDataSeeder;
use Illuminate\Database\Seeder;
class DummyPurchaseSeeder extends Seeder
{
public function run(): void
{
$tenantId = DummyDataSeeder::TENANT_ID;
$userId = DummyDataSeeder::USER_ID;
// 거래처 매핑 (PURCHASE, BOTH만)
$clients = Client::where('tenant_id', $tenantId)
->whereIn('client_type', ['PURCHASE', 'BOTH'])
->get()
->keyBy('name');
$clientNames = [
'한화솔루션', '포스코', '롯데케미칼', 'GS칼텍스', '대한항공',
'현대제철', 'SK이노베이션', 'CJ대한통운', '두산에너빌리티',
];
$amounts = [
'small' => [1000000, 5000000],
'medium' => [5000000, 30000000],
'large' => [30000000, 80000000],
];
// 월별 건수: 1~10월 5~6건, 11월 7건, 12월 6건 = 70건
$monthlyCount = [5, 5, 6, 5, 6, 6, 6, 6, 6, 6, 7, 6];
$count = 0;
$year = 2025;
for ($month = 1; $month <= 12; $month++) {
$daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
$purchaseCount = $monthlyCount[$month - 1];
for ($i = 0; $i < $purchaseCount; $i++) {
$day = (int) (($i + 1) * $daysInMonth / ($purchaseCount + 1));
$day = max(1, min($day, $daysInMonth));
$clientName = $clientNames[($count) % count($clientNames)];
$client = $clients->get($clientName);
$rand = rand(1, 100);
if ($rand <= 30) {
$supply = rand($amounts['small'][0], $amounts['small'][1]);
} elseif ($rand <= 80) {
$supply = rand($amounts['medium'][0], $amounts['medium'][1]);
} else {
$supply = rand($amounts['large'][0], $amounts['large'][1]);
}
// 상태 결정
if ($month <= 10) {
$status = 'confirmed';
} elseif ($month == 11) {
$status = $i < 4 ? 'confirmed' : 'draft';
} else {
$status = $i < 1 ? 'confirmed' : 'draft';
}
$tax = round($supply * 0.1);
$total = $supply + $tax;
Purchase::create([
'tenant_id' => $tenantId,
'purchase_number' => sprintf('PUR-%04d%02d-%04d', $year, $month, $i + 1),
'purchase_date' => sprintf('%04d-%02d-%02d', $year, $month, $day),
'client_id' => $client->id,
'supply_amount' => $supply,
'tax_amount' => $tax,
'total_amount' => $total,
'description' => $clientName . ' 매입',
'status' => $status,
'created_by' => $userId,
]);
$count++;
}
}
$this->command->info(' ✓ purchases: ' . $count . '건 생성');
}
}
```
#### 4.7.8 DummyBillSeeder.php
```php
<?php
namespace Database\Seeders\Dummy;
use App\Models\Orders\Client;
use App\Models\Tenants\BankAccount;
use App\Models\Tenants\Bill;
use App\Models\Tenants\BillInstallment;
use Database\Seeders\DummyDataSeeder;
use Illuminate\Database\Seeder;
class DummyBillSeeder extends Seeder
{
public function run(): void
{
$tenantId = DummyDataSeeder::TENANT_ID;
$userId = DummyDataSeeder::USER_ID;
// 거래처 매핑
$clients = Client::where('tenant_id', $tenantId)->get()->keyBy('name');
// 은행계좌 (대표계좌)
$primaryBankId = BankAccount::where('tenant_id', $tenantId)
->where('is_primary', true)
->value('id');
// 수취 어음 데이터 (received) - 15건
$receivedBills = [
['bill_number' => '202501000001', 'client' => '삼성전자', 'amount' => 50000000, 'issue_date' => '2025-01-15', 'maturity_date' => '2025-04-15', 'status' => 'paymentComplete'],
['bill_number' => '202501000002', 'client' => 'LG전자', 'amount' => 35000000, 'issue_date' => '2025-02-10', 'maturity_date' => '2025-05-10', 'status' => 'paymentComplete'],
['bill_number' => '202502000001', 'client' => 'SK하이닉스', 'amount' => 80000000, 'issue_date' => '2025-02-20', 'maturity_date' => '2025-05-20', 'status' => 'paymentComplete'],
['bill_number' => '202503000001', 'client' => '현대자동차', 'amount' => 45000000, 'issue_date' => '2025-03-05', 'maturity_date' => '2025-06-05', 'status' => 'maturityResult'],
['bill_number' => '202504000001', 'client' => '네이버', 'amount' => 25000000, 'issue_date' => '2025-04-12', 'maturity_date' => '2025-07-12', 'status' => 'maturityResult'],
['bill_number' => '202505000001', 'client' => '카카오', 'amount' => 18000000, 'issue_date' => '2025-05-08', 'maturity_date' => '2025-08-08', 'status' => 'stored'],
['bill_number' => '202506000001', 'client' => '쿠팡', 'amount' => 32000000, 'issue_date' => '2025-06-15', 'maturity_date' => '2025-09-15', 'status' => 'stored'],
['bill_number' => '202507000001', 'client' => '삼성SDS', 'amount' => 65000000, 'issue_date' => '2025-07-20', 'maturity_date' => '2025-10-20', 'status' => 'stored'],
['bill_number' => '202508000001', 'client' => '토스', 'amount' => 15000000, 'issue_date' => '2025-08-10', 'maturity_date' => '2025-11-10', 'status' => 'stored'],
['bill_number' => '202509000001', 'client' => '두산에너빌리티', 'amount' => 55000000, 'issue_date' => '2025-09-05', 'maturity_date' => '2025-12-05', 'status' => 'maturityAlert'],
['bill_number' => '202510000001', 'client' => '삼성전자', 'amount' => 42000000, 'issue_date' => '2025-10-15', 'maturity_date' => '2026-01-15', 'status' => 'stored'],
['bill_number' => '202511000001', 'client' => 'LG전자', 'amount' => 28000000, 'issue_date' => '2025-11-08', 'maturity_date' => '2026-02-08', 'status' => 'stored'],
['bill_number' => '202511000002', 'client' => '네이버', 'amount' => 38000000, 'issue_date' => '2025-11-20', 'maturity_date' => '2026-02-20', 'status' => 'stored'],
['bill_number' => '202512000001', 'client' => '현대자동차', 'amount' => 52000000, 'issue_date' => '2025-12-10', 'maturity_date' => '2026-03-10', 'status' => 'stored'],
['bill_number' => '202512000002', 'client' => 'SK하이닉스', 'amount' => 70000000, 'issue_date' => '2025-12-18', 'maturity_date' => '2026-03-18', 'status' => 'stored'],
];
// 발행 어음 데이터 (issued) - 15건
$issuedBills = [
['bill_number' => '202501100001', 'client' => '한화솔루션', 'amount' => 40000000, 'issue_date' => '2025-01-20', 'maturity_date' => '2025-04-20', 'status' => 'collectionComplete'],
['bill_number' => '202502100001', 'client' => '포스코', 'amount' => 55000000, 'issue_date' => '2025-02-15', 'maturity_date' => '2025-05-15', 'status' => 'collectionComplete'],
['bill_number' => '202503100001', 'client' => '롯데케미칼', 'amount' => 30000000, 'issue_date' => '2025-03-10', 'maturity_date' => '2025-06-10', 'status' => 'collectionComplete'],
['bill_number' => '202504100001', 'client' => 'GS칼텍스', 'amount' => 22000000, 'issue_date' => '2025-04-18', 'maturity_date' => '2025-07-18', 'status' => 'collectionComplete'],
['bill_number' => '202505100001', 'client' => '대한항공', 'amount' => 18000000, 'issue_date' => '2025-05-12', 'maturity_date' => '2025-08-12', 'status' => 'collectionRequest'],
['bill_number' => '202506100001', 'client' => '현대제철', 'amount' => 48000000, 'issue_date' => '2025-06-20', 'maturity_date' => '2025-09-20', 'status' => 'collectionRequest'],
['bill_number' => '202507100001', 'client' => 'SK이노베이션', 'amount' => 35000000, 'issue_date' => '2025-07-15', 'maturity_date' => '2025-10-15', 'status' => 'stored'],
['bill_number' => '202508100001', 'client' => 'CJ대한통운', 'amount' => 25000000, 'issue_date' => '2025-08-22', 'maturity_date' => '2025-11-22', 'status' => 'stored'],
['bill_number' => '202509100001', 'client' => '두산에너빌리티', 'amount' => 60000000, 'issue_date' => '2025-09-10', 'maturity_date' => '2025-12-10', 'status' => 'maturityAlert'],
['bill_number' => '202510100001', 'client' => '한화솔루션', 'amount' => 45000000, 'issue_date' => '2025-10-08', 'maturity_date' => '2026-01-08', 'status' => 'stored'],
['bill_number' => '202511100001', 'client' => '포스코', 'amount' => 58000000, 'issue_date' => '2025-11-05', 'maturity_date' => '2026-02-05', 'status' => 'stored'],
['bill_number' => '202511100002', 'client' => '롯데케미칼', 'amount' => 32000000, 'issue_date' => '2025-11-18', 'maturity_date' => '2026-02-18', 'status' => 'stored'],
['bill_number' => '202512100001', 'client' => 'GS칼텍스', 'amount' => 28000000, 'issue_date' => '2025-12-05', 'maturity_date' => '2026-03-05', 'status' => 'stored'],
['bill_number' => '202512100002', 'client' => '현대제철', 'amount' => 42000000, 'issue_date' => '2025-12-15', 'maturity_date' => '2026-03-15', 'status' => 'stored'],
['bill_number' => '202512100003', 'client' => 'SK이노베이션', 'amount' => 38000000, 'issue_date' => '2025-12-22', 'maturity_date' => '2026-03-22', 'status' => 'stored'],
];
// 차수 관리 데이터
$installmentsData = [
'202501000001' => [
['date' => '2025-02-15', 'amount' => 25000000, 'note' => '1차 분할 입금'],
['date' => '2025-03-15', 'amount' => 25000000, 'note' => '2차 분할 입금'],
],
'202502000001' => [
['date' => '2025-03-20', 'amount' => 40000000, 'note' => '1차 분할 입금'],
['date' => '2025-04-20', 'amount' => 40000000, 'note' => '2차 분할 입금'],
],
'202507000001' => [
['date' => '2025-08-20', 'amount' => 30000000, 'note' => '1차 분할 입금'],
['date' => '2025-09-20', 'amount' => 35000000, 'note' => '2차 분할 입금'],
],
'202501100001' => [
['date' => '2025-02-20', 'amount' => 20000000, 'note' => '1차 분할 지급'],
['date' => '2025-03-20', 'amount' => 20000000, 'note' => '2차 분할 지급'],
],
'202502100001' => [
['date' => '2025-03-15', 'amount' => 27500000, 'note' => '1차 분할 지급'],
['date' => '2025-04-15', 'amount' => 27500000, 'note' => '2차 분할 지급'],
],
'202506100001' => [
['date' => '2025-07-20', 'amount' => 24000000, 'note' => '1차 분할 지급'],
['date' => '2025-08-20', 'amount' => 24000000, 'note' => '2차 분할 지급'],
],
];
$billCount = 0;
$installmentCount = 0;
// 수취 어음 생성
foreach ($receivedBills as $data) {
$client = $clients->get($data['client']);
$bill = Bill::create([
'tenant_id' => $tenantId,
'bill_number' => $data['bill_number'],
'bill_type' => 'received',
'client_id' => $client?->id,
'client_name' => $client ? null : $data['client'],
'amount' => $data['amount'],
'issue_date' => $data['issue_date'],
'maturity_date' => $data['maturity_date'],
'status' => $data['status'],
'is_electronic' => rand(0, 1) === 1,
'bank_account_id' => $primaryBankId,
'installment_count' => isset($installmentsData[$data['bill_number']]) ? count($installmentsData[$data['bill_number']]) : 0,
'created_by' => $userId,
]);
$billCount++;
// 차수 관리 데이터 추가
if (isset($installmentsData[$data['bill_number']])) {
foreach ($installmentsData[$data['bill_number']] as $instData) {
BillInstallment::create([
'bill_id' => $bill->id,
'installment_date' => $instData['date'],
'amount' => $instData['amount'],
'note' => $instData['note'],
]);
$installmentCount++;
}
}
}
// 발행 어음 생성
foreach ($issuedBills as $data) {
$client = $clients->get($data['client']);
$bill = Bill::create([
'tenant_id' => $tenantId,
'bill_number' => $data['bill_number'],
'bill_type' => 'issued',
'client_id' => $client?->id,
'client_name' => $client ? null : $data['client'],
'amount' => $data['amount'],
'issue_date' => $data['issue_date'],
'maturity_date' => $data['maturity_date'],
'status' => $data['status'],
'is_electronic' => rand(0, 1) === 1,
'bank_account_id' => $primaryBankId,
'installment_count' => isset($installmentsData[$data['bill_number']]) ? count($installmentsData[$data['bill_number']]) : 0,
'created_by' => $userId,
]);
$billCount++;
// 차수 관리 데이터 추가
if (isset($installmentsData[$data['bill_number']])) {
foreach ($installmentsData[$data['bill_number']] as $instData) {
BillInstallment::create([
'bill_id' => $bill->id,
'installment_date' => $instData['date'],
'amount' => $instData['amount'],
'note' => $instData['note'],
]);
$installmentCount++;
}
}
}
$this->command->info(' ✓ bills: ' . $billCount . '건 생성');
$this->command->info(' ✓ bill_installments: ' . $installmentCount . '건 생성');
}
}
```
---
## 5. 데이터 검증
### 5.1 시딩 후 검증 쿼리
```sql
-- 테이블별 데이터 수 확인
SELECT 'client_groups' as tbl, COUNT(*) as cnt FROM client_groups WHERE tenant_id = 287
UNION ALL
SELECT 'bank_accounts', COUNT(*) FROM bank_accounts WHERE tenant_id = 287
UNION ALL
SELECT 'clients', COUNT(*) FROM clients WHERE tenant_id = 287
UNION ALL
SELECT 'deposits', COUNT(*) FROM deposits WHERE tenant_id = 287
UNION ALL
SELECT 'withdrawals', COUNT(*) FROM withdrawals WHERE tenant_id = 287
UNION ALL
SELECT 'bills', COUNT(*) FROM bills WHERE tenant_id = 287
UNION ALL
SELECT 'bill_installments', COUNT(*) FROM bill_installments WHERE bill_id IN (SELECT id FROM bills WHERE tenant_id = 287)
UNION ALL
SELECT 'sales', COUNT(*) FROM sales WHERE tenant_id = 287
UNION ALL
SELECT 'purchases', COUNT(*) FROM purchases WHERE tenant_id = 287;
-- 어음 현황 (수취/발행별)
SELECT bill_type, status, COUNT(*) as count, SUM(amount) as total_amount
FROM bills
WHERE tenant_id = 287
GROUP BY bill_type, status
ORDER BY bill_type, status;
-- 월별 매출 현황
SELECT DATE_FORMAT(sale_date, '%Y-%m') as month,
COUNT(*) as count,
SUM(total_amount) as total
FROM sales
WHERE tenant_id = 287
GROUP BY month
ORDER BY month;
-- 거래처별 매출/매입 합계
SELECT c.name,
COALESCE(SUM(s.total_amount), 0) as total_sales,
COALESCE(SUM(p.total_amount), 0) as total_purchases
FROM clients c
LEFT JOIN sales s ON c.id = s.client_id AND s.deleted_at IS NULL
LEFT JOIN purchases p ON c.id = p.client_id AND p.deleted_at IS NULL
WHERE c.tenant_id = 287
GROUP BY c.id, c.name
ORDER BY total_sales DESC;
```
### 5.2 예상 결과 요약
| 항목 | 예상 값 |
|------|---------|
| 총 거래처 | 24개 (기존 4 + 신규 20) |
| 총 매출건수 | 80건 |
| 총 매입건수 | 70건 |
| 총 입금건수 | 60건 |
| 총 출금건수 | 60건 |
| 총 어음건수 | 30건 (수취 15건 + 발행 15건) |
| 총 차수건수 | 12건 (6개 어음 × 2차) |
| 연간 매출액 | 약 25~30억원 |
| 연간 매입액 | 약 18~22억원 |
| 수취어음 총액 | 약 6.5억원 |
| 발행어음 총액 | 약 5.8억원 |
---
## 6. 변경 이력
| 날짜 | 내용 | 작성자 |
|------|------|--------|
| 2025-12-23 | 문서 초안 작성 | Claude |
| 2025-12-23 | 1년치 300개 데이터로 확장 | Claude |
| 2025-12-23 | DB 스키마 상세 정의 추가 (4.4) | Claude |
| 2025-12-23 | 모델 경로 및 네임스페이스 추가 (4.5) | Claude |
| 2025-12-23 | 필수 상수값 정의 추가 (4.6) | Claude |
| 2025-12-23 | 완전한 Seeder 코드 구현 추가 (4.7) | Claude |
| 2025-12-23 | 어음(bills) 더미 데이터 추가 (30건 + 차수 12건) | Claude |
---
## 7. 다음 단계
1. [x] Seeder 파일 구현 → 4.7 섹션에 완전한 코드 포함
2. [ ] Seeder 파일 생성 (코드 복사 후 파일 생성)
3. [ ] 시딩 실행 (`php artisan db:seed --class=DummyDataSeeder`)
4. [ ] 데이터 검증 (5.1 검증 쿼리 실행)
5. [ ] React 연동 테스트
6. [ ] 추가 데이터 필요시 확장
---
## 8. 빠른 시작 가이드
### 8.1 Seeder 파일 생성 순서
```bash
# 1. Dummy 디렉토리 생성
mkdir -p api/database/seeders/Dummy
# 2. 파일 생성 (4.2 ~ 4.7 코드 복사)
# - api/database/seeders/DummyDataSeeder.php
# - api/database/seeders/Dummy/DummyClientGroupSeeder.php
# - api/database/seeders/Dummy/DummyBankAccountSeeder.php
# - api/database/seeders/Dummy/DummyClientSeeder.php
# - api/database/seeders/Dummy/DummyDepositSeeder.php
# - api/database/seeders/Dummy/DummyWithdrawalSeeder.php
# - api/database/seeders/Dummy/DummySaleSeeder.php
# - api/database/seeders/Dummy/DummyPurchaseSeeder.php
# 3. 시딩 실행
cd api
php artisan db:seed --class=DummyDataSeeder
# 4. 검증
php artisan tinker
>>> \App\Models\Tenants\Sale::where('tenant_id', 287)->count()
# 결과: 80
```
### 8.2 주요 참조 정보
| 항목 | 값 |
|------|-----|
| **대상 테넌트 ID** | 287 |
| **생성자 사용자 ID** | 1 |
| **데이터 기간** | 2025년 1월 ~ 12월 |
| **총 레코드 수** | ~300개 |
### 8.3 관련 문서
- [React Mock to API Migration Plan](./react-mock-to-api-migration-plan.md) - API 마이그레이션 계획
- [API Rules](../reference/api-rules.md) - API 개발 규칙