docs: 기획 문서 추가
- 문서관리 1단계 변경사항 (20260128_document_management_phase1_1.md) - FCM 사용자 타겟 알림 계획 - 수입검사 문서 통합 계획 - 품목 마이그레이션 계획 (경동) - 수주 마이그레이션 계획 (경동) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
106
changes/20260128_document_management_phase1_1.md
Normal file
106
changes/20260128_document_management_phase1_1.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# 변경 내용 요약
|
||||
|
||||
**날짜:** 2026-01-28
|
||||
**작업자:** Claude Code
|
||||
**작업명:** 문서 관리 시스템 Phase 1.1 - 마이그레이션 파일 생성
|
||||
|
||||
## 📋 변경 개요
|
||||
|
||||
문서 관리 시스템의 데이터베이스 스키마를 구현했습니다.
|
||||
- 4개 테이블 신규 생성 (documents, document_approvals, document_data, document_attachments)
|
||||
- SAM API 개발 규칙 준수 (tenant_id, 감사 컬럼, softDeletes, comment)
|
||||
|
||||
## 📁 추가된 파일
|
||||
|
||||
| 파일 | 설명 |
|
||||
|------|------|
|
||||
| `api/database/migrations/2026_01_28_200000_create_documents_table.php` | 문서 관리 테이블 마이그레이션 |
|
||||
|
||||
## 🔧 상세 변경 사항
|
||||
|
||||
### 1. documents 테이블 (16 컬럼)
|
||||
실제 문서 정보를 저장하는 메인 테이블
|
||||
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| id | bigint | PK |
|
||||
| tenant_id | bigint | 테넌트 ID (FK) |
|
||||
| template_id | bigint | 템플릿 ID (FK → document_templates) |
|
||||
| document_no | varchar(50) | 문서번호 |
|
||||
| title | varchar(255) | 문서 제목 |
|
||||
| status | enum | DRAFT/PENDING/APPROVED/REJECTED/CANCELLED |
|
||||
| linkable_type | varchar(100) | 연결 모델 타입 (다형성) |
|
||||
| linkable_id | bigint | 연결 모델 ID |
|
||||
| submitted_at | timestamp | 결재 요청일 |
|
||||
| completed_at | timestamp | 결재 완료일 |
|
||||
| created_by | bigint | 생성자 ID |
|
||||
| updated_by | bigint | 수정자 ID |
|
||||
| deleted_by | bigint | 삭제자 ID |
|
||||
| created_at | timestamp | 생성일 |
|
||||
| updated_at | timestamp | 수정일 |
|
||||
| deleted_at | timestamp | 삭제일 (Soft Delete) |
|
||||
|
||||
### 2. document_approvals 테이블 (12 컬럼)
|
||||
문서 결재 정보 저장
|
||||
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| id | bigint | PK |
|
||||
| document_id | bigint | 문서 ID (FK) |
|
||||
| user_id | bigint | 결재자 ID (FK) |
|
||||
| step | tinyint | 결재 순서 |
|
||||
| role | varchar(50) | 역할 (작성/검토/승인) |
|
||||
| status | enum | PENDING/APPROVED/REJECTED |
|
||||
| comment | text | 결재 의견 |
|
||||
| acted_at | timestamp | 결재 처리일 |
|
||||
| created_by | bigint | 생성자 ID |
|
||||
| updated_by | bigint | 수정자 ID |
|
||||
| created_at | timestamp | 생성일 |
|
||||
| updated_at | timestamp | 수정일 |
|
||||
|
||||
### 3. document_data 테이블 (9 컬럼)
|
||||
문서 데이터 저장 (EAV 패턴)
|
||||
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| id | bigint | PK |
|
||||
| document_id | bigint | 문서 ID (FK) |
|
||||
| section_id | bigint | 섹션 ID |
|
||||
| column_id | bigint | 컬럼 ID |
|
||||
| row_index | smallint | 행 인덱스 |
|
||||
| field_key | varchar(100) | 필드 키 |
|
||||
| field_value | text | 필드 값 |
|
||||
| created_at | timestamp | 생성일 |
|
||||
| updated_at | timestamp | 수정일 |
|
||||
|
||||
### 4. document_attachments 테이블 (8 컬럼)
|
||||
문서 첨부파일 연결
|
||||
|
||||
| 컬럼 | 타입 | 설명 |
|
||||
|------|------|------|
|
||||
| id | bigint | PK |
|
||||
| document_id | bigint | 문서 ID (FK) |
|
||||
| file_id | bigint | 파일 ID (FK → files) |
|
||||
| attachment_type | varchar(50) | 첨부 유형 |
|
||||
| description | varchar(255) | 설명 |
|
||||
| created_by | bigint | 생성자 ID |
|
||||
| created_at | timestamp | 생성일 |
|
||||
| updated_at | timestamp | 수정일 |
|
||||
|
||||
## ✅ 검증 결과
|
||||
|
||||
| 시나리오 | 예상 결과 | 실제 결과 | 상태 |
|
||||
|----------|----------|----------|:----:|
|
||||
| 마이그레이션 실행 | 4개 테이블 생성 | 4개 테이블 생성 | ✅ |
|
||||
| PHP 문법 검사 | 오류 없음 | 오류 없음 | ✅ |
|
||||
| Pint 포맷팅 | 통과 | 1개 스타일 수정 후 통과 | ✅ |
|
||||
| SAM 규칙 준수 | 모든 규칙 적용 | 모든 규칙 적용 | ✅ |
|
||||
|
||||
## 🔗 관련 문서
|
||||
|
||||
- 계획 문서: `docs/plans/document-management-system-plan.md`
|
||||
- 다음 작업: Phase 1.2 - 모델 생성 (Document, DocumentApproval, DocumentData, DocumentAttachment)
|
||||
|
||||
## ⚠️ 배포 시 주의사항
|
||||
|
||||
특이사항 없음 (마이그레이션은 이미 실행됨)
|
||||
369
plans/fcm-user-targeted-notification-plan.md
Normal file
369
plans/fcm-user-targeted-notification-plan.md
Normal file
@@ -0,0 +1,369 @@
|
||||
# FCM 사용자별 알림 발송 계획
|
||||
|
||||
> **작성일**: 2026-01-28
|
||||
> **목적**: FCM 푸시 알림을 테넌트 전체 브로드캐스트에서 사용자별 타겟 발송으로 변경
|
||||
> **상태**: ✅ 구현 완료
|
||||
|
||||
---
|
||||
|
||||
## 📍 현재 진행 상태
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **마지막 완료 작업** | Phase 4 - FCM 발송 로직 수정 완료 |
|
||||
| **다음 작업** | 테스트 검증 |
|
||||
| **진행률** | 8/8 (100%) |
|
||||
| **마지막 업데이트** | 2026-01-28 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 개요
|
||||
|
||||
### 1.1 배경
|
||||
|
||||
현재 TodayIssue 생성 시 FCM 푸시 알림이 **테넌트 전체 사용자** 중 알림 설정이 켜진 모든 사용자에게 발송됨.
|
||||
|
||||
**문제점**:
|
||||
- 결재요청 알림이 결재자가 아닌 사람에게도 발송됨
|
||||
- 기안 승인/반려/완료 알림이 기안자가 아닌 사람에게도 발송됨
|
||||
- 불필요한 알림으로 사용자 경험 저하
|
||||
|
||||
### 1.2 목표
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 🎯 핵심 목표 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ 1. 이슈 타입에 따라 특정 대상자에게만 FCM 발송 │
|
||||
│ 2. 사용자별 알림 설정(ON/OFF)이 정상 동작하도록 보장 │
|
||||
│ 3. 근태 알림은 제외 (정책 미확정) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.3 발송 대상 정책
|
||||
|
||||
| 이슈 타입 | 현재 | 변경 후 대상 |
|
||||
|-----------|------|-------------|
|
||||
| **결재요청** | 테넌트 전체 | **결재자(나)** - ApprovalStep.user_id |
|
||||
| **기안 승인** | 테넌트 전체 | **기안자** - Approval.drafter_id |
|
||||
| **기안 반려** | 테넌트 전체 | **기안자** - Approval.drafter_id |
|
||||
| **기안 완료** | 테넌트 전체 | **기안자** - Approval.drafter_id |
|
||||
| 수주등록 | 테넌트 전체 | 테넌트 전체 (변경 없음) |
|
||||
| 추심이슈 | 테넌트 전체 | 테넌트 전체 (변경 없음) |
|
||||
| 안전재고 | 테넌트 전체 | 테넌트 전체 (변경 없음) |
|
||||
| 지출승인 | 테넌트 전체 | 테넌트 전체 (변경 없음) |
|
||||
| 세금신고 | 테넌트 전체 | 테넌트 전체 (변경 없음) |
|
||||
| 신규업체 | 테넌트 전체 | 테넌트 전체 (변경 없음) |
|
||||
| 입금 | 테넌트 전체 | 테넌트 전체 (변경 없음) |
|
||||
| 출금 | 테넌트 전체 | 테넌트 전체 (변경 없음) |
|
||||
| **근태 알림** | - | **제외** (정책 미확정) |
|
||||
|
||||
### 1.4 변경 승인 정책
|
||||
|
||||
| 분류 | 예시 | 승인 |
|
||||
|------|------|------|
|
||||
| ✅ 즉시 가능 | 필드 추가, 로직 수정, 문서 수정 | 불필요 |
|
||||
| ⚠️ 컨펌 필요 | 마이그레이션, 새 테이블/컬럼 | **필수** |
|
||||
| 🔴 금지 | 기존 테이블 구조 변경, 파괴적 변경 | 별도 협의 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 대상 범위
|
||||
|
||||
### 2.1 Phase 1: 데이터베이스 변경
|
||||
|
||||
| # | 작업 항목 | 상태 | 비고 |
|
||||
|---|----------|:----:|------|
|
||||
| 1.1 | TodayIssue 테이블에 `target_user_id` 컬럼 추가 | ✅ | nullable, FK |
|
||||
| 1.2 | 마이그레이션 파일 생성 | ✅ | 2026_01_28_132426 |
|
||||
|
||||
### 2.2 Phase 2: 모델 수정
|
||||
|
||||
| # | 작업 항목 | 상태 | 비고 |
|
||||
|---|----------|:----:|------|
|
||||
| 2.1 | TodayIssue 모델에 target_user_id 추가 | ✅ | fillable, relation, scopes |
|
||||
| 2.2 | TodayIssue::createIssue() 메서드에 targetUserId 파라미터 추가 | ✅ | |
|
||||
|
||||
### 2.3 Phase 3: Observer 수정
|
||||
|
||||
| # | 작업 항목 | 상태 | 비고 |
|
||||
|---|----------|:----:|------|
|
||||
| 3.1 | handleApprovalStepChange() - 결재요청 시 결재자 지정 | ✅ | step->user_id 전달 |
|
||||
| 3.2 | 기안 승인/반려/완료 알림 추가 (기안자 지정) | ✅ | ApprovalIssueObserver 신규 |
|
||||
|
||||
### 2.4 Phase 4: FCM 발송 로직 수정
|
||||
|
||||
| # | 작업 항목 | 상태 | 비고 |
|
||||
|---|----------|:----:|------|
|
||||
| 4.1 | sendFcmNotification() - target_user_id 있으면 해당 사용자만 | ✅ | |
|
||||
| 4.2 | getEnabledUserTokens() - 특정 사용자 필터링 로직 추가 | ✅ | |
|
||||
|
||||
---
|
||||
|
||||
## 3. 작업 절차
|
||||
|
||||
### 3.1 단계별 절차
|
||||
|
||||
```
|
||||
Step 1: 데이터베이스 변경
|
||||
├── today_issues 테이블에 target_user_id 컬럼 추가
|
||||
├── 마이그레이션 실행
|
||||
└── 검증: 테이블 구조 확인
|
||||
|
||||
Step 2: TodayIssue 모델 수정
|
||||
├── target_user_id fillable 추가
|
||||
├── targetUser() relation 추가
|
||||
└── createIssue() 파라미터 추가
|
||||
|
||||
Step 3: TodayIssueObserverService 수정
|
||||
├── createIssueWithFcm() 파라미터 추가
|
||||
├── handleApprovalStepChange() 수정 - 결재자 지정
|
||||
├── 기안 상태 변경 알림 추가 (신규)
|
||||
└── 근태 알림 비활성화
|
||||
|
||||
Step 4: FCM 발송 로직 수정
|
||||
├── sendFcmNotification() 수정
|
||||
├── getEnabledUserTokens() 수정 - targetUserId 파라미터 추가
|
||||
└── 검증: 대상자만 수신 확인
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 상세 작업 내용
|
||||
|
||||
### 4.1 Phase 1: 데이터베이스 변경
|
||||
|
||||
**마이그레이션 파일**:
|
||||
```php
|
||||
// database/migrations/xxxx_add_target_user_id_to_today_issues_table.php
|
||||
|
||||
Schema::table('today_issues', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('target_user_id')
|
||||
->nullable()
|
||||
->after('source_id')
|
||||
->comment('특정 대상 사용자 ID (null이면 테넌트 전체)');
|
||||
|
||||
$table->foreign('target_user_id')
|
||||
->references('id')
|
||||
->on('users')
|
||||
->onDelete('cascade');
|
||||
|
||||
$table->index(['tenant_id', 'target_user_id']);
|
||||
});
|
||||
```
|
||||
|
||||
### 4.2 Phase 2: TodayIssue 모델 수정
|
||||
|
||||
```php
|
||||
// app/Models/Tenants/TodayIssue.php
|
||||
|
||||
protected $fillable = [
|
||||
// ... 기존 필드
|
||||
'target_user_id', // 추가
|
||||
];
|
||||
|
||||
public function targetUser(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'target_user_id');
|
||||
}
|
||||
|
||||
public static function createIssue(
|
||||
int $tenantId,
|
||||
string $sourceType,
|
||||
?int $sourceId,
|
||||
string $badge,
|
||||
string $content,
|
||||
?string $path = null,
|
||||
bool $needsApproval = false,
|
||||
?\DateTime $expiresAt = null,
|
||||
?int $targetUserId = null // 추가
|
||||
): self {
|
||||
// ... 기존 로직 + target_user_id 저장
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 Phase 3: Observer 수정
|
||||
|
||||
**결재요청 - 결재자에게만**:
|
||||
```php
|
||||
// handleApprovalStepChange() 수정
|
||||
|
||||
$this->createIssueWithFcm(
|
||||
tenantId: $approval->tenant_id,
|
||||
sourceType: TodayIssue::SOURCE_APPROVAL,
|
||||
sourceId: $step->id,
|
||||
badge: TodayIssue::BADGE_APPROVAL_REQUEST,
|
||||
content: __('message.today_issue.approval_pending', [...]),
|
||||
path: '/approval/inbox',
|
||||
needsApproval: true,
|
||||
expiresAt: null,
|
||||
targetUserId: $step->user_id // 결재자
|
||||
);
|
||||
```
|
||||
|
||||
**기안 승인/반려/완료 - 기안자에게만** (신규):
|
||||
```php
|
||||
// handleApprovalStatusChange() 신규 메서드
|
||||
|
||||
public function handleApprovalStatusChange(Approval $approval): void
|
||||
{
|
||||
$badge = match($approval->status) {
|
||||
'approved' => TodayIssue::BADGE_DRAFT_APPROVED,
|
||||
'rejected' => TodayIssue::BADGE_DRAFT_REJECTED,
|
||||
'completed' => TodayIssue::BADGE_DRAFT_COMPLETED,
|
||||
default => null,
|
||||
};
|
||||
|
||||
if (!$badge) return;
|
||||
|
||||
$this->createIssueWithFcm(
|
||||
tenantId: $approval->tenant_id,
|
||||
sourceType: TodayIssue::SOURCE_APPROVAL,
|
||||
sourceId: $approval->id,
|
||||
badge: $badge,
|
||||
content: __('message.today_issue.'.$approval->status, [...]),
|
||||
path: '/approval/draft',
|
||||
needsApproval: false,
|
||||
expiresAt: Carbon::now()->addDays(7),
|
||||
targetUserId: $approval->drafter_id // 기안자
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 Phase 4: FCM 발송 로직 수정
|
||||
|
||||
```php
|
||||
// sendFcmNotification() 수정
|
||||
|
||||
public function sendFcmNotification(TodayIssue $issue): void
|
||||
{
|
||||
// target_user_id가 있으면 해당 사용자만, 없으면 테넌트 전체
|
||||
$tokens = $this->getEnabledUserTokens(
|
||||
$issue->tenant_id,
|
||||
$issue->notification_type,
|
||||
$issue->target_user_id // 추가
|
||||
);
|
||||
|
||||
// ... 기존 발송 로직
|
||||
}
|
||||
|
||||
// getEnabledUserTokens() 수정
|
||||
|
||||
private function getEnabledUserTokens(
|
||||
int $tenantId,
|
||||
string $notificationType,
|
||||
?int $targetUserId = null // 추가
|
||||
): array {
|
||||
$query = PushDeviceToken::withoutGlobalScopes()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('is_active', true)
|
||||
->whereNull('deleted_at');
|
||||
|
||||
// 특정 대상자가 지정된 경우
|
||||
if ($targetUserId !== null) {
|
||||
$query->where('user_id', $targetUserId);
|
||||
}
|
||||
|
||||
$tokens = $query->get();
|
||||
|
||||
// 알림 설정 확인 후 필터링
|
||||
$enabledTokens = [];
|
||||
foreach ($tokens as $token) {
|
||||
if ($this->isNotificationEnabledForUser($tenantId, $token->user_id, $notificationType)) {
|
||||
$enabledTokens[] = $token->token;
|
||||
}
|
||||
}
|
||||
|
||||
return $enabledTokens;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 제외 항목
|
||||
|
||||
### 5.1 근태 알림 (정책 미확정)
|
||||
|
||||
다음 알림 타입은 이번 작업에서 **제외**:
|
||||
- 연차 알림
|
||||
- 출근 알림
|
||||
- 지각 알림
|
||||
- 결근 알림
|
||||
|
||||
**사유**: 정책이 모호하여 추후 별도 작업
|
||||
|
||||
### 5.2 알림 소리 커스터마이징
|
||||
|
||||
현재는 **하드코딩된 채널별 알림음** 사용:
|
||||
- `push_urgent`: 긴급 (신규업체)
|
||||
- `push_payment`: 결재
|
||||
- `push_sales_order`: 수주
|
||||
- `push_default`: 기타
|
||||
|
||||
**추후 작업**: 사용자별 알림 설정의 `soundType` 값 기준으로 발송
|
||||
|
||||
---
|
||||
|
||||
## 6. 영향받는 파일
|
||||
|
||||
### API (api/)
|
||||
|
||||
| 파일 | 변경 내용 |
|
||||
|------|----------|
|
||||
| `database/migrations/2026_01_28_132426_add_target_user_id_to_today_issues_table.php` | 신규 - 마이그레이션 |
|
||||
| `app/Models/Tenants/TodayIssue.php` | target_user_id 추가, 신규 뱃지 상수, targetUser 관계, forUser/targetedTo 스코프 |
|
||||
| `app/Services/TodayIssueObserverService.php` | createIssueWithFcm, sendFcmNotification, getEnabledUserTokens 수정, handleApprovalStatusChange 추가 |
|
||||
| `app/Observers/TodayIssue/ApprovalIssueObserver.php` | 신규 - 기안 상태 변경 Observer |
|
||||
| `app/Providers/AppServiceProvider.php` | ApprovalIssueObserver 등록 |
|
||||
| `lang/ko/message.php` | 신규 메시지 키 추가 (draft_approved/rejected/completed) |
|
||||
|
||||
### React (react/) - 변경 없음
|
||||
|
||||
프론트엔드 알림 설정 UI는 이미 사용자별로 구현되어 있음.
|
||||
|
||||
---
|
||||
|
||||
## 7. 검증 방법
|
||||
|
||||
### 7.1 테스트 시나리오
|
||||
|
||||
| # | 시나리오 | 예상 결과 |
|
||||
|---|----------|----------|
|
||||
| 1 | A가 B에게 결재 요청 | B에게만 FCM 발송 |
|
||||
| 2 | B가 A의 기안 승인 | A에게만 FCM 발송 |
|
||||
| 3 | B가 A의 기안 반려 | A에게만 FCM 발송 |
|
||||
| 4 | 수주 등록 | 테넌트 전체 (알림 ON인 사용자만) |
|
||||
| 5 | A가 알림 OFF → 수주 등록 | A에게는 발송 안됨 |
|
||||
|
||||
### 7.2 성공 기준
|
||||
|
||||
- [ ] 결재요청 알림이 결재자에게만 발송됨
|
||||
- [ ] 기안 상태 변경 알림이 기안자에게만 발송됨
|
||||
- [ ] 사용자별 알림 설정(ON/OFF)이 정상 동작함
|
||||
- [ ] 기존 브로드캐스트 이슈(수주, 입금 등)는 정상 동작함
|
||||
|
||||
---
|
||||
|
||||
## 8. 참고 문서
|
||||
|
||||
- `api/app/Services/TodayIssueObserverService.php` - 현재 발송 로직
|
||||
- `api/app/Models/NotificationSetting.php` - 알림 설정 모델
|
||||
- `react/src/components/settings/NotificationSettings/types.ts` - 프론트엔드 알림 설정 타입
|
||||
|
||||
---
|
||||
|
||||
## 9. 변경 이력
|
||||
|
||||
| 날짜 | 항목 | 변경 내용 | 파일 | 승인 |
|
||||
|------|------|----------|------|------|
|
||||
| 2026-01-28 | - | 계획 문서 초안 작성 | - | - |
|
||||
| 2026-01-28 | Phase 1 | target_user_id 컬럼 추가 마이그레이션 | migrations/2026_01_28_132426_* | ✅ |
|
||||
| 2026-01-28 | Phase 2 | TodayIssue 모델 수정 (fillable, relation, scopes) | TodayIssue.php | ✅ |
|
||||
| 2026-01-28 | Phase 3 | Observer 수정 (결재자/기안자 타겟팅) | TodayIssueObserverService.php, ApprovalIssueObserver.php | ✅ |
|
||||
| 2026-01-28 | Phase 4 | FCM 발송 로직 수정 | TodayIssueObserverService.php | ✅ |
|
||||
| 2026-01-28 | 신규 | ApprovalIssueObserver 생성 | ApprovalIssueObserver.php | ✅ |
|
||||
| 2026-01-28 | i18n | 기안 상태 알림 메시지 추가 | lang/ko/message.php | ✅ |
|
||||
|
||||
---
|
||||
|
||||
*이 문서는 /sc:plan 스킬로 생성되었습니다.*
|
||||
672
plans/incoming-inspection-document-integration-plan.md
Normal file
672
plans/incoming-inspection-document-integration-plan.md
Normal file
@@ -0,0 +1,672 @@
|
||||
# 수입검사 성적서 시스템 연동 계획
|
||||
|
||||
> **작성일**: 2025-01-28
|
||||
> **목적**: MNG 문서양식관리로 수입검사 성적서 템플릿(20종 - 제품별 검사기준 상이) 생성 및 미리보기 구현, 이후 API/React 연동
|
||||
> **기준 문서**: `docs/plans/document-management-system-plan.md`, `mng/resources/views/document-templates/`
|
||||
> **상태**: 📋 계획 수립
|
||||
|
||||
---
|
||||
|
||||
## 📍 현재 진행 상태
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **마지막 완료 작업** | 분석 완료 |
|
||||
| **다음 작업** | Phase 1.1 - 수입검사 성적서 양식 템플릿 생성 (MNG) |
|
||||
| **진행률** | 0/8 (0%) |
|
||||
| **마지막 업데이트** | 2025-01-28 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 개요
|
||||
|
||||
### 1.1 배경
|
||||
|
||||
현재 React 프론트엔드의 수입검사 성적서 모달(`InspectionCreate.tsx`)은 4개 검사항목이 하드코딩되어 있음. 실제로는 **품목(원자재) 종류별로 검사기준이 다른 20여 종의 수입검사 성적서 양식**이 필요하며, MNG의 문서양식관리/문서관리 시스템과 연동하여:
|
||||
|
||||
1. **문서양식관리**: 수입검사 성적서 양식 20종 생성 (각 양식마다 검사항목, 기준, 수치가 다름)
|
||||
2. **품목-양식 매핑**: 각 품목이 어떤 양식을 사용할지 연결
|
||||
3. **문서관리**: 실제 검사 결과 저장 및 조회
|
||||
4. **React 모달**: 품목에 맞는 양식 자동 선택 → 검사항목 동적 렌더링
|
||||
|
||||
**양식 20종 구조:**
|
||||
```
|
||||
양식 A (철제품용) ←── 품목: 가이드레일, 브라켓, 철판
|
||||
양식 B (도장품용) ←── 품목: 도어프레임, 패널
|
||||
양식 C (플라스틱용) ←── 품목: 사출부품, 커버
|
||||
양식 D (원자재용) ←── 품목: 철판, 봉강
|
||||
... (20종)
|
||||
```
|
||||
|
||||
### 1.2 현재 시스템 구조
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ React (InspectionCreate.tsx) │
|
||||
│ ├─ 검사 대상 선택 (좌측) │
|
||||
│ ├─ 검사 정보 (검사일, 검사자, LOT번호) │
|
||||
│ ├─ 검사 항목 테이블 (4개 하드코딩) ← 동적화 필요 │
|
||||
│ └─ 종합 의견 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
↓ (현재 미연동)
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ MNG (문서양식관리/문서관리) │
|
||||
│ ├─ DocumentTemplate (양식 정의) │
|
||||
│ │ ├─ ApprovalLines (결재선) │
|
||||
│ │ ├─ BasicFields (기본 필드) │
|
||||
│ │ ├─ Sections → SectionItems (검사 항목) ← 20종 동적 기준 │
|
||||
│ │ └─ Columns (테이블 컬럼) │
|
||||
│ └─ Document + DocumentData (EAV 패턴) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.3 목표 시스템 구조
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ React (InspectionCreate.tsx) │
|
||||
│ ├─ API: GET /inspection-templates?item_code=xxx │
|
||||
│ │ └─ 제품별 검사 항목 동적 로드 │
|
||||
│ ├─ API: POST /documents │
|
||||
│ │ └─ 검사 결과 저장 (Document + DocumentData) │
|
||||
│ └─ API: GET /documents/{id} │
|
||||
│ └─ 저장된 성적서 조회 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ API (Laravel) │
|
||||
│ ├─ InspectionTemplateService │
|
||||
│ │ └─ 제품 ↔ 검사양식 매핑 │
|
||||
│ └─ DocumentService │
|
||||
│ └─ 검사 결과 CRUD │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.4 기준 원칙
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 🎯 핵심 원칙 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ 1. EAV 패턴 활용: DocumentData로 동적 필드 저장 │
|
||||
│ 2. 제품-양식 매핑: 품목코드 기반 검사양식 자동 선택 │
|
||||
│ 3. 기존 구조 활용: MNG DocumentTemplate 구조 그대로 사용 │
|
||||
│ 4. 결재 기능 보류: 결재요청/승인/반려는 기존 시스템 연동 예정 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.5 변경 승인 정책
|
||||
|
||||
| 분류 | 예시 | 승인 |
|
||||
|------|------|------|
|
||||
| ✅ 즉시 가능 | API 엔드포인트 추가, React 컴포넌트 수정 | 불필요 |
|
||||
| ⚠️ 컨펌 필요 | 테이블 컬럼 추가, 새 테이블 생성 | **필수** |
|
||||
| 🔴 금지 | 기존 테이블 구조 변경, documents 테이블 필드 삭제 | 별도 협의 |
|
||||
|
||||
### 1.6 준수 규칙
|
||||
|
||||
- `docs/reference/api-rules.md` - API 개발 규칙
|
||||
- `docs/specs/database-schema.md` - DB 스키마
|
||||
- `docs/guides/swagger-guide.md` - Swagger 문서화
|
||||
- `docs/reference/quality-checklist.md` - 품질 체크리스트
|
||||
|
||||
---
|
||||
|
||||
## 2. 대상 범위
|
||||
|
||||
### 2.1 Phase 1: MNG 문서양식 및 미리보기 (메인 작업) ⭐
|
||||
|
||||
| # | 작업 항목 | 상태 | 파일 | 비고 |
|
||||
|---|----------|:----:|------|------|
|
||||
| 1.1 | 수입검사 양식 템플릿 생성 | ⏳ | MNG UI | 1종 먼저 생성 (샘플) |
|
||||
| 1.2 | 미리보기 기능 확인 | ⏳ | edit.blade.php | 수입검사 성적서 양식 출력 |
|
||||
| 1.3 | 문서 생성 테스트 | ⏳ | MNG /documents/create | 템플릿 기반 문서 작성 |
|
||||
| 1.4 | **품목-양식 매핑 기능** | ⏳ | 신규 페이지 | 품목별 사용할 양식 연결 |
|
||||
| 1.5 | 추가 양식 생성 (필요시) | ⏳ | MNG UI | 20종 순차 생성 |
|
||||
|
||||
### 2.2 Phase 2: API 백엔드 (후속 작업)
|
||||
|
||||
| # | 작업 항목 | 상태 | 파일 | 비고 |
|
||||
|---|----------|:----:|------|------|
|
||||
| 2.1 | 검사 템플릿 조회 API | ⏳ | `InspectionTemplateController` | 제품별 검사항목 반환 |
|
||||
| 2.2 | 제품-양식 매핑 테이블 | ⏳ | 마이그레이션 | item_inspection_template_mappings |
|
||||
| 2.3 | 문서 생성/조회 API 확장 | ⏳ | `DocumentController` | linkable 연동 |
|
||||
|
||||
### 2.3 Phase 3: React 연동 (최종 작업)
|
||||
|
||||
| # | 작업 항목 | 상태 | 파일 | 비고 |
|
||||
|---|----------|:----:|------|------|
|
||||
| 3.1 | 검사항목 동적 로드 | ⏳ | `InspectionCreate.tsx` | API 연동 |
|
||||
| 3.2 | 검사 결과 저장/조회 | ⏳ | `InspectionCreate.tsx` | POST/GET /documents |
|
||||
|
||||
---
|
||||
|
||||
## 3. 작업 절차
|
||||
|
||||
### 3.1 Phase 1 작업 흐름 (MNG - 메인 작업)
|
||||
|
||||
```
|
||||
[Step 1: 문서양식 생성] (1종 샘플 먼저)
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ MNG /document-templates/create │
|
||||
│ │
|
||||
│ 예: "철제품 수입검사 성적서" 양식 생성 │
|
||||
│ │
|
||||
│ 1. 기본정보 탭 │
|
||||
│ - 양식명: 철제품 수입검사 성적서 │
|
||||
│ - 분류: 품질/수입검사 │
|
||||
│ - 문서 제목: 수입검사 성적서 │
|
||||
│ │
|
||||
│ 2. 결재라인 탭 │
|
||||
│ - 작성 (품질팀) → 검토 (품질팀장) → 승인 (공장장) │
|
||||
│ │
|
||||
│ 3. 검사 기준서 탭 │
|
||||
│ - 섹션: "검사 항목" │
|
||||
│ - 항목들 (철제품에 맞는 검사기준): │
|
||||
│ · 겉모양 - 외관 - 흠집,녹 없음 - 육안 │
|
||||
│ · 치수 - 두께 - ±0.1mm - 마이크로미터 │
|
||||
│ · 치수 - 폭 - ±1mm - 줄자 │
|
||||
│ · 재질 - 경도 - HRC 45-50 - 경도계 │
|
||||
│ │
|
||||
│ 4. 테이블 컬럼 탭 │
|
||||
│ - 구분, 항목, 규격, 방법, 판정, 비고 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
[Step 2: 미리보기 확인]
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 미리보기 버튼 클릭 │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ 철제품 수입검사 성적서 │ │
|
||||
│ │ (주)SAM │ │
|
||||
│ │ │ │
|
||||
│ │ 결재란: [작성] [검토] [승인] │ │
|
||||
│ │ │ │
|
||||
│ │ [검사 항목] │ │
|
||||
│ │ ┌──────┬──────┬──────────┬──────┬──────┬──────┐ │ │
|
||||
│ │ │ 구분 │ 항목 │ 규격 │ 방법 │ 판정 │ 비고 │ │ │
|
||||
│ │ ├──────┼──────┼──────────┼──────┼──────┼──────┤ │ │
|
||||
│ │ │겉모양│ 외관 │흠집,녹無 │ 육안 │ │ │ │ │
|
||||
│ │ │ 치수 │ 두께 │ ±0.1mm │마이크로│ │ │ │ │
|
||||
│ │ │ 치수 │ 폭 │ ±1mm │ 줄자 │ │ │ │ │
|
||||
│ │ │ 재질 │ 경도 │HRC 45-50│경도계│ │ │ │ │
|
||||
│ │ └──────┴──────┴──────────┴──────┴──────┴──────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ 종합 판정: □ 적합 □ 부적합 □ 조건부적합 │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ✅ 양식이 원하는 대로 출력되는지 확인 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
[Step 3: 문서 생성 테스트]
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ MNG /documents/create │
|
||||
│ │
|
||||
│ 1. 템플릿 선택: 철제품 수입검사 성적서 │
|
||||
│ 2. 제목 입력 │
|
||||
│ 3. 기본 필드 입력 (검사일, 검사자, LOT번호 등) │
|
||||
│ 4. 검사 항목별 판정 입력 │
|
||||
│ 5. 저장 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
[Step 4: 품목-양식 매핑 기능] ⭐ 신규
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ MNG /item-inspection-mappings (신규 페이지) │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ 품목-검사양식 매핑 │ │
|
||||
│ │ │ │
|
||||
│ │ [양식 선택] 철제품 수입검사 성적서 ▼ │ │
|
||||
│ │ │ │
|
||||
│ │ 연결된 품목: │ │
|
||||
│ │ ┌──────────┬──────────────┬────────┐ │ │
|
||||
│ │ │ 품목코드 │ 품목명 │ 해제 │ │ │
|
||||
│ │ ├──────────┼──────────────┼────────┤ │ │
|
||||
│ │ │ A001 │ 가이드레일 │ X │ │ │
|
||||
│ │ │ A002 │ 브라켓 │ X │ │ │
|
||||
│ │ │ A003 │ 철판 1.0t │ X │ │ │
|
||||
│ │ └──────────┴──────────────┴────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ [+ 품목 추가] │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ → 품목 선택 시 해당 양식의 검사항목으로 검사 진행 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
[Step 5: 추가 양식 생성] (필요시)
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 같은 방식으로 나머지 양식 생성: │
|
||||
│ │
|
||||
│ - 도장품 수입검사 성적서 (도막두께, 밀착력, 색상...) │
|
||||
│ - 플라스틱 수입검사 성적서 (외관, 치수, 강도...) │
|
||||
│ - 원자재 수입검사 성적서 (성적서 확인, 치수...) │
|
||||
│ - ... (총 20종) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 Phase 2-3 데이터 흐름 (후속 작업)
|
||||
|
||||
> Phase 1 완료 후 진행
|
||||
|
||||
### 3.2 API 스펙
|
||||
|
||||
#### API 1: 검사 템플릿 조회
|
||||
|
||||
```
|
||||
GET /api/v1/inspection-templates
|
||||
|
||||
Query Parameters:
|
||||
- item_code: string (선택) - 품목코드로 매핑된 템플릿 조회
|
||||
- category: string (선택) - 카테고리로 필터링
|
||||
|
||||
Response 200:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": 1,
|
||||
"name": "수입검사 성적서",
|
||||
"category": "품질",
|
||||
"title": "수입검사 성적서",
|
||||
"basic_fields": [
|
||||
{ "id": 1, "label": "검사일", "field_type": "date", "is_required": true },
|
||||
{ "id": 2, "label": "검사자", "field_type": "text", "is_required": true },
|
||||
{ "id": 3, "label": "LOT번호", "field_type": "text", "is_required": true }
|
||||
],
|
||||
"sections": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "철제품 검사",
|
||||
"image_path": null,
|
||||
"items": [
|
||||
{
|
||||
"id": 101,
|
||||
"category": "겉모양",
|
||||
"item": "외관",
|
||||
"standard": "이상 없음",
|
||||
"method": "육안",
|
||||
"frequency": "전수",
|
||||
"regulation": "사내규격"
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"category": "치수",
|
||||
"item": "두께",
|
||||
"standard": "1.0±0.1mm",
|
||||
"method": "계측",
|
||||
"frequency": "샘플링",
|
||||
"regulation": "KS D 3503"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"columns": [
|
||||
{ "id": 1, "label": "검사항목", "width": "150px", "column_type": "text" },
|
||||
{ "id": 2, "label": "규격", "width": "200px", "column_type": "text" },
|
||||
{ "id": 3, "label": "검사방법", "width": "100px", "column_type": "text" },
|
||||
{ "id": 4, "label": "판정", "width": "100px", "column_type": "select" },
|
||||
{ "id": 5, "label": "비고", "width": "200px", "column_type": "text" }
|
||||
],
|
||||
"footer_judgement_options": ["적합", "부적합", "조건부적합"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### API 2: 문서 생성 (수입검사 결과 저장)
|
||||
|
||||
```
|
||||
POST /api/v1/documents
|
||||
|
||||
Request Body:
|
||||
{
|
||||
"template_id": 1,
|
||||
"title": "수입검사 성적서 - A001 가이드레일",
|
||||
"linkable_type": "App\\Models\\Receiving",
|
||||
"linkable_id": 5,
|
||||
"data": {
|
||||
"basic_fields": {
|
||||
"inspection_date": "2025-01-28",
|
||||
"inspector": "김철수",
|
||||
"lot_no": "250128-01"
|
||||
},
|
||||
"section_items": [
|
||||
{
|
||||
"section_id": 1,
|
||||
"item_id": 101,
|
||||
"judgment": "적합",
|
||||
"remark": ""
|
||||
},
|
||||
{
|
||||
"section_id": 1,
|
||||
"item_id": 102,
|
||||
"judgment": "적합",
|
||||
"remark": "측정값: 0.98mm"
|
||||
}
|
||||
],
|
||||
"overall_judgment": "적합",
|
||||
"opinion": "전 항목 적합 판정"
|
||||
}
|
||||
}
|
||||
|
||||
Response 201:
|
||||
{
|
||||
"success": true,
|
||||
"message": "문서가 저장되었습니다.",
|
||||
"data": {
|
||||
"id": 100,
|
||||
"document_no": "IQC-20250128-0001",
|
||||
"status": "DRAFT"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 DB 스키마 추가
|
||||
|
||||
#### 제품-검사양식 매핑 테이블
|
||||
|
||||
```sql
|
||||
CREATE TABLE item_inspection_template_mappings (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
item_id BIGINT UNSIGNED NOT NULL, -- items.id
|
||||
template_id BIGINT UNSIGNED NOT NULL, -- document_templates.id
|
||||
priority INT DEFAULT 0, -- 우선순위 (높을수록 우선)
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
|
||||
FOREIGN KEY (tenant_id) REFERENCES tenants(id),
|
||||
FOREIGN KEY (item_id) REFERENCES items(id),
|
||||
FOREIGN KEY (template_id) REFERENCES document_templates(id),
|
||||
UNIQUE KEY unique_item_template (tenant_id, item_id, template_id)
|
||||
);
|
||||
```
|
||||
|
||||
### 3.4 React 컴포넌트 수정
|
||||
|
||||
#### InspectionCreate.tsx 변경 사항
|
||||
|
||||
```typescript
|
||||
// 기존 (하드코딩)
|
||||
const defaultInspectionItems: InspectionCheckItem[] = [
|
||||
{ id: '1', name: '겉모양', specification: '외관 이상 없음', method: '육안', judgment: '' },
|
||||
{ id: '2', name: '두께', specification: 't 1.0', method: '계측', judgment: '' },
|
||||
// ...
|
||||
];
|
||||
|
||||
// 변경 후 (동적 로드)
|
||||
const [template, setTemplate] = useState<InspectionTemplate | null>(null);
|
||||
const [inspectionItems, setInspectionItems] = useState<InspectionItem[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedTarget?.itemCode) {
|
||||
loadInspectionTemplate(selectedTarget.itemCode);
|
||||
}
|
||||
}, [selectedTarget]);
|
||||
|
||||
const loadInspectionTemplate = async (itemCode: string) => {
|
||||
const response = await fetch(`/api/v1/inspection-templates?item_code=${itemCode}`);
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
setTemplate(result.data);
|
||||
// 섹션의 아이템들을 평탄화하여 검사항목 배열 생성
|
||||
const items = result.data.sections.flatMap(section =>
|
||||
section.items.map(item => ({
|
||||
...item,
|
||||
section_id: section.id,
|
||||
judgment: '',
|
||||
remark: ''
|
||||
}))
|
||||
);
|
||||
setInspectionItems(items);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 상세 작업 내용
|
||||
|
||||
### 4.1 Phase 1: MNG 문서양식 및 미리보기 (메인 작업) ⭐
|
||||
|
||||
#### 1.1 수입검사 양식 템플릿 생성
|
||||
|
||||
MNG `/document-templates` 페이지에서 수입검사 성적서 양식 생성:
|
||||
|
||||
**양식 구조:**
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ [상단 고정] │
|
||||
│ ├─ 문서 제목: 수입검사 성적서 │
|
||||
│ ├─ 회사명, 문서번호, 작성일 │
|
||||
│ └─ 결재란 (작성 → 검토 → 승인) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ [기본 정보] │
|
||||
│ ├─ 품목코드, 품목명, 규격 │
|
||||
│ ├─ 공급업체, 입고수량, 입고일 │
|
||||
│ ├─ 검사일, 검사자, LOT번호 │
|
||||
│ └─ 발주번호, PO번호 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ [검사 항목 테이블] ← 동적 (20종) │
|
||||
│ ┌──────┬──────┬──────┬──────┬──────┬──────┐ │
|
||||
│ │ 구분 │ 항목 │ 규격 │ 방법 │ 판정 │ 비고 │ │
|
||||
│ ├──────┼──────┼──────┼──────┼──────┼──────┤ │
|
||||
│ │겉모양│ 외관 │이상無│ 육안 │ 적합 │ │ │
|
||||
│ │ 치수 │ 두께 │1.0mm │ 계측 │ 적합 │0.98mm│ │
|
||||
│ │ 치수 │ 폭 │1000mm│ 계측 │ 적합 │ │ │
|
||||
│ └──────┴──────┴──────┴──────┴──────┴──────┘ │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ [하단] │
|
||||
│ ├─ 종합 판정: ○ 적합 / ○ 부적합 / ○ 조건부적합 │
|
||||
│ └─ 비고 (종합 의견) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**MNG에서 설정할 항목:**
|
||||
|
||||
1. **기본정보 탭**
|
||||
- 양식명: 수입검사 성적서
|
||||
- 분류: 품질
|
||||
- 문서 제목: 수입검사 성적서
|
||||
|
||||
2. **결재라인 탭**
|
||||
- 작성 (품질팀)
|
||||
- 검토 (품질팀장)
|
||||
- 승인 (공장장)
|
||||
|
||||
3. **검사 기준서 탭** (섹션 + 항목)
|
||||
- 섹션: "검사 항목"
|
||||
- 항목들 (20종 예시):
|
||||
|
||||
| 구분 | 검사항목 | 검사기준 | 검사방법 | 검사주기 | 관련규정 |
|
||||
|------|---------|---------|---------|---------|---------|
|
||||
| 겉모양 | 외관 | 흠집, 녹 없음 | 육안 | 전수 | 사내규격 |
|
||||
| 치수 | 두께 | ±0.1mm | 마이크로미터 | 샘플링 | KS D 3503 |
|
||||
| 치수 | 폭 | ±1mm | 줄자 | 샘플링 | KS D 3503 |
|
||||
| 치수 | 길이 | ±2mm | 줄자 | 샘플링 | KS D 3503 |
|
||||
| 재질 | 경도 | HRC 45-50 | 경도계 | 샘플링 | ASTM E18 |
|
||||
| 도막 | 두께 | 60±10μm | 도막계 | 샘플링 | KS M 5000 |
|
||||
| 도막 | 밀착력 | 5B 이상 | 크로스컷 | 샘플링 | ASTM D3359 |
|
||||
| 외관 | 색상 | 표준색상 | 색차계 | 전수 | 사내규격 |
|
||||
| ... | ... | ... | ... | ... | ... |
|
||||
|
||||
4. **테이블 컬럼 탭**
|
||||
- 구분 (text, 80px)
|
||||
- 검사항목 (text, 100px)
|
||||
- 검사기준 (text, 150px)
|
||||
- 검사방법 (text, 100px)
|
||||
- 판정 (select: 적합/부적합, 100px)
|
||||
- 비고 (text, 150px)
|
||||
|
||||
#### 1.2 검사항목 섹션 구성
|
||||
|
||||
현재 document-templates의 섹션 구조가 수입검사에 맞는지 확인하고 조정:
|
||||
|
||||
**확인 사항:**
|
||||
- `document_template_sections`: 섹션(검사 항목 그룹)
|
||||
- `document_template_section_items`: 개별 검사 항목
|
||||
- 필드: category, item, standard, method, frequency, regulation
|
||||
|
||||
#### 1.3 문서 생성 테스트
|
||||
|
||||
MNG `/documents/create`에서:
|
||||
1. 수입검사 성적서 템플릿 선택
|
||||
2. 기본 정보 입력 (품목, 검사일, 검사자 등)
|
||||
3. 검사 항목별 판정 입력
|
||||
4. 저장
|
||||
|
||||
#### 1.4 미리보기 기능 구현/확인
|
||||
|
||||
`document-templates/edit.blade.php`의 미리보기 모달이 수입검사 성적서 양식을 제대로 출력하는지 확인:
|
||||
|
||||
**미리보기 출력 형태:**
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 수입검사 성적서 │
|
||||
│ (주)SAM │
|
||||
│ │
|
||||
│ 결재 ┌────┬────┬────┐ │
|
||||
│ │작성│검토│승인│ │
|
||||
│ ├────┼────┼────┤ │
|
||||
│ │ │ │ │ │
|
||||
│ └────┴────┴────┘ │
|
||||
│ │
|
||||
│ [기본 정보] │
|
||||
│ 품목코드: A001 품목명: 가이드레일 │
|
||||
│ 검사일: 2025-01-28 검사자: 김철수 │
|
||||
│ LOT번호: 250128-01 │
|
||||
│ │
|
||||
│ [검사 항목] │
|
||||
│ ┌──────┬──────┬──────────┬──────┬──────┬──────┐ │
|
||||
│ │ 구분 │ 항목 │ 규격 │ 방법 │ 판정 │ 비고 │ │
|
||||
│ ├──────┼──────┼──────────┼──────┼──────┼──────┤ │
|
||||
│ │겉모양│ 외관 │흠집,녹無 │ 육안 │ │ │ │
|
||||
│ │ 치수 │ 두께 │ ±0.1mm │ 계측 │ │ │ │
|
||||
│ │ 치수 │ 폭 │ ±1mm │ 계측 │ │ │ │
|
||||
│ └──────┴──────┴──────────┴──────┴──────┴──────┘ │
|
||||
│ │
|
||||
│ 종합 판정: □ 적합 □ 부적합 □ 조건부적합 │
|
||||
│ 비고: │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4.2 Phase 2: API 백엔드 (후속 작업)
|
||||
|
||||
> Phase 1 완료 후 진행
|
||||
|
||||
- 검사 템플릿 조회 API
|
||||
- 제품-양식 매핑 테이블
|
||||
- 문서 생성/조회 API 확장
|
||||
|
||||
### 4.3 Phase 3: React 연동 (최종 작업)
|
||||
|
||||
> Phase 2 완료 후 진행
|
||||
|
||||
- 검사항목 동적 로드
|
||||
- 검사 결과 저장/조회
|
||||
|
||||
---
|
||||
|
||||
## 5. 컨펌 대기 목록
|
||||
|
||||
| # | 항목 | 변경 내용 | 영향 범위 | 상태 |
|
||||
|---|------|----------|----------|------|
|
||||
| 1 | 수입검사 템플릿 구조 | 기본정보 + 검사항목 20종 구성 | mng/document-templates | ⏳ 대기 |
|
||||
| 2 | 미리보기 출력 형식 | 성적서 양식 레이아웃 | mng/edit.blade.php | ⏳ 대기 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 변경 이력
|
||||
|
||||
| 날짜 | 항목 | 변경 내용 | 파일 | 승인 |
|
||||
|------|------|----------|------|------|
|
||||
| 2025-01-28 | - | 계획 문서 초안 작성 | - | - |
|
||||
|
||||
---
|
||||
|
||||
## 7. 참고 문서
|
||||
|
||||
- **문서관리 시스템 계획**: `docs/plans/document-management-system-plan.md`
|
||||
- **API 규칙**: `docs/reference/api-rules.md`
|
||||
- **DB 스키마**: `docs/specs/database-schema.md`
|
||||
- **품질 체크리스트**: `docs/reference/quality-checklist.md`
|
||||
|
||||
---
|
||||
|
||||
## 8. 세션 및 메모리 관리 정책
|
||||
|
||||
### 8.1 세션 시작 시
|
||||
```javascript
|
||||
read_memory("inspection-document-state")
|
||||
read_memory("inspection-document-snapshot")
|
||||
```
|
||||
|
||||
### 8.2 Serena 메모리 구조
|
||||
- `inspection-document-state`: { phase, progress, next_step }
|
||||
- `inspection-document-snapshot`: 코드 변경점 및 논의 요약
|
||||
|
||||
---
|
||||
|
||||
## 9. 검증 결과
|
||||
|
||||
### 9.1 테스트 케이스 (Phase 1)
|
||||
|
||||
| 입력값 | 예상 결과 | 실제 결과 | 상태 |
|
||||
|--------|----------|----------|------|
|
||||
| MNG에서 수입검사 템플릿 생성 | 기본정보 + 20종 검사항목 저장 | - | ⏳ |
|
||||
| 템플릿 미리보기 클릭 | 성적서 양식 출력 | - | ⏳ |
|
||||
| MNG에서 문서 생성 | 템플릿 기반 문서 작성 가능 | - | ⏳ |
|
||||
| 문서 상세 보기 | 입력 데이터 표시 | - | ⏳ |
|
||||
|
||||
### 9.2 성공 기준 달성 현황
|
||||
|
||||
| 기준 | 달성 | 비고 |
|
||||
|------|------|------|
|
||||
| MNG 템플릿 생성 (20종 검사항목) | ⏳ | Phase 1.1-1.2 |
|
||||
| 미리보기 성적서 양식 출력 | ⏳ | Phase 1.4 |
|
||||
| MNG 문서 생성/조회 | ⏳ | Phase 1.3 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 자기완결성 점검 결과
|
||||
|
||||
### 10.1 체크리스트 검증
|
||||
|
||||
| # | 검증 항목 | 상태 | 비고 |
|
||||
|---|----------|:----:|------|
|
||||
| 1 | 작업 목적이 명확한가? | ✅ | 섹션 1.1 |
|
||||
| 2 | 성공 기준이 정의되어 있는가? | ✅ | 섹션 9.2 |
|
||||
| 3 | 작업 범위가 구체적인가? | ✅ | 섹션 2 |
|
||||
| 4 | 의존성이 명시되어 있는가? | ✅ | 섹션 1.6, 7 |
|
||||
| 5 | 참고 파일 경로가 정확한가? | ✅ | 섹션 3, 4 |
|
||||
| 6 | 단계별 절차가 실행 가능한가? | ✅ | 섹션 3, 4 |
|
||||
| 7 | 검증 방법이 명시되어 있는가? | ✅ | 섹션 9.1 |
|
||||
| 8 | 모호한 표현이 없는가? | ✅ | API 스펙 구체화 |
|
||||
|
||||
### 10.2 새 세션 시뮬레이션 테스트
|
||||
|
||||
| 질문 | 답변 가능 | 참조 섹션 |
|
||||
|------|:--------:|----------|
|
||||
| Q1. 이 작업의 목적은 무엇인가? | ✅ | 1.1 배경 |
|
||||
| Q2. 어디서부터 시작해야 하는가? | ✅ | 2.1 Phase 1 |
|
||||
| Q3. 어떤 파일을 수정해야 하는가? | ✅ | 4. 상세 작업 |
|
||||
| Q4. 작업 완료 확인 방법은? | ✅ | 9. 검증 결과 |
|
||||
| Q5. 막혔을 때 참고 문서는? | ✅ | 7. 참고 문서 |
|
||||
|
||||
**결과**: 5/5 통과 → ✅ 자기완결성 확보
|
||||
|
||||
---
|
||||
|
||||
*이 문서는 /plan 스킬로 생성되었습니다.*
|
||||
1399
plans/items-migration-kyungdong-plan.md
Normal file
1399
plans/items-migration-kyungdong-plan.md
Normal file
File diff suppressed because it is too large
Load Diff
825
plans/kd-orders-migration-plan.md
Normal file
825
plans/kd-orders-migration-plan.md
Normal file
@@ -0,0 +1,825 @@
|
||||
# 경동기업(5130) 입고/재고/주문 마이그레이션 계획
|
||||
|
||||
> **작성일**: 2026-01-28
|
||||
> **목적**: 경동기업 레거시 시스템(5130/)의 **입고(instock), 재고(stocks), 주문(output)** 데이터를 SAM으로 이관
|
||||
> **기준 문서**: `5130/` 폴더 분석 결과
|
||||
> **상태**: ⏳ 대기 (품목 마이그레이션 선행 필요)
|
||||
> **데이터 규모**: ~78,000 레코드 (입고 2,286 + 재고 ~500 + 주문 75,000+)
|
||||
> **선행 조건**: `kd-items-migration-plan.md` 완료 필수
|
||||
|
||||
---
|
||||
|
||||
## 🚀 새 세션 시작 가이드 (Quick Start)
|
||||
|
||||
### 이 문서만 보고 작업을 재개하려면:
|
||||
|
||||
```bash
|
||||
# 1. Docker 서비스 확인
|
||||
docker ps | grep sam
|
||||
|
||||
# 2. 선행 조건 확인 (items 마이그레이션 완료 여부)
|
||||
docker exec sam-mysql-1 mysql -uroot -proot samdb -e "SELECT COUNT(*) FROM items WHERE tenant_id=287;"
|
||||
# → 최소 600건 이상이어야 함
|
||||
|
||||
# 3. 레거시 DB 테스트
|
||||
docker exec sam-mysql-1 mysql -uroot -proot chandj -e "SELECT COUNT(*) FROM output;"
|
||||
|
||||
# 4. 현재 진행 상태 확인
|
||||
# → 아래 "📍 현재 진행 상태" 섹션 참조
|
||||
```
|
||||
|
||||
### 환경 정보
|
||||
|
||||
| 항목 | 값 |
|
||||
|------|-----|
|
||||
| **프로젝트 루트** | `/Users/kent/Works/@KD_SAM/SAM` |
|
||||
| **레거시 소스** | `5130/` (프로젝트 루트 직하) |
|
||||
| **API 프로젝트** | `api/` |
|
||||
| **Docker 컨테이너** | `sam-mysql-1` |
|
||||
| **레거시 DB** | `chandj` (MySQL) |
|
||||
| **SAM DB** | `samdb` (MySQL) ⚠️ |
|
||||
| **대상 테넌트 ID** | `287` (경동기업) |
|
||||
| **생성자 사용자 ID** | `1` |
|
||||
|
||||
### DB 접속 명령어
|
||||
|
||||
```bash
|
||||
# 레거시 DB (chandj) 접속
|
||||
docker exec -it sam-mysql-1 mysql -uroot -proot chandj
|
||||
|
||||
# SAM DB 접속
|
||||
docker exec -it sam-mysql-1 mysql -uroot -proot samdb
|
||||
|
||||
# 입고 기록 확인
|
||||
docker exec sam-mysql-1 mysql -uroot -proot chandj -e "SELECT COUNT(*) FROM instock;"
|
||||
|
||||
# 주문 기록 확인
|
||||
docker exec sam-mysql-1 mysql -uroot -proot chandj -e "SELECT COUNT(*) FROM output;"
|
||||
```
|
||||
|
||||
### 전제 조건 (작업 전 확인)
|
||||
|
||||
- [x] Docker 서비스 실행 중
|
||||
- [x] `sam-mysql-1` 컨테이너 실행 중
|
||||
- [x] chandj 데이터베이스 접근 가능
|
||||
- [ ] **⚠️ 품목 마이그레이션 완료** (`kd-items-migration-plan.md`)
|
||||
- [ ] SAM orders 마이그레이션 실행 완료 (`php artisan migrate`)
|
||||
- [ ] SAM item_receipts 마이그레이션 실행 완료
|
||||
|
||||
---
|
||||
|
||||
## 📍 현재 진행 상태
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| **마지막 완료 작업** | 문서 분리 완료 (items + orders 분리) |
|
||||
| **다음 작업** | ⏳ 품목 마이그레이션 완료 대기 |
|
||||
| **진행률** | 0/2 (0%) - 대기 중 |
|
||||
| **마지막 업데이트** | 2026-01-28 |
|
||||
|
||||
### 시작 조건
|
||||
|
||||
**이 문서의 작업을 시작하기 전:**
|
||||
|
||||
1. ✅ `kd-items-migration-plan.md` Phase 1~4 완료
|
||||
2. ✅ SAM items 테이블에 ~800건 이상 존재
|
||||
3. ✅ SAM prices 테이블에 ~500건 이상 존재
|
||||
|
||||
```sql
|
||||
-- 시작 조건 확인 쿼리
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM items WHERE tenant_id=287) AS items_count,
|
||||
(SELECT COUNT(*) FROM prices WHERE tenant_id=287) AS prices_count;
|
||||
-- items_count >= 700, prices_count >= 400 이어야 시작 가능
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 0. 성공 기준
|
||||
|
||||
| 기준 | 목표값 | 확인 방법 |
|
||||
|------|-------|----------|
|
||||
| **item_receipts 합계** | **~2,300건** | `SELECT COUNT(*) FROM item_receipts WHERE tenant_id=287` |
|
||||
| **stocks 합계** | **~500건** | `SELECT COUNT(*) FROM stocks WHERE tenant_id=287` |
|
||||
| **lots 합계** | **~200건** | `SELECT COUNT(*) FROM lots WHERE tenant_id=287` |
|
||||
| **lot_sales 합계** | **~300건** | `SELECT COUNT(*) FROM lot_sales WHERE tenant_id=287` |
|
||||
| **orders 합계** | **~25,000건** | `SELECT COUNT(*) FROM orders WHERE tenant_id=287` |
|
||||
| **order_items 합계** | **~50,000건** | `SELECT COUNT(*) FROM order_items WHERE tenant_id=287` |
|
||||
| item_id 연결율 | 100% | `SELECT COUNT(*) FROM item_receipts WHERE item_id IS NULL` (0건) |
|
||||
| API 테스트 | 100% | `/api/v1/orders` 목록 조회 성공 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 개요
|
||||
|
||||
### 1.1 배경
|
||||
|
||||
경동기업 레거시 시스템의 **입고/재고/주문** 데이터를 SAM으로 이관. 이 작업은 **품목(items) 마이그레이션 완료 후** 진행해야 함 (item_id FK 참조 필요).
|
||||
|
||||
### 1.2 핵심 차이점
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────────────┐
|
||||
│ 레거시 (chandj) → SAM (samdb) │
|
||||
├────────────────────────────────────────────────────────────────────────────┤
|
||||
│ 📥 입고/재고 │
|
||||
│ ───────────────────────────────────────────────────────────────────────── │
|
||||
│ instock (2,286건) → item_receipts + stocks │
|
||||
│ lot, lot_sales → lots + lot_sales │
|
||||
│ │
|
||||
│ 📋 주문/출고 │
|
||||
│ ───────────────────────────────────────────────────────────────────────── │
|
||||
│ output (24,564건) → orders + order_items │
|
||||
│ output.iList (JSON 파일 참조) → orders.options │
|
||||
│ estimate → orders (type=견적) │
|
||||
└────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.3 output.iList JSON 파일 구조 ⭐
|
||||
|
||||
```sql
|
||||
-- output 테이블의 iList 컬럼
|
||||
-- 값: "../output/i_json/22545.json" (파일 경로!)
|
||||
-- 실제 파일 위치: 5130/output/i_json/{output_id}.json
|
||||
```
|
||||
|
||||
**JSON 파일 내용 예시 (5130/output/i_json/22545.json)**:
|
||||
```json
|
||||
{
|
||||
"inputValue": [
|
||||
"2024-12-03", // 날짜
|
||||
"명보에스티", // 거래처명
|
||||
"KWE01 전체적인 테스트", // 모델/설명
|
||||
// ... 추가 입력값들
|
||||
],
|
||||
"beforeWidth": ["8000", "7000"], // 변경전 폭
|
||||
"beforeHeight": ["4000", "3500"], // 변경전 높이
|
||||
"afterWidth": ["8000", "7000"], // 변경후 폭
|
||||
"afterHeight": ["4000", "3500"], // 변경후 높이
|
||||
"pages": [
|
||||
{
|
||||
"page": "1",
|
||||
"inputItems": {
|
||||
"openWidth": "8000",
|
||||
"openHeight": "4000",
|
||||
// ... 기타 치수 정보
|
||||
},
|
||||
"checkboxData": [...]
|
||||
}
|
||||
],
|
||||
"approval": {
|
||||
"writer": {"name": "개발자", "date": "25/01/02"},
|
||||
"approver": {"name": "관리자", "date": "25/01/03"}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**SAM 매핑**:
|
||||
- `inputValue` → `orders.options` (JSON)
|
||||
- `pages` → `order_items.options` (JSON)
|
||||
- `approval` → `orders.approved_by`, `orders.approved_at`
|
||||
- `beforeWidth/Height`, `afterWidth/Height` → `order_items.options.dimensions`
|
||||
|
||||
---
|
||||
|
||||
## 2. 레거시 DB 구조 분석
|
||||
|
||||
### 2.1 핵심 테이블 및 레코드 수
|
||||
|
||||
#### 📥 입고/재고 테이블
|
||||
|
||||
| 테이블 | 레코드 수 | 역할 | SAM 매핑 |
|
||||
|--------|----------|------|----------|
|
||||
| **`instock`** ⭐ | **2,286** | 입고 기록 | item_receipts + stocks |
|
||||
| `lot` | ~200 | 로트 관리 | lots |
|
||||
| `lot_sales` | ~300 | 로트 소진 | lot_sales |
|
||||
|
||||
#### 📋 주문/출고 테이블
|
||||
|
||||
| 테이블 | 레코드 수 | 역할 | SAM 매핑 |
|
||||
|--------|----------|------|----------|
|
||||
| **`output`** ⭐ | **24,564** | 주문/출고 기록 | orders + order_items |
|
||||
| `estimate` | ~500 | 견적 | orders (type=견적) |
|
||||
|
||||
### 2.2 instock 테이블 구조 ⭐
|
||||
|
||||
```sql
|
||||
-- instock: 입고 기록 (2,286건)
|
||||
-- ⚠️ 실제 컬럼명 (2026-01-28 확인됨)
|
||||
num INT PRIMARY KEY, -- PK ⭐
|
||||
is_deleted INT, -- 삭제 여부
|
||||
item_name VARCHAR(255), -- 품목명
|
||||
prodcode VARCHAR(50), -- items.code와 매칭 ⭐
|
||||
iList TEXT, -- 관련 정보 (JSON?)
|
||||
lot_no VARCHAR(100), -- 로트번호
|
||||
lotDone INT, -- 로트 완료 여부
|
||||
inspection_date DATE, -- 검수일 (입고일로 사용) ⭐
|
||||
supplier VARCHAR(255), -- 공급업체
|
||||
specification VARCHAR(255), -- 규격
|
||||
unit VARCHAR(20), -- 단위
|
||||
received_qty DECIMAL, -- 입고 수량 ⭐
|
||||
material_no VARCHAR(100), -- 자재번호
|
||||
manufacturer VARCHAR(255), -- 제조사
|
||||
remarks TEXT, -- 비고 ⭐
|
||||
purchase_price_excl_vat DECIMAL, -- 단가 (부가세 제외) ⭐
|
||||
weight_kg DECIMAL, -- 중량
|
||||
searchtag TEXT, -- 검색 태그
|
||||
update_log TEXT -- 변경 이력
|
||||
```
|
||||
|
||||
### 2.3 output 테이블 구조 ⭐
|
||||
|
||||
```sql
|
||||
-- output: 주문/출고 기록 (24,564건)
|
||||
-- ⚠️ 실제 컬럼명 (2026-01-28 확인됨) - 70+ 컬럼 중 주요 컬럼만 표시
|
||||
num INT PRIMARY KEY, -- PK ⭐ (output_id 대신)
|
||||
secondordnum VARCHAR(50), -- 2차 주문번호
|
||||
iList VARCHAR(255), -- JSON 파일 경로 (../output/i_json/xxx.json) ⭐
|
||||
COD VARCHAR(50), -- COD 코드
|
||||
con_num VARCHAR(50), -- 계약번호
|
||||
is_deleted INT, -- 삭제 여부
|
||||
outdate DATE, -- 출고일 (order_date 대신) ⭐
|
||||
indate DATE, -- 입고일/등록일
|
||||
outworkplace VARCHAR(255), -- 출고처/거래처 ⭐
|
||||
orderman VARCHAR(100), -- 주문자
|
||||
outputplace VARCHAR(255), -- 출력처
|
||||
receiver VARCHAR(100), -- 수령자
|
||||
phone VARCHAR(50), -- 전화번호
|
||||
comment TEXT, -- 비고 (memo 대신) ⭐
|
||||
-- ... 이하 70+ 컬럼 (상세 분석 필요)
|
||||
-- 참고: 전체 컬럼 목록 확인 필요
|
||||
-- docker exec sam-mysql-1 mysql -uroot -proot chandj -e "DESCRIBE output;"
|
||||
```
|
||||
|
||||
**output 테이블 전체 컬럼 확인 명령:**
|
||||
```bash
|
||||
docker exec sam-mysql-1 mysql -uroot -proot chandj -e "DESCRIBE output;" | head -80
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. SAM 테이블 구조 (Target)
|
||||
|
||||
### 3.1 item_receipts 테이블
|
||||
|
||||
```sql
|
||||
CREATE TABLE item_receipts (
|
||||
id BIGINT PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL, -- 287 (경동기업)
|
||||
item_id BIGINT NOT NULL, -- items.id FK ⭐
|
||||
receipt_date DATE NOT NULL, -- 입고일
|
||||
quantity DECIMAL(15,4) NOT NULL, -- 수량
|
||||
unit_price DECIMAL(15,4), -- 단가
|
||||
total_amount DECIMAL(15,4), -- 금액
|
||||
supplier_id BIGINT, -- 공급업체 ID
|
||||
lot_id BIGINT, -- 로트 ID
|
||||
note TEXT,
|
||||
created_by, updated_by, deleted_by, timestamps, soft_deletes
|
||||
);
|
||||
```
|
||||
|
||||
### 3.2 stocks 테이블
|
||||
|
||||
```sql
|
||||
CREATE TABLE stocks (
|
||||
id BIGINT PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
item_id BIGINT NOT NULL, -- items.id FK
|
||||
warehouse_id BIGINT, -- 창고 ID
|
||||
quantity DECIMAL(15,4) NOT NULL, -- 현재고
|
||||
reserved_qty DECIMAL(15,4) DEFAULT 0, -- 예약수량
|
||||
available_qty DECIMAL(15,4), -- 가용재고
|
||||
last_movement_at TIMESTAMP,
|
||||
created_by, updated_by, timestamps
|
||||
);
|
||||
```
|
||||
|
||||
### 3.3 orders 테이블
|
||||
|
||||
```sql
|
||||
CREATE TABLE orders (
|
||||
id BIGINT PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
order_no VARCHAR(50) NOT NULL, -- 주문번호
|
||||
order_type VARCHAR(20) NOT NULL, -- 주문/견적
|
||||
order_date DATE NOT NULL,
|
||||
delivery_date DATE,
|
||||
client_id BIGINT, -- 거래처 ID
|
||||
status VARCHAR(30), -- 상태
|
||||
total_amount DECIMAL(15,4),
|
||||
options JSON, -- iList JSON 데이터 ⭐
|
||||
approved_by BIGINT,
|
||||
approved_at TIMESTAMP,
|
||||
note TEXT,
|
||||
created_by, updated_by, deleted_by, timestamps, soft_deletes
|
||||
);
|
||||
```
|
||||
|
||||
### 3.4 order_items 테이블
|
||||
|
||||
```sql
|
||||
CREATE TABLE order_items (
|
||||
id BIGINT PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
order_id BIGINT NOT NULL, -- orders.id FK
|
||||
item_id BIGINT, -- items.id FK (nullable - 신규품목 가능)
|
||||
seq_no INT NOT NULL, -- 순번
|
||||
item_code VARCHAR(100),
|
||||
item_name VARCHAR(255),
|
||||
quantity DECIMAL(15,4) NOT NULL,
|
||||
unit_price DECIMAL(15,4),
|
||||
amount DECIMAL(15,4),
|
||||
options JSON, -- pages[n] JSON 데이터 ⭐
|
||||
note TEXT,
|
||||
created_by, updated_by, timestamps
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 대상 범위
|
||||
|
||||
### 4.1 Phase 5: 입고/재고 데이터 이관 ⭐
|
||||
|
||||
| # | 작업 항목 | 상태 | 비고 |
|
||||
|---|----------|:----:|------|
|
||||
| 5.1 | instock 테이블 구조 분석 | ⏳ | 컬럼 확인 필요 |
|
||||
| 5.2 | instock → item_receipts 매핑 설계 | ⏳ | item_code → item_id |
|
||||
| 5.3 | instock → item_receipts INSERT | ⏳ | 2,286건 |
|
||||
| 5.4 | instock 재고 집계 → stocks | ⏳ | 품목별 현재고 |
|
||||
| 5.5 | lot → lots | ⏳ | 로트 관리 |
|
||||
| 5.6 | lot_sales → lot_sales | ⏳ | 로트 소진 |
|
||||
| 5.7 | ⚠️ **사용자 승인**: 입고/재고 INSERT 실행 | ⏳ | |
|
||||
|
||||
### 4.2 Phase 6: 주문/출고 데이터 이관 ⭐
|
||||
|
||||
| # | 작업 항목 | 상태 | 비고 |
|
||||
|---|----------|:----:|------|
|
||||
| 6.1 | output 테이블 구조 분석 | ⏳ | 컬럼 확인 필요 |
|
||||
| 6.2 | output → orders 헤더 INSERT | ⏳ | 24,564건 |
|
||||
| 6.3 | output.iList JSON 파일 파싱 | ⏳ | 파일 경로 → JSON 읽기 |
|
||||
| 6.4 | JSON → order_items 생성 | ⏳ | pages 배열 처리 |
|
||||
| 6.5 | JSON.approval → orders 승인 정보 | ⏳ | approved_by, approved_at |
|
||||
| 6.6 | estimate → orders (type=견적) | ⏳ | 견적 데이터 |
|
||||
| 6.7 | ⚠️ **사용자 승인**: 주문/출고 INSERT 실행 | ⏳ | |
|
||||
|
||||
---
|
||||
|
||||
## 5. SQL 쿼리 / 스크립트
|
||||
|
||||
### 5.1 instock → item_receipts
|
||||
|
||||
```sql
|
||||
-- 입고 데이터 이관 (prodcode로 item_id 조회)
|
||||
-- ⚠️ 실제 컬럼명 사용 (2026-01-28 확인됨)
|
||||
INSERT INTO samdb.item_receipts (
|
||||
tenant_id, item_id, receipt_date, quantity,
|
||||
unit_price, total_amount, note,
|
||||
created_by, created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
287 AS tenant_id,
|
||||
i.id AS item_id,
|
||||
ins.inspection_date AS receipt_date, -- ⭐ inspection_date 사용
|
||||
ins.received_qty AS quantity, -- ⭐ received_qty 사용
|
||||
ins.purchase_price_excl_vat AS unit_price, -- ⭐ purchase_price_excl_vat 사용
|
||||
(ins.received_qty * COALESCE(ins.purchase_price_excl_vat, 0)) AS total_amount, -- 계산
|
||||
CONCAT_WS(' | ',
|
||||
ins.remarks,
|
||||
CONCAT('supplier:', ins.supplier),
|
||||
CONCAT('manufacturer:', ins.manufacturer),
|
||||
CONCAT('material_no:', ins.material_no)
|
||||
) AS note, -- ⭐ remarks + 추가 정보
|
||||
1 AS created_by,
|
||||
NOW(), NOW()
|
||||
FROM chandj.instock ins
|
||||
JOIN samdb.items i ON i.code = ins.prodcode AND i.tenant_id = 287 -- ⭐ prodcode 사용
|
||||
WHERE ins.is_deleted = 0
|
||||
AND ins.prodcode IS NOT NULL AND ins.prodcode != '';
|
||||
|
||||
-- 결과 확인
|
||||
SELECT COUNT(*) FROM samdb.item_receipts WHERE tenant_id = 287;
|
||||
|
||||
-- item_id 연결 실패 레코드 확인
|
||||
SELECT ins.prodcode, ins.item_name, COUNT(*) AS cnt
|
||||
FROM chandj.instock ins
|
||||
LEFT JOIN samdb.items i ON i.code = ins.prodcode AND i.tenant_id = 287
|
||||
WHERE ins.is_deleted = 0 AND i.id IS NULL
|
||||
GROUP BY ins.prodcode, ins.item_name;
|
||||
```
|
||||
|
||||
### 5.2 재고 집계 → stocks
|
||||
|
||||
```sql
|
||||
-- 입고 데이터 기반 현재고 집계
|
||||
INSERT INTO samdb.stocks (
|
||||
tenant_id, item_id, quantity, available_qty,
|
||||
last_movement_at, created_by, created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
287 AS tenant_id,
|
||||
ir.item_id,
|
||||
SUM(ir.quantity) AS quantity,
|
||||
SUM(ir.quantity) AS available_qty,
|
||||
MAX(ir.receipt_date) AS last_movement_at,
|
||||
1 AS created_by,
|
||||
NOW(), NOW()
|
||||
FROM samdb.item_receipts ir
|
||||
WHERE ir.tenant_id = 287
|
||||
GROUP BY ir.item_id;
|
||||
```
|
||||
|
||||
### 5.3 output → orders + order_items [PHP 스크립트]
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* output → orders + order_items 마이그레이션 * ⚠️ 실제 컬럼명 사용 (2026-01-28 확인됨)
|
||||
*
|
||||
* 1단계: output 레코드 → orders 헤더 생성
|
||||
* 2단계: iList JSON 파일 파싱 → order_items 생성
|
||||
*/
|
||||
|
||||
$tenantId = 287;
|
||||
$userId = 1;
|
||||
$basePath = '/Users/kent/Works/@KD_SAM/SAM/5130';
|
||||
|
||||
// output 레코드 조회 (실제 컬럼명 사용)
|
||||
$stmt = $pdo->query("
|
||||
SELECT num, secondordnum, iList, COD, con_num,
|
||||
outdate, indate, outworkplace, orderman,
|
||||
outputplace, receiver, phone, comment
|
||||
FROM output
|
||||
WHERE is_deleted = 0
|
||||
ORDER BY num
|
||||
");
|
||||
$outputs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$orderCount = 0;
|
||||
$itemCount = 0;
|
||||
|
||||
foreach ($outputs as $output) {
|
||||
// 1단계: orders INSERT
|
||||
// ⭐ num을 사용 (output_id 대신)
|
||||
$orderNo = 'ORD-' . str_pad($output['num'], 8, '0', STR_PAD_LEFT);
|
||||
|
||||
// iList JSON 파일 읽기
|
||||
$iListPath = $output['iList']; // "../output/i_json/22545.json"
|
||||
if (empty($iListPath)) {
|
||||
continue; // iList 없으면 스킵
|
||||
}
|
||||
|
||||
$jsonFile = str_replace('../', '', $iListPath);
|
||||
$fullPath = $basePath . '/' . $jsonFile;
|
||||
|
||||
$options = null;
|
||||
$approvedBy = null;
|
||||
$approvedAt = null;
|
||||
$jsonContent = null;
|
||||
|
||||
if (file_exists($fullPath)) {
|
||||
$jsonContent = json_decode(file_get_contents($fullPath), true);
|
||||
|
||||
// options에 전체 JSON 저장
|
||||
$options = json_encode([
|
||||
'inputValue' => $jsonContent['inputValue'] ?? [],
|
||||
'beforeWidth' => $jsonContent['beforeWidth'] ?? [],
|
||||
'beforeHeight' => $jsonContent['beforeHeight'] ?? [],
|
||||
'afterWidth' => $jsonContent['afterWidth'] ?? [],
|
||||
'afterHeight' => $jsonContent['afterHeight'] ?? [],
|
||||
]);
|
||||
|
||||
// 승인 정보 추출
|
||||
if (isset($jsonContent['approval']['approver'])) {
|
||||
$approver = $jsonContent['approval']['approver'];
|
||||
// approver.name으로 사용자 ID 조회 필요
|
||||
$approvedAt = $approver['date'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
$orderStmt = $pdo->prepare("
|
||||
INSERT INTO orders (
|
||||
tenant_id, order_no, order_type, order_date, delivery_date,
|
||||
status, total_amount, options, approved_at, note,
|
||||
created_by, created_at, updated_at
|
||||
) VALUES (?, ?, 'order', ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
|
||||
");
|
||||
$orderStmt->execute([
|
||||
$tenantId,
|
||||
$orderNo,
|
||||
$output['outdate'], // ⭐ outdate 사용 (order_date 대신)
|
||||
$output['indate'], // ⭐ indate 사용 (delivery_date 대신?)
|
||||
'completed', // 상태 - output 테이블에서 확인 필요
|
||||
0, // total_amount - output 테이블에서 확인 필요
|
||||
$options,
|
||||
$approvedAt,
|
||||
$output['comment'], // ⭐ comment 사용 (memo 대신)
|
||||
$userId,
|
||||
]);
|
||||
$orderId = $pdo->lastInsertId();
|
||||
$orderCount++;
|
||||
|
||||
// 2단계: order_items INSERT (pages 배열 처리)
|
||||
if ($jsonContent && isset($jsonContent['pages']) && is_array($jsonContent['pages'])) {
|
||||
foreach ($jsonContent['pages'] as $seqNo => $page) {
|
||||
$itemOptions = json_encode([
|
||||
'inputItems' => $page['inputItems'] ?? [],
|
||||
'checkboxData' => $page['checkboxData'] ?? [],
|
||||
]);
|
||||
|
||||
$itemStmt = $pdo->prepare("
|
||||
INSERT INTO order_items (
|
||||
tenant_id, order_id, seq_no, item_code, item_name,
|
||||
quantity, options,
|
||||
created_by, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, 1, ?, ?, NOW(), NOW())
|
||||
");
|
||||
$itemStmt->execute([
|
||||
$tenantId,
|
||||
$orderId,
|
||||
$seqNo + 1,
|
||||
null, // item_code - JSON에서 추출 필요
|
||||
$output['outworkplace'] ?? '', // ⭐ outworkplace 사용 (거래처명)
|
||||
$itemOptions,
|
||||
$userId
|
||||
]);
|
||||
$itemCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($orderCount % 1000 === 0) {
|
||||
echo "진행중: {$orderCount} orders, {$itemCount} items\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "완료: {$orderCount} orders, {$itemCount} items\n";
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 기준 원칙
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ 🎯 핵심 원칙 │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 📦 데이터 전략 │
|
||||
│ ───────────────────────────────────────────────────────────────────── │
|
||||
│ - item_code → item_id 변환 (items 테이블 참조) │
|
||||
│ - JSON 파일은 options 컬럼에 통째로 저장 (파싱 + 원본 보존) │
|
||||
│ - 재고는 입고 기록 집계로 계산 │
|
||||
│ │
|
||||
│ ⚠️ 선행 조건 │
|
||||
│ ───────────────────────────────────────────────────────────────────── │
|
||||
│ - 반드시 items 마이그레이션 완료 후 진행 │
|
||||
│ - item_code가 없는 레코드는 스킵하고 로그 기록 │
|
||||
│ │
|
||||
│ ✅ 필수 사항 │
|
||||
│ ───────────────────────────────────────────────────────────────────── │
|
||||
│ - 전체 이관 (instock 2,286건, output 24,564건) │
|
||||
│ - JSON 파일 파싱 (5130/output/i_json/*.json) │
|
||||
│ - 로컬 검증 완료 후 개발서버 배포 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 6.1 변경 승인 정책
|
||||
|
||||
| 분류 | 예시 | 승인 |
|
||||
|------|------|------|
|
||||
| ✅ 즉시 가능 | SELECT 쿼리, 분석, 매핑 설계 | 불필요 |
|
||||
| ⚠️ 컨펌 필요 | INSERT 실행, TRUNCATE, 개발서버 배포 | **필수** |
|
||||
| 🔴 금지 | 운영서버 직접 작업 | 별도 협의 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 데이터 규모 예상
|
||||
|
||||
### 7.1 입고/재고 테이블 예상
|
||||
|
||||
| 소스 | 레코드 수 | SAM 테이블 | 예상 건수 |
|
||||
|------|----------|------------|----------|
|
||||
| instock | 2,286 | item_receipts | ~2,286 |
|
||||
| instock (집계) | - | stocks | ~500 (품목별 현재고) |
|
||||
| lot | ~200 | lots | ~200 |
|
||||
| lot_sales | ~300 | lot_sales | ~300 |
|
||||
| **합계** | - | - | **~3,300건** |
|
||||
|
||||
### 7.2 주문/출고 테이블 예상
|
||||
|
||||
| 소스 | 레코드 수 | SAM 테이블 | 예상 건수 |
|
||||
|------|----------|------------|----------|
|
||||
| output | 24,564 | orders | ~24,564 |
|
||||
| output.iList (JSON) | ~24,564 파일 | order_items | ~50,000 (주문당 2건 평균) |
|
||||
| estimate | ~500 | orders (type=견적) | ~500 |
|
||||
| **합계** | - | - | **~75,000건** |
|
||||
|
||||
### 7.3 전체 마이그레이션 요약 (이 문서 범위)
|
||||
|
||||
| SAM 테이블 | 예상 건수 | 비고 |
|
||||
|------------|----------|------|
|
||||
| item_receipts | ~2,300 | 입고 기록 |
|
||||
| stocks | ~500 | 현재고 |
|
||||
| lots | ~200 | 로트 |
|
||||
| lot_sales | ~300 | 로트 소진 |
|
||||
| orders | ~25,000 | 주문 헤더 |
|
||||
| order_items | ~50,000 | 주문 상세 |
|
||||
| **총계** | **~78,000건** | |
|
||||
|
||||
---
|
||||
|
||||
## 8. 체크리스트
|
||||
|
||||
### Phase 5: 입고/재고 데이터 이관 ⭐
|
||||
- [ ] instock 테이블 구조 분석 (컬럼명 확인)
|
||||
- [ ] instock → item_receipts 매핑 설계
|
||||
- [ ] item_code → item_id 변환 쿼리 작성
|
||||
- [ ] 마이그레이션 스크립트 작성
|
||||
- [ ] 재고 집계 → stocks 쿼리 작성
|
||||
- [ ] lot/lot_sales 구조 분석 및 매핑
|
||||
- [ ] ⚠️ **사용자 승인**: 입고/재고 INSERT 실행
|
||||
|
||||
### Phase 6: 주문/출고 데이터 이관 ⭐
|
||||
- [ ] output 테이블 구조 분석 (컬럼명 확인)
|
||||
- [ ] output → orders 매핑 설계
|
||||
- [ ] iList JSON 파일 구조 분석 (완료)
|
||||
- [ ] JSON → order_items 매핑 설계
|
||||
- [ ] estimate → orders 매핑 설계
|
||||
- [ ] 마이그레이션 스크립트 작성 (24,564건)
|
||||
- [ ] JSON 파일 파싱 로직 구현
|
||||
- [ ] ⚠️ **사용자 승인**: 주문/출고 INSERT 실행
|
||||
|
||||
---
|
||||
|
||||
## 9. 참고 문서
|
||||
|
||||
- **레거시 소스**: `5130/` 폴더
|
||||
- **JSON 파일 경로**: `5130/output/i_json/*.json`
|
||||
- **선행 문서**: `docs/plans/kd-items-migration-plan.md` (품목/단가 마이그레이션)
|
||||
- **SAM orders 마이그레이션**: `api/database/migrations/*_create_orders_table.php`
|
||||
- **SAM item_receipts 마이그레이션**: `api/database/migrations/*_create_item_receipts_table.php`
|
||||
- **DummyDataSeeder**: `api/database/seeders/DummyDataSeeder.php` (TENANT_ID=287, USER_ID=1)
|
||||
|
||||
---
|
||||
|
||||
## 10. 세션 및 메모리 관리 정책
|
||||
|
||||
### 10.1 세션 시작 시 (Load Strategy)
|
||||
```bash
|
||||
# 1. Docker 확인
|
||||
docker ps | grep sam
|
||||
|
||||
# 2. 선행 조건 확인
|
||||
docker exec sam-mysql-1 mysql -uroot -proot samdb -e "SELECT COUNT(*) FROM items WHERE tenant_id=287;"
|
||||
# → 최소 600건 이상이어야 시작 가능
|
||||
|
||||
# 3. 현재 진행 상태 확인
|
||||
# → 이 문서의 "📍 현재 진행 상태" 섹션 참조
|
||||
```
|
||||
|
||||
### 10.2 작업 중 관리
|
||||
|
||||
| 작업 완료 시 | 조치 |
|
||||
|-------------|------|
|
||||
| Phase 완료 | "📍 현재 진행 상태" 업데이트 |
|
||||
| INSERT 실행 | "12. 변경 이력" 추가 |
|
||||
| 오류 발생 | 체크리스트에 메모 추가 |
|
||||
|
||||
---
|
||||
|
||||
## 11. 자기완결성 점검 결과
|
||||
|
||||
### 11.1 핵심 정보 요약 (새 세션용)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ 📋 핵심 정보 요약 │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 🎯 목표: 경동기업 레거시(chandj) → SAM(samdb) 입고/재고/주문 이관 │
|
||||
│ │
|
||||
│ 📊 데이터 규모 (총 ~78,000건): │
|
||||
│ - item_receipts: ~2,300건 (입고) │
|
||||
│ - stocks: ~500건 (현재고) │
|
||||
│ - orders: ~25,000건 (주문 헤더) │
|
||||
│ - order_items: ~50,000건 (주문 상세) │
|
||||
│ │
|
||||
│ 🔑 핵심 상수: │
|
||||
│ - tenant_id = 287 (경동기업) │
|
||||
│ - user_id = 1 (생성자) │
|
||||
│ - Docker: sam-mysql-1 │
|
||||
│ - 레거시 DB: chandj / SAM DB: samdb ⚠️ │
|
||||
│ - JSON 파일: 5130/output/i_json/*.json │
|
||||
│ │
|
||||
│ ⭐ instock 실제 컬럼명 (2026-01-28 확인): │
|
||||
│ - prodcode (품목코드) → items.code 매칭용 │
|
||||
│ - item_name (품목명) │
|
||||
│ - received_qty (입고수량) │
|
||||
│ - purchase_price_excl_vat (단가) │
|
||||
│ - inspection_date (입고일) │
|
||||
│ - remarks (비고) │
|
||||
│ │
|
||||
│ ⭐ output 실제 컬럼명 (2026-01-28 확인): │
|
||||
│ - num (PK, output_id 대신) │
|
||||
│ - outdate (출고일, order_date 대신) │
|
||||
│ - iList (JSON 파일 경로) │
|
||||
│ - outworkplace (거래처) │
|
||||
│ - comment (비고, memo 대신) │
|
||||
│ │
|
||||
│ ⚠️ 선행 조건: │
|
||||
│ - kd-items-migration-plan.md 완료 필수! │
|
||||
│ - SAM items 테이블에 ~800건 이상 존재해야 함 │
|
||||
│ │
|
||||
│ ⭐ 마이그레이션 순서: │
|
||||
│ 1. instock → item_receipts (2,286건) │
|
||||
│ 2. 재고 집계 → stocks (~500건) │
|
||||
│ 3. output → orders + order_items (24,564건 + ~50,000건) │
|
||||
│ │
|
||||
│ 📍 현재 상태: ⏳ 대기 (품목 마이그레이션 완료 대기) │
|
||||
│ │
|
||||
│ 📎 선행 문서: docs/plans/kd-items-migration-plan.md (품목/단가) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 변경 이력
|
||||
|
||||
| 날짜 | 항목 | 변경 내용 | 파일 | 승인 |
|
||||
|------|------|----------|------|------|
|
||||
| 2026-01-28 | 문서 분리 | items-migration-kyungdong-plan.md에서 입고/재고/주문 부분 분리 | - | - |
|
||||
| 2026-01-28 | 문서 생성 | kd-orders-migration-plan.md 신규 생성 | - | - |
|
||||
| 2026-01-28 | 컬럼명 수정 | 실제 DB 컬럼명으로 업데이트 (item_code→prodcode, output_id→num 등) | - | - |
|
||||
|
||||
---
|
||||
|
||||
## 13. 트러블슈팅 가이드
|
||||
|
||||
### 13.1 일반적인 문제
|
||||
|
||||
| 문제 | 원인 | 해결책 |
|
||||
|------|------|--------|
|
||||
| item_id 연결 실패 | items 마이그레이션 미완료 | `kd-items-migration-plan.md` 먼저 완료 |
|
||||
| JSON 파일 없음 | 파일 경로 오류 | `5130/output/i_json/` 폴더 확인 |
|
||||
| 대량 INSERT 느림 | 단건 INSERT | 배치 INSERT (1000건씩) 사용 |
|
||||
| 외래키 오류 | item_id 없음 | item_code → item_id 매핑 확인 |
|
||||
|
||||
### 13.2 output.iList JSON 파일 처리
|
||||
|
||||
```php
|
||||
// output.iList 값 예시: "../output/i_json/22545.json"
|
||||
$iListPath = $output['iList']; // "../output/i_json/22545.json"
|
||||
|
||||
// 실제 파일 경로로 변환
|
||||
$basePath = '/Users/kent/Works/@KD_SAM/SAM/5130';
|
||||
$jsonFile = str_replace('../', '', $iListPath);
|
||||
$fullPath = $basePath . '/' . $jsonFile;
|
||||
|
||||
// JSON 파일 읽기
|
||||
if (file_exists($fullPath)) {
|
||||
$jsonContent = json_decode(file_get_contents($fullPath), true);
|
||||
// $jsonContent['inputValue'], $jsonContent['pages'] 등 사용
|
||||
} else {
|
||||
// 파일 없음 - 로그 기록 후 스킵
|
||||
error_log("JSON file not found: {$fullPath}");
|
||||
}
|
||||
```
|
||||
|
||||
### 13.3 prodcode → item_id 매칭 실패
|
||||
|
||||
```sql
|
||||
-- 매칭 실패 레코드 확인 (⭐ prodcode 사용)
|
||||
SELECT ins.prodcode, ins.item_name, COUNT(*) AS cnt
|
||||
FROM chandj.instock ins
|
||||
LEFT JOIN samdb.items i ON i.code = ins.prodcode AND i.tenant_id = 287
|
||||
WHERE ins.is_deleted = 0 AND i.id IS NULL
|
||||
GROUP BY ins.prodcode, ins.item_name;
|
||||
|
||||
-- 해결 방법:
|
||||
-- 1. 매칭 실패한 prodcode를 items 테이블에 추가
|
||||
-- 2. 또는 스킵하고 로그 기록
|
||||
|
||||
-- items에 없는 품목 신규 생성 쿼리 (필요시)
|
||||
INSERT INTO samdb.items (tenant_id, item_type, code, name, unit, attributes, is_active, created_by, created_at, updated_at)
|
||||
SELECT DISTINCT
|
||||
287 AS tenant_id,
|
||||
'SM' AS item_type, -- 기본값: 부자재
|
||||
ins.prodcode AS code,
|
||||
ins.item_name AS name,
|
||||
ins.unit AS unit,
|
||||
JSON_OBJECT('legacy_source', 'instock', 'specification', ins.specification) AS attributes,
|
||||
1 AS is_active,
|
||||
1 AS created_by,
|
||||
NOW(), NOW()
|
||||
FROM chandj.instock ins
|
||||
LEFT JOIN samdb.items i ON i.code = ins.prodcode AND i.tenant_id = 287
|
||||
WHERE ins.is_deleted = 0
|
||||
AND ins.prodcode IS NOT NULL AND ins.prodcode != ''
|
||||
AND i.id IS NULL;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*이 문서는 /sc:plan 스킬로 생성되었습니다.*
|
||||
Reference in New Issue
Block a user