From 00023b2d69336856eb78feddafff5d418d9982fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Tue, 20 Jan 2026 19:05:43 +0900 Subject: [PATCH] =?UTF-8?q?chore:=20=EA=B3=84=ED=9A=8D=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=20=EC=A0=95=EB=A6=AC=20=EB=B0=8F=20=EC=95=84=EC=B9=B4?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 완료된 계획 문서 12개 → plans/archive/ 이동 - 완료된 하위 계획 2개 → plans/sub/archive/ 이동 - 새 계획 문서 추가: - 5130-bom-migration-plan.md (완료) - 5130-sam-data-migration-plan.md (완료) - bidding-api-implementation-plan.md (완료) - dashboard-api-integration-plan.md - order-workorder-shipment-integration-plan.md - dev-toolbar-plan.md - AI 리포트 키워드 색상체계 가이드 v1.4 추가 - index_plans.md 업데이트 --- .../AI_리포트_키워드_색상체계_가이드_v1.4.md | 406 +++++++++ plans/archive/5130-bom-migration-plan.md | 446 ++++++++++ plans/archive/5130-sam-data-migration-plan.md | 828 ++++++++++++++++++ .../bidding-api-implementation-plan.md | 817 +++++++++++++++++ .../construction-api-integration-plan.md | 0 .../erp-api-development-plan-d1.0-changes.md | 0 .../l2-permission-management-plan.md | 0 .../mes-integration-analysis-plan.md | 0 .../mng-quote-formula-development-plan.md | 0 .../notification-sound-system-plan.md | 0 plans/{ => archive}/order-management-plan.md | 0 .../{ => archive}/process-management-plan.md | 0 ...quote-auto-calculation-development-plan.md | 0 .../react-fcm-push-notification-plan.md | 0 .../react-server-component-audit-plan.md | 0 plans/{ => archive}/work-order-plan.md | 0 plans/dashboard-api-integration-plan.md | 578 ++++++++++++ plans/dev-toolbar-plan.md | 358 ++++++++ plans/index_plans.md | 52 +- ...der-workorder-shipment-integration-plan.md | 659 ++++++++++++++ .../sub/{ => archive}/handover-report-plan.md | 0 plans/sub/{ => archive}/labor-plan.md | 0 22 files changed, 4126 insertions(+), 18 deletions(-) create mode 100644 plans/AI_리포트_키워드_색상체계_가이드_v1.4.md create mode 100644 plans/archive/5130-bom-migration-plan.md create mode 100644 plans/archive/5130-sam-data-migration-plan.md create mode 100644 plans/archive/bidding-api-implementation-plan.md rename plans/{ => archive}/construction-api-integration-plan.md (100%) rename plans/{ => archive}/erp-api-development-plan-d1.0-changes.md (100%) rename plans/{ => archive}/l2-permission-management-plan.md (100%) rename plans/{ => archive}/mes-integration-analysis-plan.md (100%) rename plans/{ => archive}/mng-quote-formula-development-plan.md (100%) rename plans/{ => archive}/notification-sound-system-plan.md (100%) rename plans/{ => archive}/order-management-plan.md (100%) rename plans/{ => archive}/process-management-plan.md (100%) rename plans/{ => archive}/quote-auto-calculation-development-plan.md (100%) rename plans/{ => archive}/react-fcm-push-notification-plan.md (100%) rename plans/{ => archive}/react-server-component-audit-plan.md (100%) rename plans/{ => archive}/work-order-plan.md (100%) create mode 100644 plans/dashboard-api-integration-plan.md create mode 100644 plans/dev-toolbar-plan.md create mode 100644 plans/order-workorder-shipment-integration-plan.md rename plans/sub/{ => archive}/handover-report-plan.md (100%) rename plans/sub/{ => archive}/labor-plan.md (100%) diff --git a/plans/AI_리포트_키워드_색상체계_가이드_v1.4.md b/plans/AI_리포트_키워드_색상체계_가이드_v1.4.md new file mode 100644 index 0000000..aedaf24 --- /dev/null +++ b/plans/AI_리포트_키워드_색상체계_가이드_v1.4.md @@ -0,0 +1,406 @@ +# SAM ERP 대시보드 +## AI 리포트 핵심 키워드 색상 체계 가이드 +### (임계값 명확화 버전 v1.4) + +> 버전: D1.4 | 작성일: 2026년 1월 + +--- + +## 1. AI 리포트 색상 체계 개요 + +AI 리포트는 각 섹션별 핵심 키워드에 색상을 적용하여 사용자가 즉시 상태를 파악할 수 있도록 합니다. 모든 기준은 명확한 수치로 정의되어 일관된 적용이 가능합니다. + +### 1.1 색상 정의 + +| 색상 | 의미 | 적용 원칙 | 우선순위 | +|:---:|:---:|:---|:---:| +| 🔴 빨간색 | 경고 | 즉각 조치 필요, 한도/기준 초과, 손실 발생 | 1순위 (최우선) | +| 🟠 주황색 | 주의 | 기준의 80~100% 도달, 기한 임박, 검토 필요 | 2순위 | +| 🟢 녹색 | 긍정 | 목표 달성, 정상 완료, 개선, 입금/회수 | 3순위 | +| 🔵 파란색 | 양호 | 안정적 유지, 정상 진행 중, 충분히 확보 | 4순위 | + +### 1.2 공통 임계값 원칙 + +| 구분 | 🔴 경고 | 🟠 주의 | 🟢 긍정 | 🔵 양호 | +|:---|:---|:---|:---|:---| +| 한도 사용률 | 100% 초과 | 85~100% | - | 85% 미만 | +| 전월/전기 대비 증감 | ±20% 이상 | ±10~20% | 개선 방향 변동 | ±10% 이내 | +| 예산 대비 | 100% 초과 | 90~100% | - | 90% 미만 | +| 연체 기간 | 90일 초과 | 30~90일 | 정상 회수 | 만기 전 | +| 운영자금 확보 | 3개월 미만 | 3~6개월 | - | 6개월 이상 | + +--- + +## 2. 일일 일보 섹션 + +일일 일보는 당일 자금 현황을 요약하여 보여주며, 현금 흐름에 대한 AI 분석 리포트가 함께 제공됩니다. + +### 2.1 현금 자산 - 출금 분석 + +| 키워드 | 색상 | 임계값 기준 | 계산 방식 | +|:---|:---:|:---|:---| +| **출금** | 🔴 빨간색 | 7일 평균 대비 200% 이상 | 당일출금 ÷ 7일평균출금 ≥ 2.0 | +| **점검이 필요** | 🔴 빨간색 | 7일 평균 대비 200% 이상 | 출금 키워드와 함께 사용 | +| **출금 증가** | 🟠 주황색 | 7일 평균 대비 150~200% | 당일출금 ÷ 7일평균출금 1.5~2.0 | +| **정상 출금** | 🔵 파란색 | 7일 평균 대비 150% 미만 | 당일출금 ÷ 7일평균출금 < 1.5 | + +#### 적용 예시 +- 어제 🔴**3.5억원 출금**했습니다. 최근 7일 평균(1.7억원) 대비 206%로 🔴**점검이 필요**합니다. + +### 2.2 현금 자산 - 입금 분석 + +| 키워드 | 색상 | 임계값 기준 | 계산 방식 | +|:---|:---:|:---|:---| +| **입금** | 🟢 녹색 | 입금 발생 시 (금액 무관) | 당일 입금 > 0 | +| **대규모 입금** | 🟢 녹색 | 월평균 입금의 200% 이상 | 당일입금 ÷ 월평균입금 ≥ 2.0 | +| **주요 원인** | 🟢 녹색 | 입금 원인 설명 시 | 입금 키워드와 함께 사용 | + +#### 적용 예시 +- 어제 🟢**10.2억원이 입금**되었습니다. 대한건설 선수금 🟢**입금**이 🟢**주요 원인**입니다. + +### 2.3 현금 자산 - 운영자금 안정성 + +| 키워드 | 색상 | 임계값 기준 | 계산 방식 | +|:---|:---:|:---|:---| +| **자금 부족 우려** | 🔴 빨간색 | 월 운영비용 대비 3개월 미만 | 현금자산 ÷ 월운영비 < 3 | +| **자금 관리 필요** | 🟠 주황색 | 월 운영비용 대비 3~6개월 | 현금자산 ÷ 월운영비 3~6 | +| **확보되어 안정적** | 🔵 파란색 | 월 운영비용 대비 6개월 이상 | 현금자산 ÷ 월운영비 ≥ 6 | + +#### 적용 예시 +- 총 현금성 자산이 300.2억원입니다. 월 운영비용(16.7억원) 대비 🔵**18개월 분이 확보되어 안정적**입니다. + +### 2.4 외화 현황 - 환율 변동 + +| 키워드 | 색상 | 임계값 기준 (일일) | 임계값 기준 (주간) | +|:---|:---:|:---|:---| +| **환율 급등** | 🔴 빨간색 | 전일 대비 ±1.5% 이상 또는 ±20원 이상 | 전주 대비 ±3% 이상 | +| **환율 급락** | 🔴 빨간색 | 전일 대비 ±1.5% 이상 또는 ±20원 이상 | 전주 대비 ±3% 이상 | +| **환율 변동 주의** | 🟠 주황색 | 전일 대비 ±1.0~1.5% 또는 ±10~20원 | 전주 대비 ±2~3% | +| **환율 안정** | 🔵 파란색 | 전일 대비 ±1.0% 미만 또는 ±10원 미만 | 전주 대비 ±2% 미만 | + +### 2.5 외화 현황 - 환차손익 + +| 키워드 | 색상 | 임계값 기준 (금액) | 임계값 기준 (비율) | +|:---|:---:|:---|:---| +| **환차손 발생** | 🔴 빨간색 | 평가손실 1,000만원 이상 | 외화보유액 대비 2% 이상 손실 | +| **환리스크 주의** | 🟠 주황색 | 평가손실 500~1,000만원 | 외화보유액 대비 1~2% 손실 | +| **환차익 발생** | 🟢 녹색 | 평가이익 500만원 이상 | 외화보유액 대비 1% 이상 이익 | +| **환율 영향 미미** | 🔵 파란색 | 평가손익 ±500만원 미만 | 외화보유액 대비 ±1% 미만 | + +#### 적용 예시 +- 전일 대비 환율이 🔴**1.8% 상승(+24원)**했습니다. 외화자산 평가손실 🔴**약 1,500만원 환차손 발생**이 예상됩니다. +- 전일 대비 환율 변동 0.3%(+4원)으로 🔵**환율 안정**적인 상태입니다. 🔵**환율 영향 미미**합니다. + +--- + +## 3. 당월 예상 지출 내역 섹션 + +당월 예상되는 지출 항목(매입, 카드, 발행어음 등)을 분석하여 전월 대비 및 예산 대비 현황을 제공합니다. + +### 3.1 전월 대비 분석 + +| 키워드 | 색상 | 임계값 기준 | 계산 방식 | +|:---|:---:|:---|:---| +| **전월 대비 N% 증가** | 🔴 빨간색 | 전월 대비 15% 이상 증가 | (당월-전월) ÷ 전월 ≥ 0.15 | +| **지출 증가 추이** | 🟠 주황색 | 전월 대비 10~15% 증가 | (당월-전월) ÷ 전월 0.10~0.15 | +| **전월 대비 N% 감소** | 🟢 녹색 | 전월 대비 5% 이상 감소 | (당월-전월) ÷ 전월 ≤ -0.05 | +| **전월과 유사** | 🔵 파란색 | 전월 대비 ±10% 이내 | |(당월-전월) ÷ 전월| < 0.10 | + +#### 적용 예시 +- 이번 달 예상 지출이 🔴**전월 대비 15% 증가**했습니다. 매입 비용 증가가 주요 원인입니다. +- 이번 달 예상 지출이 🟢**전월 대비 8% 감소**했습니다. 외주비용 절감이 주요 원인입니다. + +### 3.2 예산 대비 분석 + +| 키워드 | 색상 | 임계값 기준 | 계산 방식 | +|:---|:---:|:---|:---| +| **예산을 N% 초과** | 🔴 빨간색 | 예산 대비 100% 초과 | 예상지출 ÷ 예산 > 1.0 | +| **예산 임박** | 🟠 주황색 | 예산 대비 90~100% | 예상지출 ÷ 예산 0.9~1.0 | +| **예산 내 운영** | 🟢 녹색 | 예산 대비 90% 미만 | 예상지출 ÷ 예산 < 0.9 | + +#### 적용 예시 +- 이번 달 예상 지출이 🔴**예산을 12% 초과**했습니다. 비용 항목별 점검이 필요합니다. +- 이번 달 예상 지출이 🟢**예산 내 운영** 중입니다. (예산 대비 82%) + +### 3.3 항목별 지출 분석 기준 + +| 지출 항목 | 🔴 경고 | 🟠 주의 | 🟢 긍정 | 🔵 양호 | +|:---|:---|:---|:---|:---| +| 매입 | 전월 대비 20% 이상 증가 | 전월 대비 10~20% 증가 | 전월 대비 감소 | ±10% 이내 | +| 카드 | 한도 100% 초과 | 한도 80~100% 사용 | - | 한도 80% 미만 | +| 발행어음 | 만기 초과 또는 부도 위험 | 만기 D-7일 이내 | 정상 결제 완료 | 만기 D-8일 이상 | +| 인건비 | 예산 대비 100% 초과 | 예산 대비 90~100% | - | 예산 대비 90% 미만 | + +--- + +## 4. 카드/가지급금 관리 섹션 + +법인카드 사용 현황과 가지급금 발생 현황을 분석하여 세무 리스크를 사전에 안내합니다. + +### 4.1 가지급금 전환 + +| 키워드 | 색상 | 임계값 기준 | 세무 영향 | +|:---|:---:|:---|:---| +| **가지급금으로 전환** | 🔴 빨간색 | 미정리 법인카드 사용 100만원 이상 | 인정이자 4.6% 발생 | +| **인정이자가 발생** | 🔴 빨간색 | 가지급금 잔액 × 4.6% | 법인세 증가 | +| **연간 N만원의 인정이자** | 🔴 빨간색 | 연간 인정이자 100만원 이상 | 가지급금 × 4.6% | +| **가지급금 정리 필요** | 🟠 주황색 | 미정리 법인카드 사용 50~100만원 | 정리 권고 | + +#### 적용 예시 +- 법인카드 사용 중 850만원이 🔴**가지급금으로 전환**되었습니다. 🔴**연 4.6% 인정이자가 발생**합니다. +- 현재 가지급금 3.5억 × 4.6% = 🔴**연간 약 1,610만원의 인정이자가 발생** 중입니다. + +### 4.2 업무관련성 소명 필요 + +| 키워드 | 색상 | 임계값 기준 | 발생 사유 | +|:---|:---:|:---|:---| +| **불인정 가맹점 결제** | 🔴 빨간색 | 유흥업소, 귀금속, 상품권 등 결제 | 가지급금 전환 대상 | +| **본인 청구 결제 감지** | 🟠 주황색 | 상품권, 귀금속, 면세점 등 1건 이상 | 소명 자료 필요 | +| **주말 사용 감지** | 🟠 주황색 | 토/일요일 결제 50만원 이상 | 업무관련성 검토 | +| **심야 사용 감지** | 🟠 주황색 | 22시~06시 결제 30만원 이상 | 업무관련성 검토 | +| **해외 사용 감지** | 🟠 주황색 | 해외 결제 발생 시 | 출장 증빙 필요 | + +#### 적용 예시 +- 상품권 구매 🟠**본인 청구 결제 감지**. 가지급금 처리 예정입니다. +- 🟠**주말 사용 감지** - 토요일 120만원 결제. 업무관련성 소명이 어려울 수 있으니 기록을 남겨주세요. + +### 4.3 법인세/종합소득세 예상 가중 + +| 키워드 | 색상 | 임계값 기준 | 계산 방식 | +|:---|:---:|:---|:---| +| **법인세 예상 가중** | 🔴 빨간색 | 추가 법인세 100만원 이상 예상 | 가지급금 인정이자 × 법인세율 | +| **대표자 종합소득세 예상 가중** | 🔴 빨간색 | 추가 종합소득세 50만원 이상 예상 | 인정상여 × 소득세율 | +| **세무 리스크 주의** | 🟠 주황색 | 추가 세금 50만원 미만 예상 | 정리 권고 | + +#### 적용 예시 +- 가지급금으로 인한 🔴**법인세 예상 가중 약 320만원**이 발생합니다. +- 🔴**대표자 종합소득세 예상 가중 약 180만원**이 예상됩니다. (추가 사용 +10.5%) + +--- + +## 5. 접대비 현황 섹션 + +접대비 사용 현황과 한도 대비 사용률을 분석하여 세법상 한도 초과 여부를 사전에 안내합니다. + +### 5.1 한도 사용률 기준 + +| 키워드 | 색상 | 임계값 기준 | 계산 방식 | +|:---|:---:|:---|:---| +| **한도 초과 N만원 발생** | 🔴 빨간색 | 한도 사용률 100% 초과 | 사용액 > 한도액 | +| **손금 불산입** | 🔴 빨간색 | 한도 초과액 발생 시 | 초과액 = 사용액 - 한도액 | +| **법인세 부담이 증가** | 🔴 빨간색 | 한도 초과로 인한 법인세 증가 | 초과액 × 법인세율 | +| **잔여 한도 N원** | 🟠 주황색 | 한도 사용률 85~100% | 잔여 = 한도액 - 사용액 | +| **사용 계획을 점검** | 🟠 주황색 | 한도 사용률 85% 이상 | 사용액 ÷ 한도액 ≥ 0.85 | +| **여유 있게 운영** | 🟢 녹색 | 한도 사용률 75% 미만 | 사용액 ÷ 한도액 < 0.75 | +| **정상 운영** | 🔵 파란색 | 한도 사용률 75~85% | 사용액 ÷ 한도액 0.75~0.85 | + +#### 세법상 접대비 한도 계산 +- 기본한도: 중소기업 3,600만원, 일반기업 2,400만원 (연간) +- 추가한도: 수입금액 × 적용률 (100억 이하 0.3%, 100~500억 0.2%, 500억 초과 0.03%) + +#### 적용 예시 +- (1분기) 접대비 사용 1,000만원 / 한도 4,012만원 (25%). 🟢**여유 있게 운영** 중입니다. +- 접대비 한도 85% 도달. 🟠**잔여 한도 600만원**입니다. 🟠**사용 계획을 점검**해 주세요. +- 🔴**접대비 한도 초과 320만원 발생**. 초과분은 🔴**손금 불산입**되어 🔴**법인세 부담이 증가**합니다. + +### 5.2 증빙 관리 + +| 키워드 | 색상 | 임계값 기준 | 필수 정보 | +|:---|:---:|:---|:---| +| **거래처 정보가 누락** | 🔴 빨간색 | 거래처명 또는 참석자 미입력 1건 이상 | 거래처명, 참석자, 목적 | +| **증빙 누락** | 🔴 빨간색 | 영수증 또는 카드전표 미첨부 1건 이상 | 적격증빙 필수 | +| **기록 보완 필요** | 🟠 주황색 | 상세 내용 미기재 (목적, 장소 등) | 상세 기록 권고 | +| **증빙 완비** | 🟢 녹색 | 모든 필수 정보 입력 완료 | - | + +#### 적용 예시 +- 접대비 사용 중 3건(45만원)의 🔴**거래처 정보가 누락**되었습니다. 🟠**기록 보완 필요**합니다. + +--- + +## 6. 복리후생비 현황 섹션 + +복리후생비 사용 현황을 분석하여 비과세 한도 초과 여부와 업계 평균 대비 적정성을 안내합니다. + +### 6.1 1인당 복리후생비 + +| 키워드 | 색상 | 임계값 기준 | 업계 평균 | +|:---|:---:|:---|:---| +| **과다 지출** | 🔴 빨간색 | 1인당 월 30만원 초과 | 업계 평균의 150% 초과 | +| **지출 증가 추이** | 🟠 주황색 | 1인당 월 25~30만원 | 업계 평균의 120~150% | +| **업계 평균 내 정상 운영** | 🟢 녹색 | 1인당 월 15~25만원 | 업계 평균 범위 내 | +| **적정 운영** | 🔵 파란색 | 1인당 월 15만원 미만 | 업계 평균 미만 | + +#### 적용 예시 +- 1인당 월 복리후생비 20만원. 🟢**업계 평균(15~25만원) 내 정상 운영** 중입니다. + +### 6.2 항목별 비과세 한도 + +| 항목 | 비과세 한도 | 🔴 경고 기준 | 🟢 정상 기준 | +|:---|:---|:---|:---| +| 식대 | 월 20만원 | 20만원 초과 시 초과분 과세 | 20만원 이하 | +| 자가운전보조금 | 월 20만원 | 20만원 초과 시 초과분 과세 | 20만원 이하 | +| 출산/보육수당 | 월 20만원 | 20만원 초과 시 초과분 과세 | 20만원 이하 | +| 연구보조비 | 월 20만원 | 20만원 초과 시 초과분 과세 | 20만원 이하 | +| 야근식대/숙직비 | 실비 정산 | 과다 지급 시 과세 위험 | 실비 범위 내 | + +### 6.3 비과세 초과 시 + +| 키워드 | 색상 | 임계값 기준 | 세무 처리 | +|:---|:---:|:---|:---| +| **비과세 한도를 초과** | 🔴 빨간색 | 항목별 비과세 한도 초과 시 | 초과분 근로소득 과세 | +| **근로소득 과세됩니다** | 🔴 빨간색 | 비과세 초과분 발생 시 | 원천세 추가 징수 | +| **초과분 N만원 과세 처리** | 🔴 빨간색 | 과세 금액 명시 | 급여에 합산 | +| **한도 임박** | 🟠 주황색 | 비과세 한도의 90% 이상 사용 | 사용 주의 | + +#### 적용 예시 +- 식대가 월 25만원으로 🔴**비과세 한도(20만원)를 초과**했습니다. 🔴**초과분 5만원 근로소득 과세됩니다**. + +--- + +## 7. 미수금 현황 섹션 + +미수금 현황을 분석하여 연체 상태, 회수 필요성, 리스크 집중도를 안내합니다. + +### 7.1 연체 기간별 분류 + +| 키워드 | 색상 | 연체 기간 | 조치 수준 | +|:---|:---:|:---|:---| +| **장기 미수금 발생** | 🔴 빨간색 | 90일 초과 | 법적 조치 검토 | +| **회수 조치가 필요** | 🔴 빨간색 | 60~90일 | 적극적 독촉/추심 | +| **연체 발생** | 🟠 주황색 | 30~60일 | 독촉장 발송 | +| **연체 임박** | 🟠 주황색 | 만기 D-7일 ~ 만기 후 30일 | 사전 연락 | +| **정상 거래** | 🟢 녹색 | 만기 전 | 정상 관리 | +| **회수 완료** | 🟢 녹색 | 전액 회수 시 | 완료 처리 | + +#### 적용 예시 +- 90일 이상 🔴**장기 미수금 3건(2,500만원) 발생**. 🔴**회수 조치가 필요**합니다. + +### 7.2 리스크 집중도 + +| 키워드 | 색상 | 임계값 기준 | 계산 방식 | +|:---|:---:|:---|:---| +| **전체의 N%를 차지** | 🔴 빨간색 | 상위 1개사 미수금 비중 30% 이상 | 거래처 미수금 ÷ 총 미수금 | +| **리스크 분산이 필요** | 🔴 빨간색 | 상위 1개사 비중 30% 이상 | 집중 리스크 경고 | +| **리스크 관리 필요** | 🟠 주황색 | 상위 3개사 비중 50% 이상 | 분산 권고 | +| **리스크 분산 양호** | 🟢 녹색 | 상위 1개사 비중 20% 미만 | 정상 분산 | +| **리스크 관리 양호** | 🔵 파란색 | 상위 3개사 비중 40% 미만 | 양호한 분산 | + +#### 적용 예시 +- (주)대한전자 미수금 1,500만원으로 🔴**전체의 35%를 차지**합니다. 🔴**리스크 분산이 필요**합니다. +- 상위 3개사 미수금 비중 38%. 🔵**리스크 관리 양호**합니다. + +### 7.3 미수금 금액 기준 + +| 키워드 | 색상 | 임계값 기준 | 비고 | +|:---|:---:|:---|:---| +| **대형 미수금** | 🔴 빨간색 | 단일 건 3,000만원 이상 | 집중 관리 대상 | +| **주요 미수금** | 🟠 주황색 | 단일 건 1,000~3,000만원 | 관리 주의 | +| **일반 미수금** | 🔵 파란색 | 단일 건 1,000만원 미만 | 정상 관리 | + +--- + +## 8. 채권추심 현황 섹션 + +채권추심 진행 현황을 분석하여 법적 조치 상태와 회수 가능성을 안내합니다. + +### 8.1 추심 진행 상태 + +| 키워드 | 색상 | 임계값 기준 | 다음 단계 | +|:---|:---:|:---|:---| +| **회수 불가 판정** | 🔴 빨간색 | 채무자 무자력 확인 또는 소멸시효 완성 | 대손 처리 | +| **파산/회생 신청** | 🔴 빨간색 | 채무자 파산/회생 신청 확인 시 | 채권 신고 | +| **대손 처리 검토가 필요** | 🟠 주황색 | 회수 가능성 30% 미만 판단 시 | 세무 검토 | +| **법적 조치 진행 중** | 🔵 파란색 | 소송/강제집행 진행 중 | 결과 대기 | +| **지급명령 신청 완료** | 🟢 녹색 | 지급명령 신청 접수 완료 | 법원 결정 대기 | +| **회수 완료** | 🟢 녹색 | 채권 전액 또는 일부 회수 | 종결 처리 | + +#### 적용 예시 +- (주)대한전자 건 🟢**지급명령 신청 완료**. 법원 결정까지 약 2주 소요 예정입니다. +- (주)삼성테크 건 🔴**회수 불가 판정**. 🟠**대손 처리 검토가 필요**합니다. + +### 8.2 예상 소요 기간 + +| 키워드 | 색상 | 임계값 기준 | 비고 | +|:---|:---:|:---|:---| +| **장기 소송 예상** | 🔴 빨간색 | 예상 소요 기간 6개월 이상 | 비용/효익 검토 필요 | +| **소송 진행 중** | 🟠 주황색 | 예상 소요 기간 3~6개월 | 진행 상황 모니터링 | +| **법원 결정까지 약 N주 소요 예정** | 🔵 파란색 | 예상 소요 기간 3개월 미만 | 정상 진행 | +| **조기 회수 예상** | 🟢 녹색 | 예상 소요 기간 1개월 미만 | 신속 처리 | + +### 8.3 회수율 기준 + +| 키워드 | 색상 | 임계값 기준 | 판단 기준 | +|:---|:---:|:---|:---| +| **회수 불가** | 🔴 빨간색 | 예상 회수율 10% 미만 | 대손 처리 대상 | +| **회수 곤란** | 🔴 빨간색 | 예상 회수율 10~30% | 적극 추심 필요 | +| **부분 회수 예상** | 🟠 주황색 | 예상 회수율 30~70% | 협상 검토 | +| **회수 가능성 높음** | 🟢 녹색 | 예상 회수율 70% 이상 | 정상 추심 | +| **전액 회수 예상** | 🟢 녹색 | 예상 회수율 90% 이상 | 양호 | + +--- + +## 9. 부가세 현황 섹션 + +부가세 예정/확정 신고 현황을 분석하여 납부/환급 예상액과 세금계산서 발행 현황을 안내합니다. + +### 9.1 납부/환급 세액 + +| 키워드 | 색상 | 임계값 기준 | 판단 근거 | +|:---|:---:|:---|:---| +| **납부세액 급증** | 🔴 빨간색 | 전기 대비 30% 이상 증가 (매출 증가율 대비 초과) | 비정상 증가 | +| **매입세액 누락 의심** | 🔴 빨간색 | 매입세액 ÷ 매출세액 < 업종 평균의 70% | 누락 가능성 | +| **납부세액 증가** | 🟠 주황색 | 전기 대비 15~30% 증가 | 검토 필요 | +| **예상 환급세액** | 🟢 녹색 | 매입세액 > 매출세액 | 환급 발생 | +| **매입세액 증가가 주요 원인** | 🟢 녹색 | 설비투자 등 정당한 매입 증가 시 | 정상 사유 | +| **정상적인 증가로 판단** | 🟢 녹색 | 매출 증가율과 납부세액 증가율 유사 | 정상 범위 | +| **전기 대비 N% 증가** | 🔵 파란색 | 전기 대비 15% 미만 증가 | 정상 변동 | + +#### 적용 예시 +- 2026년 1기 예정신고 기준, 🟢**예상 환급세액**은 5,200,000원입니다. 설비투자에 따른 🟢**매입세액 증가가 주요 원인**입니다. +- 예상 납부세액 110,100,000원. 🔵**전기 대비 12.9% 증가**했으며, 매출 증가(11.5%)에 따른 🟢**정상적인 증가로 판단**됩니다. + +### 9.2 세금계산서 발행 관리 + +| 키워드 | 색상 | 임계값 기준 | 가산세 | +|:---|:---:|:---|:---| +| **세금계산서 미발행** | 🔴 빨간색 | 발행 기한 경과 후 미발행 1건 이상 | 공급가액의 2% | +| **발행 기한 초과** | 🔴 빨간색 | 공급일 다음 달 10일 경과 | 지연 발급 1% | +| **가산세 발생 위험** | 🔴 빨간색 | 미발행 또는 지연 발행 시 | 최대 2% | +| **발행 기한 임박** | 🟠 주황색 | 발행 기한 D-3일 이내 | 발행 권고 | +| **정상 발행** | 🟢 녹색 | 공급일 다음 달 10일 이내 발행 | 정상 | + +#### 적용 예시 +- 🔴**세금계산서 미발행** 3건 발생. 🔴**가산세 발생 위험**이 있습니다. 공급가액 1,500만원 × 2% = 30만원 +- 12월 매출분 세금계산서 🟠**발행 기한 임박** (D-2일). 1월 10일까지 발행 필요합니다. + +--- + +## 10. 종합 색상 적용 기준 매트릭스 + +모든 AI 리포트 섹션에 대한 색상 적용 기준을 요약한 종합 매트릭스입니다. + +| 섹션 | 🔴 경고 | 🟠 주의 | 🟢 긍정 | 🔵 양호 | +|:---|:---|:---|:---|:---| +| 일일 일보 (출금) | 7일 평균 대비 200% 이상 | 7일 평균 대비 150~200% | - | 7일 평균 대비 150% 미만 | +| 일일 일보 (입금) | - | - | 입금 발생 시 | - | +| 일일 일보 (운영자금) | 3개월 미만 확보 | 3~6개월 확보 | - | 6개월 이상 확보 | +| 일일 일보 (환율) | 일 ±1.5% 이상 | 일 ±1.0~1.5% | 환차익 발생 | 일 ±1.0% 미만 | +| 일일 일보 (환차손익) | 손실 1,000만원 이상 | 손실 500~1,000만원 | 이익 500만원 이상 | ±500만원 미만 | +| 당월 지출 (전월 대비) | 15% 이상 증가 | 10~15% 증가 | 5% 이상 감소 | ±10% 이내 | +| 당월 지출 (예산 대비) | 100% 초과 | 90~100% | - | 90% 미만 | +| 카드/가지급금 (전환) | 100만원 이상 전환 | 50~100만원 전환 | - | - | +| 카드/가지급금 (소명) | 불인정 가맹점 | 주말/심야 50만원 이상 | - | - | +| 접대비 (한도) | 100% 초과 | 85~100% | 75% 미만 | 75~85% | +| 접대비 (증빙) | 거래처 정보 누락 | 상세 내용 미기재 | 증빙 완비 | - | +| 복리후생비 | 1인당 월 30만원 초과 | 1인당 월 25~30만원 | 1인당 월 15~25만원 | 1인당 월 15만원 미만 | +| 복리후생비 (비과세) | 한도 초과 시 과세 | 한도 90% 이상 | - | 한도 이하 | +| 미수금 (연체) | 90일 초과 | 30~90일 | 회수 완료 | 만기 전 | +| 미수금 (집중도) | 1개사 30% 이상 | 3개사 50% 이상 | 1개사 20% 미만 | 3개사 40% 미만 | +| 채권추심 (상태) | 회수 불가, 파산 | 대손 검토 필요 | 지급명령 완료, 회수 | 법적 조치 진행 중 | +| 채권추심 (회수율) | 10% 미만 | 10~30% | 70% 이상 | 30~70% | +| 부가세 (납부세액) | 전기 대비 30% 이상 증가 | 전기 대비 15~30% 증가 | 환급 발생, 정상 증가 | 전기 대비 15% 미만 | +| 부가세 (세금계산서) | 미발행/기한 초과 | 기한 D-3일 이내 | 정상 발행 | - | + +--- + +*— 문서 끝 —* diff --git a/plans/archive/5130-bom-migration-plan.md b/plans/archive/5130-bom-migration-plan.md new file mode 100644 index 0000000..a970d91 --- /dev/null +++ b/plans/archive/5130-bom-migration-plan.md @@ -0,0 +1,446 @@ +# 5130 → SAM BOM 데이터 마이그레이션 계획 + +> **작성일**: 2025-01-20 +> **목적**: 5130 레거시 시스템의 BOM 데이터를 SAM items 테이블의 bom 컬럼에 마이그레이션 +> **기준 문서**: `api/app/Services/Quote/FormulaEvaluatorService.php` +> **상태**: ✅ 완료 (Serena ID: 5130-bom-migration-state) + +--- + +## 📍 현재 진행 상태 + +| 항목 | 내용 | +|------|------| +| **마지막 완료 작업** | BOM 마이그레이션 실행 완료 (61건) | +| **다음 작업** | 견적 페이지에서 실제 테스트 (사용자 수동 확인) | +| **진행률** | 4/4 (100%) | +| **마지막 업데이트** | 2025-01-20 | + +--- + +## 1. 개요 + +### 1.1 배경 + +5130 레거시 시스템에서 SAM으로 품목(items) 마이그레이션이 완료되었으나, 완제품(FG)의 BOM 데이터가 마이그레이션되지 않아 다음 문제가 발생: + +``` +문제 현상: +- 견적 페이지에서 "국민방화스크린 (일체형) (S0001)" 선택 후 자동 견적 산출 → 합계 0원 +- 원인: S0001의 bom 컬럼이 NULL +- items 테이블에서 확인: SELECT bom FROM items WHERE code = 'S0001' → NULL +``` + +**기존 마이그레이션 상태:** +- Items: 608건 (KDunitprice → items) +- Orders: 24,424건 +- Order Items: 43,900건 +- ❌ BOM 데이터: 마이그레이션 안됨 + +### 1.2 기준 원칙 +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 🎯 핵심 원칙 │ +├─────────────────────────────────────────────────────────────────┤ +│ 1. FormulaEvaluatorService 호환 BOM JSON 형식 생성 │ +│ 2. 동적 수량 계산을 위한 quantityFormula 필드 지원 │ +│ 3. childItemCode 기반 참조 (child_item_id 아님) │ +│ 4. 기존 SAM BOM 패턴과 일관성 유지 │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 1.3 변경 승인 정책 + +| 분류 | 예시 | 승인 | +|------|------|------| +| ✅ 즉시 가능 | BOM JSON 데이터 추가, 매핑 테이블 생성 | 불필요 | +| ⚠️ 컨펌 필요 | 기존 items 데이터 수정, 새 마이그레이션 스크립트 | **필수** | +| 🔴 금지 | items 테이블 구조 변경, 기존 BOM 삭제 | 별도 협의 | + +### 1.4 준수 규칙 +- `docs/quickstart/quick-start.md` - 빠른 시작 가이드 +- `docs/standards/quality-checklist.md` - 품질 체크리스트 +- `api/app/Services/Quote/FormulaEvaluatorService.php` - BOM 계산 로직 + +--- + +## 2. 데이터 구조 분석 + +### 2.1 5130 BOM 구조 + +``` +5130 DB (chandj) +├── KDunitprice (품목 마스터) +│ ├── prodcode: 품목 코드 +│ ├── item_name: 품목명 +│ └── item_div: [제품], [상품], [부재료], [원재료], [반제품] +│ +├── models (모델 마스터) +│ ├── model_id: PK +│ ├── model_name: KSS01, KSE01, KWE01... (모델 코드) +│ ├── major_category: 스크린 | 철재 +│ ├── finishing_type: SUS마감 | EGI마감 +│ └── guiderail_type: 벽면형 | 측면형 +│ +├── parts (1단계 BOM - 모델별 부품) +│ ├── part_id: PK +│ ├── model_id: FK → models +│ ├── part_name: 가이드레일, 하단마감재 등 +│ ├── spec: 120*70, 60*40 등 +│ ├── quantity: 수량 +│ ├── unit: SET, EA 등 +│ └── unitprice: 단가 (문자열, 콤마 포함) +│ +└── parts_sub (2단계 BOM - 부품별 원자재) + ├── subpart_id: PK + ├── part_id: FK → parts + ├── subpart_name: 1번(마감제), 2번(본체) 등 + ├── material: SUS 1.2T, EGI 1.55T 등 + ├── quantity: 수량 + ├── bendSum, plateSum, finalSum: 가공 관련 + └── unitPrice, computedPrice, lineTotal: 금액 +``` + +**5130 model_id별 데이터 현황:** +| model_id | model_name | category | finishing | guiderail | parts 수 | +|----------|------------|----------|-----------|-----------|----------| +| 12 | KSS01 | 스크린 | SUS마감 | 벽면형 | 2 | +| 13 | KSS01 | 스크린 | SUS마감 | 측면형 | 2 | +| 14 | KSE01 | 스크린 | SUS마감 | 벽면형 | 2 | +| ... | ... | ... | ... | ... | ... | + +**5130 KDunitprice item_div 분포:** +| item_div | 건수 | SAM item_type 매핑 | +|----------|------|-------------------| +| [제품] | 194건 | FG (완제품) | +| [상품] | 260건 | SM (부자재) | +| [부재료] | 48건 | SM (부자재) | +| [원재료] | 24건 | RM (원자재) | +| [반제품] | 73건 | SF (반제품) | +| [무형상품] | 4건 | CS (서비스) | + +### 2.2 SAM BOM 구조 + +```sql +-- SAM items 테이블 BOM 컬럼 +items.bom: JSON +``` + +**SAM BOM JSON 형식 (FormulaEvaluatorService 호환):** +```json +[ + { + "childItemCode": "SF-SCR-F01", // 필수: 하위 품목 코드 + "quantity": 1, // 필수: 기본 수량 + "quantityFormula": "W*H/1000000", // 선택: 동적 수량 계산식 + "unit": "M2", // 선택: 단위 + "note": "스크린 원단" // 선택: 비고 + }, + { + "childItemCode": "SF-SCR-M01", + "quantity": 1, + "quantityFormula": "", + "unit": "EA", + "note": "소형용 모터" + } +] +``` + +**기존 SAM BOM 예시 (FG-SCR-001):** +```json +[ + {"unit":"M2","quantity":1,"childItemCode":"SF-SCR-F01","quantityFormula":"W*H/1000000"}, + {"unit":"M","quantity":1,"childItemCode":"SF-SCR-F02","quantityFormula":"H/1000"}, + {"unit":"EA","quantity":1,"childItemCode":"SF-SCR-M01","quantityFormula":"","note":"소형용"}, + {"unit":"EA","quantity":20,"childItemCode":"SM-B002","quantityFormula":"","note":"조립용"} +] +``` + +### 2.3 핵심 차이점 + +| 항목 | 5130 | SAM | +|------|------|-----| +| **BOM 저장 위치** | parts/parts_sub 테이블 | items.bom JSON 컬럼 | +| **연결 기준** | model_id (모델 기준) | childItemCode (품목 코드 기준) | +| **수량 계산** | 고정값 + estimate.detailJson | quantityFormula 동적 계산 | +| **단가 계산** | parts.unitprice 고정 | FormulaEvaluatorService 동적 | +| **계층 구조** | 2단계 (parts → parts_sub) | 1단계 (flat JSON array) | + +--- + +## 3. 마이그레이션 전략 + +### 3.1 접근 방식: 수동 매핑 + 템플릿 기반 + +5130의 BOM 구조와 SAM의 BOM 구조가 근본적으로 다르기 때문에, 자동 변환이 아닌 **수동 매핑 + 템플릿 기반** 접근 필요: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 전략: 완제품(FG) 유형별 BOM 템플릿 정의 │ +├─────────────────────────────────────────────────────────────────┤ +│ 1. SCREEN 완제품 → screen_bom_template │ +│ 2. STEEL 완제품 → steel_bom_template │ +│ 3. BENDING 완제품 → bending_bom_template │ +│ │ +│ 각 템플릿은 FormulaEvaluatorService 호환 JSON 형식으로 정의 │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 3.2 완제품-모델 매핑 + +**매핑 대상 (SAM items WHERE item_type='FG' AND source='5130'):** +```sql +-- SAM에서 5130에서 마이그레이션된 완제품 목록 +SELECT id, code, name, item_category +FROM items +WHERE item_type = 'FG' + AND (legacy_code IS NOT NULL OR code LIKE 'S%'); +``` + +**주요 완제품 매핑 예시:** +| SAM code | SAM name | item_category | 5130 model | +|----------|----------|---------------|------------| +| S0001 | 국민방화스크린(일체형) | SCREEN | KSS01 (스크린/SUS/벽면형) | +| S0002 | 국민방화스크린(분리형) | SCREEN | KSE01 (스크린/SUS/벽면형) | +| ... | ... | ... | ... | + +### 3.3 BOM 템플릿 정의 + +**SCREEN 완제품 BOM 템플릿:** +```json +[ + {"childItemCode": "RM-SCR-FABRIC", "quantity": 1, "quantityFormula": "W*H/1000000", "unit": "M2", "note": "스크린 원단"}, + {"childItemCode": "PT-SCR-GUIDE", "quantity": 1, "quantityFormula": "H/1000", "unit": "M", "note": "가이드레일"}, + {"childItemCode": "PT-SCR-BOTTOM", "quantity": 1, "quantityFormula": "W/1000", "unit": "M", "note": "하단바"}, + {"childItemCode": "PT-SCR-CASE", "quantity": 1, "quantityFormula": "W/1000", "unit": "M", "note": "케이스"}, + {"childItemCode": "PT-SCR-MOTOR", "quantity": 1, "quantityFormula": "", "unit": "EA", "note": "모터"} +] +``` + +--- + +## 4. 작업 절차 + +### 4.1 Phase 1: 하위 품목 확인 및 생성 + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 1.1 | BOM에 필요한 하위 품목(SF, PT, RM) 목록 정의 | ✅ | 52개 품목 정의됨 | +| 1.2 | SAM items 테이블에 하위 품목 존재 여부 확인 | ✅ | 52개 모두 존재 확인 | +| 1.3 | 누락된 하위 품목 생성 (필요시) | ✅ | 누락 품목 없음 (생성 불필요) | + +### 4.2 Phase 2: BOM 템플릿 정의 + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 2.1 | SCREEN 완제품용 BOM 템플릿 정의 | ✅ | FG-SCR-001 (14개 항목) | +| 2.2 | STEEL 완제품용 BOM 템플릿 정의 | ✅ | FG-STL-001 (12개 항목) | +| 2.3 | BENDING 완제품용 BOM 템플릿 정의 | ✅ | FG-BND-001 (6개 항목) | + +### 4.3 Phase 3: 마이그레이션 스크립트 작성 + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 3.1 | Migrate5130Bom 커맨드 생성 | ✅ | `api/app/Console/Commands/Migrate5130Bom.php` | +| 3.2 | 완제품-템플릿 매핑 로직 구현 | ✅ | item_category 기반 매핑 | +| 3.3 | items.bom 컬럼 업데이트 로직 구현 | ✅ | DB::table 직접 업데이트 | +| 3.4 | 검증 로직 구현 | ✅ | dry-run, verbose 옵션 지원 | + +### 4.4 Phase 4: 검증 및 테스트 + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 4.1 | Migrate5130Bom 커맨드 실행 | ✅ | 61건 처리 완료 | +| 4.2 | 견적 페이지에서 실제 테스트 | ⏳ | 사용자 수동 확인 필요 | +| 4.3 | 결과 문서화 | ✅ | 본 문서 업데이트 | + +--- + +## 5. 기술 상세 + +### 5.1 FormulaEvaluatorService BOM 처리 로직 + +```php +// api/app/Services/Quote/FormulaEvaluatorService.php + +// BOM JSON 필드 사용 위치: +// 1. getBomItems() - bom JSON 파싱 +// 2. calculateBomQuantity() - quantityFormula 평가 +// 3. childItemCode로 하위 품목 조회 + +// 주요 변수: +// - W0, H0: 개구부 치수 (입력값) +// - W1, H1: 제작 치수 (계산값) +// - W, H: W1, H1과 동일 +// - M: 면적 (m²) +// - K: 중량 (kg) +``` + +### 5.2 마이그레이션 스크립트 구조 + +```php +// api/app/Console/Commands/Migrate5130Bom.php + +class Migrate5130Bom extends Command +{ + protected $signature = 'migration:migrate-5130-bom + {--dry-run : 실제 변경 없이 시뮬레이션} + {--code= : 특정 품목 코드만 처리}'; + + // 1. item_category별 BOM 템플릿 정의 + private array $bomTemplates = [ + 'SCREEN' => [...], + 'STEEL' => [...], + 'BENDING' => [...] + ]; + + // 2. 완제품 조회 (5130 마이그레이션된 FG) + // 3. 템플릿 기반 BOM JSON 생성 + // 4. items.bom 컬럼 업데이트 +} +``` + +### 5.3 검증 쿼리 + +```sql +-- 마이그레이션 전: BOM이 NULL인 완제품 +SELECT code, name, item_category +FROM items +WHERE item_type = 'FG' + AND item_category IN ('SCREEN', 'STEEL', 'BENDING') + AND (bom IS NULL OR bom = '[]'); + +-- 마이그레이션 후: BOM이 있는 완제품 +SELECT code, name, item_category, JSON_LENGTH(bom) as bom_count +FROM items +WHERE item_type = 'FG' + AND item_category IN ('SCREEN', 'STEEL', 'BENDING') + AND bom IS NOT NULL + AND JSON_LENGTH(bom) > 0; +``` + +--- + +## 6. 컨펌 대기 목록 + +> 모든 승인 항목 완료 + +| # | 항목 | 변경 내용 | 영향 범위 | 상태 | +|---|------|----------|----------|------| +| 1 | BOM 템플릿 확정 | SCREEN/STEEL/BENDING별 템플릿 | 견적 계산 | ✅ 완료 | +| 2 | 하위 품목 코드 확정 | childItemCode 명명 규칙 | items 테이블 | ✅ 완료 | +| 3 | 마이그레이션 실행 | items.bom 업데이트 | 완제품 61건 | ✅ 완료 | + +--- + +## 7. 변경 이력 + +| 날짜 | 항목 | 변경 내용 | 파일 | 승인 | +|------|------|----------|------|------| +| 2025-01-20 | 초안 | 계획 문서 작성 | - | - | +| 2025-01-20 | 분석 | 5130/SAM BOM 구조 분석 완료 | - | - | +| 2025-01-20 | 스크립트 | Migrate5130Bom 커맨드 생성 | `api/app/Console/Commands/Migrate5130Bom.php` | ✅ | +| 2025-01-20 | 실행 | BOM 마이그레이션 실행 (61건) | items.bom 컬럼 | ✅ | +| 2025-01-20 | 문서화 | 결과 문서화 완료 | 본 문서 | ✅ | + +--- + +## 8. 참고 문서 + +- **FormulaEvaluatorService**: `api/app/Services/Quote/FormulaEvaluatorService.php` +- **기존 마이그레이션**: `api/app/Console/Commands/Migrate5130PriceItems.php` +- **검증 커맨드**: `api/app/Console/Commands/Verify5130Calculation.php` +- **품질 체크리스트**: `docs/standards/quality-checklist.md` + +--- + +## 9. 세션 및 메모리 관리 정책 (Serena Optimized) + +### 9.1 세션 시작 시 (Load Strategy) +```javascript +// 순차적 로드 +read_memory("5130-bom-migration-state") // 1. 상태 파악 +read_memory("5130-bom-migration-rules") // 2. 규칙 확인 +read_memory("5130-bom-migration-mappings") // 3. 매핑 확인 +``` + +### 9.2 Serena 메모리 구조 +- `5130-bom-migration-state`: { phase, progress, next_step, last_decision } +- `5130-bom-migration-rules`: BOM 템플릿 정의, 변환 규칙 +- `5130-bom-migration-mappings`: 완제품-모델 매핑 테이블 + +--- + +## 10. 검증 결과 + +> 2025-01-20 마이그레이션 실행 완료 + +### 10.1 마이그레이션 실행 결과 + +``` +📊 카테고리별 BOM 적용 현황 (tenant_id=287): + SCREEN: 35건 + STEEL: 11건 + BENDING: 15건 + +✅ BOM 적용 완료: 61건 +⏳ BOM 미적용: 0건 +``` + +### 10.2 테스트 케이스 + +| 입력값 | 예상 결과 | 실제 결과 | 상태 | +|--------|----------|----------|------| +| S0001 BOM JSON 확인 | childItemCode 5개 이상 | 14개 항목 적용됨 | ✅ | +| S0001 + W0=2500, H0=2000 | 견적 금액 > 0 | 사용자 확인 필요 | ⏳ | + +### 10.3 성공 기준 달성 현황 + +| 기준 | 달성 | 비고 | +|------|------|------| +| 완제품 BOM NULL → JSON 변환 | ✅ | 61건 변환 완료 | +| BOM JSON 형식 호환 | ✅ | FormulaEvaluatorService 호환 형식 | +| 견적 계산 정상 동작 | ⏳ | 사용자 수동 확인 필요 | + +### 10.4 BOM 템플릿 상세 + +| 카테고리 | 소스 템플릿 | BOM 항목 수 | 적용 완제품 수 | +|----------|------------|------------|--------------| +| SCREEN | FG-SCR-001 | 14개 | 35건 | +| STEEL | FG-STL-001 | 12개 | 11건 | +| BENDING | FG-BND-001 | 6개 | 15건 | + +--- + +## 11. 자기완결성 점검 결과 + +> Phase 5.5에서 수행된 자기완결성 점검 결과 + +### 11.1 체크리스트 검증 + +| # | 검증 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 1 | 작업 목적이 명확한가? | ✅ | S0001 등 BOM NULL → 견적 0원 문제 해결 | +| 2 | 성공 기준이 정의되어 있는가? | ✅ | 섹션 10.2 참조 | +| 3 | 작업 범위가 구체적인가? | ✅ | SCREEN/STEEL/BENDING 완제품 대상 | +| 4 | 의존성이 명시되어 있는가? | ✅ | FormulaEvaluatorService, 하위 품목 | +| 5 | 참고 파일 경로가 정확한가? | ✅ | 섹션 8 참조 | +| 6 | 단계별 절차가 실행 가능한가? | ✅ | 섹션 4 참조 | +| 7 | 검증 방법이 명시되어 있는가? | ✅ | 섹션 10.1 참조 | +| 8 | 모호한 표현이 없는가? | ✅ | 구체적 수치/조건 명시 | + +### 11.2 새 세션 시뮬레이션 테스트 + +| 질문 | 답변 가능 | 참조 섹션 | +|------|:--------:|----------| +| Q1. 이 작업의 목적은 무엇인가? | ✅ | 1.1 배경 | +| Q2. 어디서부터 시작해야 하는가? | ✅ | 4. 작업 절차 | +| Q3. 어떤 파일을 수정해야 하는가? | ✅ | 5.2 마이그레이션 스크립트 | +| Q4. 작업 완료 확인 방법은? | ✅ | 10. 검증 결과 | +| Q5. 막혔을 때 참고 문서는? | ✅ | 8. 참고 문서 | + +**결과**: 5/5 통과 → ✅ 자기완결성 확보 + +--- + +*이 문서는 /plan 스킬로 생성되었습니다.* \ No newline at end of file diff --git a/plans/archive/5130-sam-data-migration-plan.md b/plans/archive/5130-sam-data-migration-plan.md new file mode 100644 index 0000000..5451064 --- /dev/null +++ b/plans/archive/5130-sam-data-migration-plan.md @@ -0,0 +1,828 @@ +# 5130 → SAM 자재/수주 데이터 마이그레이션 계획 + +> **작성일**: 2025-01-19 +> **목적**: 5130 레거시 시스템의 품목(KDunitprice, price_*) 및 수주(output, output_extra) 데이터를 SAM 구조(items, orders, order_items)로 마이그레이션 +> **기준 문서**: 5130/output/_row.php, 5130/KDunitprice/_row.php, api/database/migrations/* +> **상태**: ✅ 마이그레이션 완료 (Phase 1-4 완료) + +--- + +## 📍 현재 진행 상태 + +| 항목 | 내용 | +|------|------| +| **마지막 완료 작업** | Phase 4 - 전체 데이터 마이그레이션 실행 완료 | +| **다음 작업** | 완료 (운영 검증 후 문서 아카이브) | +| **진행률** | 14/14 (100%) | +| **마지막 업데이트** | 2026-01-20 | + +--- + +## 1. 개요 + +### 1.1 배경 + +5130 레거시 시스템에서 운영 중인 자재/수주 데이터를 SAM 신규 시스템으로 마이그레이션해야 합니다. +- 5130: 플랫 테이블 구조 + JSON 컬럼으로 데이터 저장 +- SAM: 정규화된 관계형 테이블 구조 + JSON attributes 필드 + +### 1.2 기준 원칙 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 🎯 핵심 원칙 │ +├─────────────────────────────────────────────────────────────────┤ +│ 📊 데이터 (값): 5130 우선 - 실제 운영 중인 사이트 │ +│ 🏗️ 구조: SAM 우선 - 신규 정규화 설계 │ +│ 🧮 견적 수식: 동일성 유지 - 5130과 SAM 결과값 일치 필수 │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 1.3 변경 승인 정책 + +| 분류 | 예시 | 승인 | +|------|------|------:| +| ✅ 즉시 가능 | 필드 추가/변경, 마이그레이션 스크립트 작성, 문서 수정 | 불필요 | +| ⚠️ 컨펌 필요 | 테이블 구조 변경, 새 컬럼 추가, 데이터 타입 변경 | **필수** | +| 🔴 금지 | 기존 데이터 삭제, 운영 DB 직접 수정, 스키마 파괴적 변경 | 별도 협의 | + +### 1.4 준수 규칙 + +- `docs/quickstart/quick-start.md` - 빠른 시작 가이드 +- `docs/standards/quality-checklist.md` - 품질 체크리스트 +- `docs/specs/database-schema.md` - 데이터베이스 스키마 +- `api/CLAUDE.md` - API 개발 규칙 + +--- + +## 2. 테이블 매핑 개요 + +### 2.1 5130 소스 테이블 + +| 테이블 | 용도 | 주요 필드 | +|--------|------|----------| +| `KDunitprice` | 단가표 (Ecount 연동) | prodcode, item_name, item_div, spec, unit, unitprice | +| `price_raw_materials` | 원자재 단가 | JSON itemList | +| `price_bend` | 절곡 단가 | JSON itemList | +| `output` | 수주 마스터 | ~80개 필드, JSON (screenlist, slatlist, motorList 등) | +| `output_extra` | 수주 부가정보 | ~30개 필드 (parent_num으로 연결) | + +### 2.2 SAM 대상 테이블 + +| 테이블 | 용도 | item_type | +|--------|------|-----------| +| `items` | 통합 품목 마스터 | FG, PT, SM, RM, CS | +| `orders` | 수주 마스터 | - | +| `order_items` | 수주 상세 | - | +| `order_item_components` | 자재 투입 | - | + +### 2.3 매핑 관계 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 5130 → SAM │ +├─────────────────────────────────────────────────────────────────┤ +│ KDunitprice → items (SM, RM, CS) │ +│ price_raw_materials.itemList → items (RM) │ +│ price_bend.itemList → items (PT) + price tables │ +│ output → orders │ +│ output.screenlist/slatlist → order_items │ +│ output_extra → order_items.attributes │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 3. 대상 범위 + +### 3.1 Phase 1: 품목 마스터 마이그레이션 + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 1.1 | KDunitprice → items 매핑 분석 | ✅ | 10개 필드 매핑 완료 | +| 1.2 | price_raw_materials → items 매핑 | ✅ | RM 타입, itemList JSON 15개 필드 매핑 | +| 1.3 | price_bend → items 매핑 | ✅ | PT 타입, itemList JSON 18개 필드 매핑 | +| 1.4 | 품목 마이그레이션 스크립트 작성 | ✅ | `Migrate5130PriceItems.php` | +| 1.5 | 품목 데이터 검증 | ✅ | dry-run 621건 성공, item_type 분류 검증 완료 | + +### 3.2 Phase 2: 수주 마스터 마이그레이션 + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 2.1 | output → orders 필드 매핑 | ✅ | 69개 필드 분석, 상세 매핑 완료 | +| 2.2 | output JSON → order_items 변환 | ✅ | screenlist, slatlist 구조 분석 완료 | +| 2.3 | output_extra → order_items.attributes | ✅ | 33개 필드, motorList/bendList 등 | +| 2.4 | 수주 마이그레이션 스크립트 작성 | ✅ | `Migrate5130Orders.php` + `order_id_mappings` 테이블 | +| 2.5 | 수주 데이터 검증 | ✅ | dry-run 100건 성공, 필드 매핑 검증 완료 | + +### 3.3 Phase 3: 견적 로직 검증 + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 3.1 | 5130 견적 수식 분석 | ✅ | write_form_script.php + fetch_unitprice.php 분석 완료 | +| 3.2 | SAM 견적 수식 구현/검증 | ✅ | Legacy5130Calculator.php + Verify5130Calculation.php | +| 3.3 | 검증 테스트 실행 | ✅ | 5/5 테스트 케이스 통과, 100% 일치 | + +--- + +## 4. 상세 필드 매핑 + +### 4.1 KDunitprice → items + +| 5130 필드 | SAM 필드 | 타입 | 비고 | +|-----------|----------|------|------| +| prodcode | code | string | 품목코드 | +| item_name | name | string | 품목명 | +| item_div | item_type 판별 기준 | - | SM/RM/CS 분류 | +| spec | attributes.spec | JSON | 규격 | +| unit | unit | string | 단위 | +| unitprice | attributes.unit_price | JSON | 단가 | + +### 4.2 output → orders (상세 매핑) + +#### 4.2.1 기본 정보 매핑 + +| 5130 필드 | SAM 필드 | 타입 변환 | 비고 | +|-----------|----------|----------|------| +| num | options.legacy_num | int→JSON | 5130 원본 PK 보존 | +| - | id | auto | SAM 신규 PK | +| - | tenant_id | 287 | 경동기업 고정 | +| outdate | received_at | date→datetime | 수주일 | +| orderdate | options.order_date | date | 발주일 | +| outworkplace | site_name | varchar(50) | 현장명 | +| orderman | options.orderman | varchar(20) | 수주담당자 | +| con_num | client_id | int→FK | 거래처 (조회 필요) | +| outputplace | options.output_place | varchar(50) | 출고장소 | +| receiver | options.receiver | varchar(20) | 수령인 | +| phone | client_contact | varchar(15) | 연락처 | +| comment | memo | varchar(250) | 메모 | +| delivery | delivery_method_code | varchar(15) | 배송방법 | + +#### 4.2.2 상태 필드 매핑 + +| 5130 필드 | SAM 필드 | 변환 규칙 | 비고 | +|-----------|----------|----------|------| +| regist_state | status_code | '등록'→'REGISTERED' | 주 상태 | +| screen_state | options.screen_state | 그대로 | 방충망 상태 | +| slat_state | options.slat_state | 그대로 | 슬랫 상태 | +| bend_state | options.bend_state | 그대로 | 절곡 상태 | +| motor_state | options.motor_state | 그대로 | 모터 상태 | + +#### 4.2.3 수량/금액 필드 + +| 5130 필드 | SAM 필드 | 비고 | +|-----------|----------|------| +| screen_su | quantity (합산) | 방충망 수량 | +| slat_su | quantity (합산) | 슬랫 수량 | +| screen_m2 | options.screen_m2 | 방충망 면적 | +| slat_m2 | options.slat_m2 | 슬랫 면적 | +| output_extra.EstimateFinalSum | total_amount | 최종금액 | +| output_extra.EstimateDiscount | discount_amount | 할인금액 | +| output_extra.EstimateDiscountRate | discount_rate | 할인율 | + +#### 4.2.4 JSON → order_items 변환 대상 + +| 5130 JSON 필드 | order_items 유형 | 비고 | +|----------------|-----------------|------| +| screenlist | item_type='SCREEN' | 방충망 품목 | +| slatlist | item_type='SLAT' | 슬랫 품목 | +| output_extra.motorList | item_type='MOTOR' | 모터 품목 | +| output_extra.bendList | item_type='BEND' | 절곡 품목 | +| output_extra.etcList | item_type='ETC' | 기타 품목 | +| output_extra.controllerList | item_type='CTRL' | 컨트롤러 | +| deliveryfeeList | item_type='DELIVERY' | 배송비 | + +#### 4.2.5 options JSON에 보존할 필드 + +```json +{ + "legacy_num": "5130 num", + "legacy_extra_num": "output_extra num", + "orderman": "수주담당자", + "output_place": "출고장소", + "receiver": "수령인", + "secondord": "2차 주문처", + "secondordman": "2차 주문 담당자", + "secondordmantel": "2차 주문 연락처", + "screen_state": "방충망 상태", + "slat_state": "슬랫 상태", + "bend_state": "절곡 상태", + "motor_state": "모터 상태", + "screen_m2": "방충망 면적", + "slat_m2": "슬랫 면적", + "warranty": "보증서 여부", + "warrantyNum": "보증서 번호", + "lotNum": "로트번호", + "prodCode": "제품코드", + "ACI": { + "regDate": "인정검사 등록일", + "askDate": "인정검사 요청일", + "doneDate": "인정검사 완료일", + "memo": "인정검사 메모", + "check": "인정검사 체크", + "groupCode": "인정검사 그룹코드", + "groupName": "인정검사 그룹명" + }, + "pjnum": "프로젝트 번호", + "major_category": "대분류", + "position": "위치", + "makeWidth": "제작폭", + "makeHeight": "제작높이", + "maguriWing": "마구리날개" +} +``` + +### 4.3 screenlist/slatlist → order_items + +#### 4.3.1 screenlist JSON 구조 + +```json +{ + "floors": "층수", + "text1": "표시텍스트1", + "text2": "표시텍스트2 (요약)", + "memo": "메모 (재질)", + "cutwidth": "절단폭", + "cutheight": "절단높이", + "number": "수량", + "exititem": "출고여부", + "printside": "인쇄면", + "direction": "방향", + "intervalnum": "간격수", + "intervalnumsecond": "2차간격수", + "exitinterval": "출고간격", + "cover": "커버", + "drawbottom1": "하부도면1", + "drawbottom2": "하부도면2", + "drawbottom3": "하부도면3", + "draw": "도면파일", + "done_check": "완료체크", + "remain_check": "잔여체크", + "mid_check": "중간체크", + "left_check": "좌측체크", + "right_check": "우측체크" +} +``` + +#### 4.3.2 screenlist → order_items 매핑 + +| screenlist 필드 | order_items 필드 | 비고 | +|-----------------|-----------------|------| +| - | serial_no | 순번 (1부터) | +| cutwidth + 'x' + cutheight | specification | 규격 (예: 3260x4000) | +| floors | floor_code | 층수 | +| text1 | symbol_code | 기호 | +| number | quantity | 수량 | +| memo | remarks | 메모 (재질 등) | +| text2 | note | 요약 텍스트 | +| (전체) | attributes | 원본 JSON 보존 | + +#### 4.3.3 slatlist JSON 구조 + +```json +{ + "floors": "층수", + "text1": "기호 (FST-1 등)", + "text2": "요약텍스트", + "memo": "메모 (재질 EGI 1.6T 등)", + "cutwidth": "절단폭", + "cutheight": "절단높이 (총H)", + "number": "수량", + "exititem": "출고여부", + "intervalnum": "간격수 (매수)", + "hinge": "힌지", + "hingenum": "힌지수량", + "hinge_direction": "힌지방향", + "done_check": "완료체크" +} +``` + +### 4.4 output_extra 상세 매핑 + +#### 4.4.1 금액 관련 필드 + +| 5130 필드 | SAM 필드 | 비고 | +|-----------|----------|------| +| estimateTotal | orders.supply_amount | 공급가액 | +| EstimateFirstSum | options.estimate_first | 최초견적 | +| EstimateUpdatetSum | options.estimate_update | 변경견적 | +| EstimateDiffer | options.estimate_diff | 차액 | +| EstimateDiscountRate | orders.discount_rate | 할인율 | +| EstimateDiscount | orders.discount_amount | 할인금액 | +| EstimateFinalSum | orders.total_amount | 최종금액 | +| estimateSurang | options.estimate_quantity | 견적수량 | +| inspectionFee | options.inspection_fee | 검사비용 | + +#### 4.4.2 JSON 리스트 필드 (→ order_items) + +| 5130 필드 | 건수 | 구조 | SAM 변환 | +|-----------|------|------|----------| +| motorList | 7건 | col1~col8 | order_items (MOTOR) | +| bendList | 10건 | col1~col8 | order_items (BEND) | +| etcList | - | col1~col5 | order_items (ETC) | +| controllerList | - | col1~col4 | order_items (CTRL) | + +#### 4.4.3 motorList col 매핑 + +| col | 내용 | order_items 필드 | +|-----|------|-----------------| +| col1 | 품명 (전동개폐기_단상 220V) | item_name | +| col2 | 용량 (300kg) | specification | +| col3 | 규격 (380*180) | attributes.dimension | +| col4 | 인치 (5인치) | attributes.inch | +| col5 | 수량 | quantity | +| col6 | 형태 (신형) | attributes.type | +| col7 | 옵션 | attributes.option | +| col8 | 전원 (단상) | attributes.power | + +#### 4.4.4 bendList col 매핑 + +| col | 내용 | order_items 필드 | +|-----|------|-----------------| +| col1 | 품명 (가이드레일) | item_name | +| col2 | 재질 (EGI 1.6T) | specification | +| col3 | 길이 (3000) | attributes.length | +| col5 | 폭 (332) | attributes.width | +| col6 | 도면이미지 | attributes.drawing | +| col7 | 수량 | quantity | +| col8 | 비고 | remarks | + +### 4.5 견적 수식 분석 (Phase 3.1) + +> **분석 대상**: `5130/output/write_form_script.php` (JS), `5130/estimate/fetch_unitprice.php` (PHP) + +#### 4.5.1 절곡품 단가 계산 + +**함수**: `getBendPlatePrice(material, thickness, length, width, qty)` + +```javascript +// 5130/output/write_form_script.php (lines 5780-5822) +// item_bend 배열: { col1: 재질, col5: 두께, col17: 면적당단가(원/m²) } + +// 1. 재질/두께 정규화 +EGI: 1.15 → 1.2, 1.55 → 1.6 +SUS: 1.15 → 1.2, 1.55 → 1.5 + +// 2. 면적 계산 (mm² → m²) +areaM² = (length × width) / 1,000,000 + +// 3. 총액 계산 (절삭) +total = Math.floor(unitPricePerM² × areaM² × qty) +``` + +**데이터 소스**: `price_bend.itemList` → `window.item_bend` (JS 전역) + +#### 4.5.2 비인정 스크린 단가 계산 + +**함수**: 익명 함수 (tables 배열 내) + +```javascript +// 5130/output/write_form_script.php (lines 6794-6822) +// materialBasePrice에서 재질(material)로 단가 조회 + +// 1. 단가 조회 +unitprice = materialBasePrice[material] || 0 + +// 2. 수량 계산 (타입별 분기) +if (원단류) { + // 세로 기준 1000mm 단위 + surang = height / 1000 +} else { + // 일반 면적 기준 + surang = (width × height) / 1,000,000 × qty +} + +// 3. 총액 +total = unitprice × surang +``` + +**데이터 소스**: `price_raw_materials.itemList` → `window.materialBasePrice` (JS 전역) + +#### 4.5.3 철재 스라트 비인정 단가 + +**함수**: 익명 함수 (tables 배열 내) + +```javascript +// 5130/output/write_form_script.php (lines 6824-6881) + +// 1. 유형별 단가 조회 +type = 방화셔터/방범셔터/단열셔터/이중파이프/조인트바 +unitprice = materialBasePrice[type] || 0 + +// 2. 수량 계산 (유형별 분기) +if (면적 기준: 방화/방범/단열/이중파이프) { + surang = (width × height) / 1,000,000 × qty +} else if (수량 기준: 조인트바) { + surang = qty +} + +// 3. 총액 +total = unitprice × surang +``` + +#### 4.5.4 전동 개폐기/제어기 조회 + +**함수**: `lookupMotorPrice(row)`, `lookupControllerPrice(row)` + +```javascript +// 5130/output/write_form_script.php (lines 6886-6920) + +// KDunitprice 테이블에서 조회 +// unitInfo: { prodcode → unitprice } 매핑 + +// 전동 개폐기 +unitprice = lookupMotorPrice(row) +// → row 데이터(용량, 전원, 형태 등)로 KDunitprice 조회 + +// 제어기 +unitprice = lookupControllerPrice(row) +// → row 데이터(유형, 규격)로 KDunitprice 조회 +``` + +**데이터 소스**: `KDunitprice` → `window.unitInfo` (JS 전역) + +#### 4.5.5 모터 용량 계산 (핵심 로직) + +**함수**: `calculateMotorSpec($item, $weight, $BracketInch)` (PHP) + +```php +// 5130/estimate/fetch_unitprice.php (lines 200-350) + +// 1. 품목 유형 판별 +$ItemSel = (substr($item['col4'], 0, 2) === 'KS' || + substr($item['col4'], 0, 2) === 'KW') + ? '스크린' : '철재'; + +// 2. 용량 결정 테이블 +// 스크린: 150K ~ 600K +// 철재: 300K ~ 1000K +// Weight + BracketInch 조합으로 용량 결정 + +// 3. 브라켓 사이즈 매핑 +300-400K → 530×320 +500-600K → 600×350 +800-1000K → 690×390 +``` + +#### 4.5.6 기타 계산 함수 + +| 함수 | 용도 | 계산식 | +|------|------|--------| +| `calculateGuidrail()` | 가이드레일 수량 | `col17 / 3490` (기본 길이) | +| `calculateShaft()` | 샤프트 단가 | `col19 × 수량`, 길이별 조회 | +| `calculatePipe()` | 파이프 단가 | `col4(길이)`, `col2(규격)`으로 `col8(단가)` 조회 | +| `slatPrice()` | 인정 슬랫 단가 | `price_raw_materials.col13` | +| `unapprovedSlatPrice()` | 비인정 슬랫 단가 | `price_raw_materials.col15` | + +#### 4.5.7 전역 데이터 구조 (JS) + +```javascript +// 5130/output/write_form.php에서 PHP→JS 전달 + +// 비인정 자재 단가 (재질 → 단가) +window.materialBasePrice = { + "실리카": 12000, + "폴리에스터": 8500, + // ... +}; + +// 비인정 자재 코드 (재질 → 코드) +window.materialBaseCode = { + "실리카": "RM001", + // ... +}; + +// 절곡품 단가표 +var item_bend = [ + { col1: "EGI", col5: 1.2, col17: 45000 }, + { col1: "SUS", col5: 1.5, col17: 85000 }, + // ... +]; + +// KDunitprice 단가 (prodcode → unitprice) +window.unitInfo = { + "MOT300": 250000, + "MOT500": 380000, + // ... +}; +``` + +#### 4.5.8 SAM 구현 시 고려사항 + +| 구분 | 5130 방식 | SAM 구현 방향 | +|------|----------|--------------| +| 단가 조회 | JS 전역 변수 | Service 클래스 + DB 쿼리 | +| 면적 계산 | JS (mm² → m²) | PHP Helper 함수 | +| 두께 매핑 | JS 하드코딩 | 설정 테이블 or Enum | +| 모터 용량 | PHP 조건문 | 룰 엔진 or 매핑 테이블 | +| 반올림/절삭 | `Math.floor()` | `floor()` 동일 적용 | + +--- + +## 5. 작업 절차 + +### 5.1 단계별 절차 + +``` +Step 1: 품목 마스터 분석 (Phase 1.1-1.3) +├── KDunitprice 테이블 구조 상세 분석 +├── price_raw_materials JSON 구조 분석 +├── price_bend JSON 구조 분석 +└── SAM items 테이블과 매핑 확정 + +Step 2: 품목 마이그레이션 (Phase 1.4-1.5) +├── 마이그레이션 스크립트 작성 (Artisan Command) +├── 테스트 데이터로 검증 +└── 전체 데이터 마이그레이션 + +Step 3: 수주 마스터 분석 (Phase 2.1-2.3) +├── output 테이블 80개 필드 분석 +├── JSON 필드 (screenlist 등) 구조 분석 +├── output_extra 연결 관계 분석 +└── SAM orders/order_items 매핑 확정 + +Step 4: 수주 마이그레이션 (Phase 2.4-2.5) +├── 마이그레이션 스크립트 작성 +├── JSON → 관계형 변환 로직 구현 +├── 테스트 데이터로 검증 +└── 전체 데이터 마이그레이션 + +Step 5: 견적 로직 검증 (Phase 3) +├── 5130 견적 계산 JS 분석 +├── SAM에서 동일 로직 구현/검증 +└── 샘플 데이터로 결과 비교 +``` + +### 5.2 분석 템플릿 + +```markdown +### [테이블명] 분석 + +**현재 상태 (5130):** +- 테이블: [테이블명] +- 필드 수: [N]개 +- 레코드 수: [N]건 + +**목표 상태 (SAM):** +- 테이블: [테이블명] +- 매핑 필드: [N]개 + +**필드 매핑:** +| 5130 | SAM | 변환 로직 | +|------|-----|----------| +| | | | + +**특이사항:** +- [ ] JSON 변환 필요 여부 +- [ ] 타입 변환 필요 여부 +- [ ] 기본값 처리 방법 +``` + +--- + +## 6. 컨펌 대기 목록 + +> 테이블 구조 변경 등 승인 필요 항목 + +| # | 항목 | 변경 내용 | 영향 범위 | 상태 | +|---|------|----------|----------|------| +| - | - | - | - | - | + +--- + +## 7. 변경 이력 + +| 날짜 | 항목 | 변경 내용 | 파일 | 승인 | +|------|------|----------|------|------| +| 2025-01-19 | 초안 | 문서 초안 작성 | - | - | +| 2025-01-19 | Phase 1.1 | KDunitprice → items 매핑 분석 완료 | - | - | +| 2025-01-19 | Phase 1.2 | price_raw_materials → items 매핑 분석 완료 (itemList JSON 15필드) | - | - | +| 2025-01-19 | Phase 1.3 | price_bend → items 매핑 분석 완료 (itemList JSON 18필드) | - | - | +| 2025-01-19 | Phase 1.4 | 품목 마이그레이션 스크립트 작성 완료 | `api/app/Console/Commands/Migrate5130PriceItems.php` | - | +| 2026-01-19 | Phase 2.4 | 수주 마이그레이션 스크립트 작성 완료 | `api/app/Console/Commands/Migrate5130Orders.php`, `api/database/migrations/2026_01_19_202830_create_order_id_mappings_table.php` | - | +| 2026-01-19 | Phase 3.1 | 5130 견적 수식 분석 완료 | `5130/output/write_form_script.php`, `5130/estimate/fetch_unitprice.php` | - | +| 2026-01-19 | Phase 3.2 | SAM 견적 수식 구현 완료 | `api/app/Helpers/Legacy5130Calculator.php`, `api/app/Console/Commands/Verify5130Calculation.php` | - | +| 2026-01-19 | Phase 3.3 | 견적 수식 검증 테스트 실행 | 5/5 테스트 케이스 100% 일치 | - | +| 2026-01-20 | 준비 완료 | Phase 1-3 모든 준비 작업 완료, 실행 대기 | 13/13 작업 완료 | - | +| 2026-01-20 | Phase 4 | 전체 마이그레이션 실행 완료 | items 608건, orders 24,424건, order_items 43,900건 | ✅ | + +--- + +## 8. 참고 문서 + +### 8.1 5130 소스 코드 + +- **수주 폼**: `5130/output/write_form.php` (1176줄) +- **견적 계산 JS**: `5130/output/write_form_script.php` (302KB, ~7000줄) +- **단가 조회 PHP**: `5130/estimate/fetch_unitprice.php` (875줄) +- **output 필드**: `5130/output/_row.php` (~80개 필드) +- **output_extra 필드**: `5130/output/_row_extra.php` (~30개 필드) +- **단가표 필드**: `5130/KDunitprice/_row.php` + +### 8.2 SAM 스키마 + +- **items 테이블**: `api/database/migrations/2025_12_13_152507_create_items_table.php` +- **orders 테이블**: `api/database/migrations/2024_11_19_000001_create_orders_table.php` +- **order_items 테이블**: `api/database/migrations/2024_11_19_000002_create_order_items_table.php` + +### 8.3 SAM 모델 + +- **Order 모델**: `api/app/Models/Orders/Order.php` +- **OrderItem 모델**: `api/app/Models/Orders/OrderItem.php` +- **Item 모델**: `api/app/Models/Items/Item.php` + +--- + +## 9. 세션 및 메모리 관리 정책 + +### 9.1 세션 시작 시 (Load Strategy) +```javascript +// 순차적 로드 +read_memory("5130-migration-state") // 1. 상태 파악 +read_memory("5130-migration-mappings") // 2. 매핑 정보 로드 +read_memory("5130-migration-rules") // 3. 규칙 확인 +``` + +### 9.2 작업 중 관리 (Context Defense) +| 컨텍스트 잔량 | Action | 내용 | +|--------------|--------|------| +| **30% 이하** | 🛠 **Snapshot** | `write_memory("5130-migration-snapshot", "진행상황")` | +| **20% 이하** | 🧹 **Context Purge** | `write_memory("5130-migration-active", "현재 작업")` | +| **10% 이하** | 🛑 **Stop & Save** | 최종 상태 저장 후 세션 교체 권고 | + +### 9.3 Serena 메모리 구조 +- `5130-migration-state`: { phase, progress, next_step } (JSON 구조) +- `5130-migration-mappings`: 테이블/필드 매핑 정보 (Text) +- `5130-migration-rules`: 변환 규칙, 타입 매핑 (Text) + +--- + +## 10. 검증 결과 + +### 10.1 Phase 1 품목 마이그레이션 검증 (2025-01-19) + +#### 소스 데이터 카운트 +| 테이블 | 총 건수 | 활성 건수 | 최신 버전 | +|--------|---------|----------|----------| +| KDunitprice | 603 | 601 (NULL/0) | - | +| price_raw_materials | 14 | 6 | 2025-06-18 | +| price_bend | 3 | 3 | 2025-03-09 | + +#### dry-run 검증 결과 +| 테이블 | Total | Migrated | Skipped | 결과 | +|--------|-------|----------|---------|:----:| +| KDunitprice | 601 | 601 | 0 | ✅ | +| price_raw_materials | 13 | 13 | 0 | ✅ | +| price_bend | 7 | 7 | 0 | ✅ | +| **합계** | **621** | **621** | **0** | ✅ | + +#### item_type 분류 검증 +| item_div | 예상 | 실제 | 결과 | +|----------|------|------|:----:| +| [상품] | FG | FG | ✅ | +| [제품] | FG | FG | ✅ | +| [반제품] | PT | PT | ✅ | +| [부재료] | SM | SM | ✅ | +| [원재료] | RM | RM | ✅ | +| [무형상품] | CS | CS | ✅ | + +#### item_div 분포 (KDunitprice 601건) +| item_div | 건수 | item_type | +|----------|------|-----------| +| [상품] | 259 | FG | +| [제품] | 193 | FG | +| [반제품] | 73 | PT | +| [부재료] | 48 | SM | +| [원재료] | 24 | RM | +| [무형상품] | 4 | CS | + +### 10.2 Phase 2 수주 마이그레이션 검증 (2026-01-19) + +#### 소스 데이터 현황 +| 테이블/필드 | 총 건수 | 비고 | +|-------------|---------|------| +| output | 24,584 | 전체 수주 | +| output (screenlist 있음) | 9,392 | 방충망 포함 | +| output (slatlist 있음) | 1,955 | 슬랫 포함 | +| output_extra (motorList 있음) | 7 | 모터 포함 | +| output_extra (bendList 있음) | 10 | 절곡 포함 | + +#### dry-run 검증 결과 +| 항목 | 건수 | 결과 | 비고 | +|------|------|:----:|------| +| orders | 100 | ✅ | 100건 테스트 성공 | +| order_items (screen) | - | ⏳ | 실제 실행 후 확인 | +| order_items (slat) | - | ⏳ | 실제 실행 후 확인 | +| order_items (motor) | 0 | ✅ | motorList 없는 범위 | +| order_items (bend) | 0 | ✅ | bendList 없는 범위 | + +#### 샘플 데이터 매핑 검증 +**샘플 num=25810** +| 5130 필드 | 값 | SAM 필드 | 변환 결과 | 검증 | +|-----------|-----|----------|----------|:----:| +| outdate | 2025-12-15 | received_at | 2025-12-15 00:00:00 | ✅ | +| outworkplace | IFC | site_name | IFC | ✅ | +| regist_state | 등록 | status_code | REGISTERED | ✅ | +| phone | 010-5231-3134 | client_contact | 010-5231-3134 | ✅ | +| comment | 실리카1틀/... | memo | 실리카1틀/... | ✅ | +| delivery | 직접배차 | delivery_method_code | 직접배차 | ✅ | +| screenlist[0].cutwidth×cutheight | 3260×4000 | specification | 3260x4000 | ✅ | +| screenlist[0].number | 1 | quantity | 1 | ✅ | +| screenlist[0].memo | 실리카 | remarks | 실리카 | ✅ | + +**motorList/bendList 구조 검증** +| col | motorList 매핑 | bendList 매핑 | 검증 | +|-----|---------------|--------------|:----:| +| col1 | item_name (전동개폐기_단상 220V) | item_name (가이드레일) | ✅ | +| col2 | specification (300kg) | specification (EGI 1.6T) | ✅ | +| col3 | attributes.dimension (380*180) | attributes.length (3000) | ✅ | +| col5 | quantity (2) | attributes.width (332) | ✅ | +| col6 | attributes.type (신형) | attributes.drawing (이미지경로) | ✅ | +| col7 | attributes.option | quantity (1) | ✅ | +| col8 | attributes.power (단상) | remarks | ✅ | + +### 10.3 데이터 정합성 요약 + +| 테이블 | 5130 건수 | SAM 건수 | 일치 | 비고 | +|--------|----------|----------|:----:|------| +| KDunitprice → items | 601 | (dry-run) | ✅ | Phase 1 검증 완료 | +| price_raw_materials → items | 13 | (dry-run) | ✅ | 최신 버전만 | +| price_bend → items | 7 | (dry-run) | ✅ | 최신 버전만 | +| output → orders | 24,584 | (dry-run) | ✅ | 100건 테스트 성공 | +| screenlist → order_items | 9,392+ | (대기) | ⏳ | 실제 마이그레이션 후 확인 | +| slatlist → order_items | 1,955+ | (대기) | ⏳ | 실제 마이그레이션 후 확인 | + +### 10.4 견적 수식 검증 (2026-01-19) + +#### 검증 도구 +- **Legacy5130Calculator.php**: 5130 호환 계산 헬퍼 클래스 +- **Verify5130Calculation.php**: 검증 Artisan 커맨드 +- **실행**: `php artisan migration:verify-5130-calculation --W0=3000 --H0=2500 --type=screen` + +#### 테스트 결과 + +| 케이스 | W0×H0 | 유형 | W1 (5130/SAM) | H1 (5130/SAM) | M (m²) | K (kg) | 결과 | +|--------|-------|------|---------------|---------------|--------|--------|:----:| +| 스크린 소형 | 1500×1200 | screen | 1640/1640 | 1550/1550 | 2.542 | 26.34 | ✅ | +| 스크린 중형 | 3000×2500 | screen | 3140/3140 | 2850/2850 | 8.949 | 60.41 | ✅ | +| 스크린 대형 | 5000×4000 | screen | 5140/5140 | 4350/4350 | 22.359 | 115.57 | ✅ | +| 철재 중형 | 2000×1800 | steel | 2110/2110 | 2150/2150 | 4.5365 | 113.41 | ✅ | +| 철재 대형 | 4000×3500 | steel | 4110/4110 | 3850/3850 | 15.8235 | 395.59 | ✅ | + +#### 검증 수식 + +``` +스크린 (screen): +├── W1 = W0 + 140 (마진) +├── H1 = H0 + 350 (마진) +├── M = (W1 × H1) / 1,000,000 (m²) +└── K = (M × 2) + (W0 / 1000 × 14.17) (kg) + +철재 (steel): +├── W1 = W0 + 110 (마진) +├── H1 = H0 + 350 (마진) +├── M = (W1 × H1) / 1,000,000 (m²) +└── K = M × 25 (kg) +``` + +#### 모터 용량/브라켓 사이즈 검증 + +| 케이스 | 중량(K) | 브라켓인치 | 모터용량 | 브라켓사이즈 | +|--------|---------|-----------|---------|-------------| +| 스크린 중형 | 60.41 | 124" | 600K | 600×350 | +| 철재 중형 | 113.41 | 84" | 1000K | 690×390 | + +**결과**: 5/5 테스트 케이스 통과 → ✅ **견적 수식 100% 일치 확인** + +--- + +## 11. 자기완결성 점검 결과 + +### 11.1 체크리스트 검증 + +| # | 검증 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 1 | 작업 목적이 명확한가? | ✅ | 5130→SAM 데이터 마이그레이션 | +| 2 | 성공 기준이 정의되어 있는가? | ✅ | 데이터 정합성 + 견적 동일성 | +| 3 | 작업 범위가 구체적인가? | ✅ | Phase 1-3 정의됨 | +| 4 | 의존성이 명시되어 있는가? | ✅ | 5130 소스 + SAM 스키마 | +| 5 | 참고 파일 경로가 정확한가? | ✅ | 섹션 8 참조 | +| 6 | 단계별 절차가 실행 가능한가? | ✅ | 섹션 5 참조 | +| 7 | 검증 방법이 명시되어 있는가? | ✅ | 섹션 10 참조 | +| 8 | 모호한 표현이 없는가? | ✅ | 구체적 테이블/필드 명시 | + +### 11.2 새 세션 시뮬레이션 테스트 + +| 질문 | 답변 가능 | 참조 섹션 | +|------|:--------:|----------| +| Q1. 이 작업의 목적은 무엇인가? | ✅ | 1.1 배경 | +| Q2. 어디서부터 시작해야 하는가? | ✅ | 5.1 단계별 절차 | +| Q3. 어떤 테이블을 매핑해야 하는가? | ✅ | 2. 테이블 매핑 개요 | +| Q4. 작업 완료 확인 방법은? | ✅ | 10. 검증 결과 | +| Q5. 막혔을 때 참고 문서는? | ✅ | 8. 참고 문서 | + +**결과**: 5/5 통과 → ✅ 자기완결성 확보 + +--- + +*이 문서는 /sc:plan 스킬로 생성되었습니다.* \ No newline at end of file diff --git a/plans/archive/bidding-api-implementation-plan.md b/plans/archive/bidding-api-implementation-plan.md new file mode 100644 index 0000000..e0c3135 --- /dev/null +++ b/plans/archive/bidding-api-implementation-plan.md @@ -0,0 +1,817 @@ +# 입찰관리(Bidding) API 구현 계획 + +> **작성일**: 2026-01-19 +> **목적**: 견적 → 입찰 전환 기능 구현 및 테스트용 더미데이터 생성 +> **기준 문서**: React 목업 타입 (`react/src/components/business/construction/bidding/types.ts`) +> **상태**: ✅ 완료 (Serena ID: bidding-api-state) + +--- + +## 📍 현재 진행 상태 + +| 항목 | 내용 | +|------|------| +| **마지막 완료 작업** | Phase 4.3 - Pint 코드 포맷팅 및 Swagger 재생성 | +| **다음 작업** | 사용자 수동 실행 (마이그레이션, 시더) | +| **진행률** | 12/12 (100%) | +| **마지막 업데이트** | 2026-01-19 | + +--- + +## 1. 개요 + +### 1.1 배경 + +**업무 흐름:** +``` +현장설명회 → 견적관리 → [견적완료] → 입찰관리 → 계약관리 → 기성/정산 + ↑ + 전환 기능 필요 +``` + +현재 React 프론트엔드의 입찰관리(`/construction/project/bidding`)는 **목업 데이터**를 사용 중입니다. +견적(Quote) API는 이미 구현되어 있으므로, 입찰(Bidding) API를 새로 구현하고 견적 → 입찰 전환 기능을 추가해야 합니다. + +**현재 상태:** +| 구분 | 견적(Estimate/Quote) | 입찰(Bidding) | +|------|---------------------|---------------| +| API Model | ✅ `Estimate.php` | ❌ 없음 | +| API Migration | ✅ `estimates` 테이블 | ❌ 없음 | +| API Endpoint | ✅ `/api/v1/quotes` | ❌ 없음 | +| React | ✅ API 연동 완료 | ❌ 목업 상태 | + +### 1.2 기준 원칙 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 🎯 핵심 원칙 │ +├─────────────────────────────────────────────────────────────────┤ +│ 1. SAM API Rules 엄격 준수 (Service-First, FormRequest) │ +│ 2. Multi-tenancy 필수 (BelongsToTenant) │ +│ 3. React 목업 타입과 100% 호환 │ +│ 4. 견적 데이터 참조 (복사가 아닌 FK 연결) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 1.3 변경 승인 정책 + +| 분류 | 예시 | 승인 | +|------|------|------| +| ✅ 즉시 가능 | 새 테이블 생성, 새 API 추가, Seeder 작성 | 불필요 | +| ⚠️ 컨펌 필요 | 기존 quotes 테이블 수정, 비즈니스 로직 변경 | **필수** | +| 🔴 금지 | 기존 API 삭제, 파괴적 변경 | 별도 협의 | + +### 1.4 준수 규칙 + +- `api/CLAUDE.md` - SAM API Development Rules +- `docs/standards/quality-checklist.md` - 품질 체크리스트 +- `docs/guides/swagger-guide.md` - Swagger 문서화 + +--- + +## 2. 대상 범위 + +### 2.1 Phase 1: Database & Model (Day 1) + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 1.1 | `biddings` 테이블 마이그레이션 생성 | ✅ | `2026_01_19_100000_create_biddings_table.php` | +| 1.2 | `Bidding` Model 생성 | ✅ | BelongsToTenant, SoftDeletes | +| 1.3 | 더미데이터 Seeder 생성 | ✅ | 10건 테스트 데이터 | + +### 2.2 Phase 2: API Implementation (Day 2) + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 2.1 | BiddingService 생성 | ✅ | CRUD + 통계 | +| 2.2 | BiddingController 생성 | ✅ | | +| 2.3 | FormRequest 생성 | ✅ | Filter, Update, Status, BulkDelete | +| 2.4 | Routes 등록 | ✅ | `/api/v1/biddings` | + +### 2.3 Phase 3: 견적 → 입찰 전환 (Day 2-3) + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 3.1 | QuoteService에 `convertToBidding()` 추가 | ✅ | 기존 코드에 메서드 추가 | +| 3.2 | 전환 API 엔드포인트 추가 | ✅ | `POST /quotes/{id}/convert-to-bidding` | + +### 2.4 Phase 4: Swagger & 검증 (Day 3) + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 4.1 | Swagger 문서 작성 | ✅ | `BiddingApi.php` | +| 4.2 | i18n 메시지 추가 | ✅ | message.php, error.php | +| 4.3 | Pint 코드 포맷팅 | ✅ | 9 style issues fixed | + +--- + +## 3. 작업 절차 + +### 3.1 단계별 절차 + +``` +Step 1: Database Schema +├── biddings 테이블 마이그레이션 작성 +├── 마이그레이션 실행 +└── Seeder로 더미데이터 생성 + +Step 2: Model & Service +├── Bidding Model 생성 (BelongsToTenant, SoftDeletes) +├── BiddingService 생성 (CRUD, stats, filter) +└── BiddingController 생성 + +Step 3: API Routes +├── routes/api.php에 biddings 라우트 추가 +├── FormRequest 클래스 생성 +└── API 테스트 + +Step 4: 견적 → 입찰 전환 +├── QuoteService에 convertToBidding() 추가 +├── 전환 API 엔드포인트 추가 +└── 전환 테스트 + +Step 5: Documentation +├── Swagger 문서 작성 +├── API 문서 검증 +└── Pint 실행 +``` + +### 3.2 데이터베이스 스키마 + +```sql +-- biddings 테이블 +CREATE TABLE biddings ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID', + + -- 기본 정보 + bidding_code VARCHAR(50) NOT NULL COMMENT '입찰번호', + quote_id BIGINT UNSIGNED NULL COMMENT '연결된 견적 ID (quotes.id)', + + -- 거래처/현장 + client_id BIGINT UNSIGNED NULL COMMENT '거래처 ID', + client_name VARCHAR(100) NULL COMMENT '거래처명 (스냅샷)', + project_name VARCHAR(200) NULL COMMENT '현장명', + + -- 입찰 정보 + bidding_date DATE NULL COMMENT '입찰일', + bid_date DATE NULL COMMENT '입찰일 (레거시 호환)', + submission_date DATE NULL COMMENT '투찰일', + confirm_date DATE NULL COMMENT '확정일', + total_count INT DEFAULT 0 COMMENT '총 개소', + bidding_amount DECIMAL(15,2) DEFAULT 0 COMMENT '입찰금액', + + -- 상태 + status VARCHAR(20) DEFAULT 'waiting' COMMENT '상태 (waiting/submitted/failed/invalid/awarded/hold)', + + -- 입찰자 + bidder_id BIGINT UNSIGNED NULL COMMENT '입찰자 ID', + bidder_name VARCHAR(50) NULL COMMENT '입찰자명 (스냅샷)', + + -- 공사기간 + construction_start_date DATE NULL COMMENT '공사 시작일', + construction_end_date DATE NULL COMMENT '공사 종료일', + vat_type VARCHAR(20) DEFAULT 'excluded' COMMENT '부가세 (included/excluded)', + + -- 비고 + remarks TEXT NULL COMMENT '비고', + + -- 견적 데이터 스냅샷 (JSON) + expense_items JSON NULL COMMENT '공과 항목 스냅샷', + estimate_detail_items JSON NULL COMMENT '견적 상세 항목 스냅샷', + + -- 감사 + created_by BIGINT UNSIGNED NULL COMMENT '생성자', + updated_by BIGINT UNSIGNED NULL COMMENT '수정자', + deleted_by BIGINT UNSIGNED NULL COMMENT '삭제자', + created_at TIMESTAMP NULL, + updated_at TIMESTAMP NULL, + deleted_at TIMESTAMP NULL, + + -- 인덱스 + INDEX idx_tenant_id (tenant_id), + INDEX idx_status (status), + INDEX idx_bidding_date (bidding_date), + INDEX idx_quote_id (quote_id), + UNIQUE INDEX idx_bidding_code (tenant_id, bidding_code) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +``` + +### 3.3 API 엔드포인트 설계 + +| Method | Path | 설명 | +|--------|------|------| +| GET | `/api/v1/biddings` | 목록 조회 (필터, 페이지네이션) | +| GET | `/api/v1/biddings/stats` | 통계 조회 | +| GET | `/api/v1/biddings/{id}` | 단건 조회 | +| PUT | `/api/v1/biddings/{id}` | 수정 | +| DELETE | `/api/v1/biddings/{id}` | 삭제 | +| DELETE | `/api/v1/biddings/bulk` | 일괄 삭제 | +| POST | `/api/v1/quotes/{id}/convert-to-bidding` | 견적 → 입찰 전환 | + +**참고**: 입찰은 별도 등록 없음 (견적완료 시 자동 전환) + +### 3.4 타입 매핑 (React → API) + +| React (camelCase) | API (snake_case) | DB Column | +|-------------------|------------------|-----------| +| `id` | `id` | `id` | +| `biddingCode` | `bidding_code` | `bidding_code` | +| `partnerId` | `client_id` | `client_id` | +| `partnerName` | `client_name` | `client_name` | +| `projectName` | `project_name` | `project_name` | +| `biddingDate` | `bidding_date` | `bidding_date` | +| `totalCount` | `total_count` | `total_count` | +| `biddingAmount` | `bidding_amount` | `bidding_amount` | +| `bidDate` | `bid_date` | `bid_date` | +| `submissionDate` | `submission_date` | `submission_date` | +| `confirmDate` | `confirm_date` | `confirm_date` | +| `status` | `status` | `status` | +| `bidderId` | `bidder_id` | `bidder_id` | +| `bidderName` | `bidder_name` | `bidder_name` | +| `remarks` | `remarks` | `remarks` | +| `estimateId` | `quote_id` | `quote_id` | +| `estimateCode` | `quote_number` | (join) | + +### 3.5 상태값 매핑 + +| 값 | 한글 | 설명 | +|----|------|------| +| `waiting` | 입찰대기 | 견적 전환 후 초기 상태 | +| `submitted` | 투찰 | 투찰서 제출 완료 | +| `failed` | 탈락 | 입찰 실패 | +| `invalid` | 유찰 | 입찰 무효 | +| `awarded` | 낙찰 | 입찰 성공 | +| `hold` | 보류 | 검토 대기 | + +### 3.6 기존 quotes 테이블 스키마 (연결용) + +> `biddings.quote_id` → `quotes.id` FK 연결 + +```sql +-- quotes 테이블 핵심 컬럼 (api/database/migrations/2025_12_04_164542_create_quotes_table.php) +quotes ( + id BIGINT PRIMARY KEY, + tenant_id BIGINT NOT NULL, + quote_type ENUM('manufacturing', 'construction'), -- 'construction' 필터 + quote_number VARCHAR(50), -- 견적번호 (예: KD-SC-251204-01) + registration_date DATE, + client_id BIGINT, -- 거래처 ID + client_name VARCHAR(100), -- 거래처명 + site_name VARCHAR(200), -- 현장명 + total_amount DECIMAL(15,2), -- 최종 금액 + status ENUM('pending','draft','sent','approved','rejected','finalized','converted'), + site_briefing_id BIGINT, -- 현장설명회 연결 + options JSON, -- { summary_items, expense_items, detail_items, price_adjustment_data } + ... +) +``` + +**Quote 상태 상수** (api/app/Models/Quote/Quote.php): +- `pending` → 견적대기 (현장설명회에서 자동생성) +- `finalized` → 확정 (입찰 전환 가능) +- `converted` → 전환완료 + +### 3.7 API 응답 형식 (JSON) + +#### 목록 조회 응답 (GET /biddings) +```json +{ + "success": true, + "message": "message.fetched", + "data": { + "data": [ + { + "id": 1, + "bidding_code": "BID-2025-001", + "client_id": 1, + "client_name": "이사대표", + "project_name": "광장 아파트", + "bidding_date": "2025-01-25", + "total_count": 15, + "bidding_amount": 71000000, + "bid_date": "2025-01-20", + "submission_date": "2025-01-22", + "confirm_date": "2025-01-25", + "status": "awarded", + "bidder_id": 1, + "bidder_name": "홍길동", + "remarks": "", + "quote_id": 1, + "quote_number": "EST-2025-001", + "created_at": "2025-01-01T00:00:00.000000Z" + } + ], + "current_page": 1, + "per_page": 20, + "total": 10, + "last_page": 1 + } +} +``` + +#### 통계 응답 (GET /biddings/stats) +```json +{ + "success": true, + "message": "message.fetched", + "data": { + "total": 10, + "waiting": 3, + "awarded": 3 + } +} +``` + +#### 단건 조회 응답 (GET /biddings/{id}) +```json +{ + "success": true, + "message": "message.fetched", + "data": { + "id": 1, + "bidding_code": "BID-2025-001", + "client_id": 1, + "client_name": "이사대표", + "project_name": "광장 아파트", + "bidding_date": "2025-01-25", + "total_count": 15, + "bidding_amount": 71000000, + "status": "awarded", + "construction_start_date": "2025-02-01", + "construction_end_date": "2025-04-30", + "vat_type": "excluded", + "expense_items": [ + { "id": "1", "name": "설계비", "amount": 5000000 }, + { "id": "2", "name": "운반비", "amount": 3000000 } + ], + "estimate_detail_items": [ + { "id": "1", "no": 1, "name": "방화문", "material": "SUS304", "width": 1000, "height": 2100, "quantity": 10, ... } + ], + "quote": { + "id": 1, + "quote_number": "EST-2025-001" + } + } +} +``` + +### 3.8 convertToBidding() 상세 로직 + +```php +/** + * 견적 → 입찰 전환 + * + * @param int $quoteId 견적 ID + * @return Bidding 생성된 입찰 + */ +public function convertToBidding(int $quoteId): Bidding +{ + $tenantId = $this->tenantId(); + $userId = $this->apiUserId(); + + // 1. 견적 조회 (quote_type=construction, status=finalized) + $quote = Quote::where('tenant_id', $tenantId) + ->where('id', $quoteId) + ->where('quote_type', 'construction') + ->where('status', 'finalized') + ->firstOrFail(); + + // 2. 이미 입찰이 존재하는지 확인 + $existingBidding = Bidding::where('quote_id', $quoteId)->first(); + if ($existingBidding) { + throw new BadRequestHttpException(__('error.bidding_already_exists')); + } + + // 3. 입찰 데이터 생성 + $bidding = Bidding::create([ + 'tenant_id' => $tenantId, + 'bidding_code' => $this->generateBiddingCode($tenantId), + 'quote_id' => $quote->id, + + // 거래처/현장 정보 복사 + 'client_id' => $quote->client_id, + 'client_name' => $quote->client_name, + 'project_name' => $quote->site_name, + + // 금액 정보 + 'bidding_amount' => $quote->total_amount, + 'total_count' => $quote->items->count(), + + // 날짜 + 'bidding_date' => now()->toDateString(), + + // 상태 + 'status' => 'waiting', + + // 현장설명회에서 공사기간 가져오기 + 'construction_start_date' => $quote->siteBriefing?->construction_start_date, + 'construction_end_date' => $quote->siteBriefing?->construction_end_date, + 'vat_type' => $quote->siteBriefing?->vat_type ?? 'excluded', + + // 견적 옵션 데이터 스냅샷 + 'expense_items' => $quote->options['expense_items'] ?? [], + 'estimate_detail_items' => $quote->options['detail_items'] ?? [], + + 'created_by' => $userId, + ]); + + // 4. 견적 상태 업데이트 (선택적) + // $quote->update(['status' => 'converted']); + + return $bidding; +} + +/** + * 입찰번호 자동 생성 (BID-YYYY-NNN) + */ +private function generateBiddingCode(int $tenantId): string +{ + $year = now()->format('Y'); + $prefix = "BID-{$year}-"; + + $lastBidding = Bidding::where('tenant_id', $tenantId) + ->where('bidding_code', 'like', "{$prefix}%") + ->orderBy('id', 'desc') + ->first(); + + $sequence = 1; + if ($lastBidding) { + $lastNum = (int) substr($lastBidding->bidding_code, -3); + $sequence = $lastNum + 1; + } + + return $prefix . str_pad($sequence, 3, '0', STR_PAD_LEFT); +} +``` + +### 3.9 Service/Controller 패턴 (SAM 표준) + +**Controller 패턴** (api/app/Http/Controllers): +```php + $this->service->index($request->validated())); + } + + public function show(int $id) + { + return ApiResponse::handle(fn () => $this->service->show($id)); + } + + public function update(BiddingUpdateRequest $request, int $id) + { + return ApiResponse::handle(fn () => $this->service->update($id, $request->validated())); + } + + public function destroy(int $id) + { + return ApiResponse::handle(fn () => $this->service->destroy($id)); + } + + public function stats() + { + return ApiResponse::handle(fn () => $this->service->stats()); + } +} +``` + +**Service 패턴** (api/app/Services): +```php +tenantId(); // 필수 + $query = Bidding::where('tenant_id', $tenantId); + // ... 필터, 정렬, 페이지네이션 + return $query->paginate($params['size'] ?? 20); + } + + public function show(int $id): Bidding + { + $tenantId = $this->tenantId(); + return Bidding::where('tenant_id', $tenantId) + ->with(['quote']) + ->findOrFail($id); + } + + public function stats(): array + { + $tenantId = $this->tenantId(); + return [ + 'total' => Bidding::where('tenant_id', $tenantId)->count(), + 'waiting' => Bidding::where('tenant_id', $tenantId)->where('status', 'waiting')->count(), + 'awarded' => Bidding::where('tenant_id', $tenantId)->where('status', 'awarded')->count(), + ]; + } +} +``` + +### 3.10 더미데이터 (Seeder용 10건) + +> React 목업 기준 (`react/src/components/business/construction/bidding/actions.ts`) + +```php +// api/database/seeders/BiddingSeeder.php +$biddings = [ + [ + 'bidding_code' => 'BID-2025-001', + 'client_name' => '이사대표', + 'project_name' => '광장 아파트', + 'bidding_date' => '2025-01-25', + 'total_count' => 15, + 'bidding_amount' => 71000000, + 'bid_date' => '2025-01-20', + 'submission_date' => '2025-01-22', + 'confirm_date' => '2025-01-25', + 'status' => 'awarded', + 'bidder_name' => '홍길동', + 'remarks' => '', + ], + [ + 'bidding_code' => 'BID-2025-002', + 'client_name' => '야사건설', + 'project_name' => '대림아파트', + 'bidding_date' => '2025-01-20', + 'total_count' => 22, + 'bidding_amount' => 100000000, + 'bid_date' => '2025-01-18', + 'submission_date' => null, + 'confirm_date' => null, + 'status' => 'waiting', + 'bidder_name' => '김철수', + 'remarks' => '', + ], + [ + 'bidding_code' => 'BID-2025-003', + 'client_name' => '여의건설', + 'project_name' => '현장아파트', + 'bidding_date' => '2025-01-18', + 'total_count' => 18, + 'bidding_amount' => 85000000, + 'bid_date' => '2025-01-15', + 'submission_date' => '2025-01-16', + 'confirm_date' => '2025-01-18', + 'status' => 'awarded', + 'bidder_name' => '홍길동', + 'remarks' => '', + ], + [ + 'bidding_code' => 'BID-2025-004', + 'client_name' => '이사대표', + 'project_name' => '송파타워', + 'bidding_date' => '2025-01-15', + 'total_count' => 30, + 'bidding_amount' => 120000000, + 'bid_date' => '2025-01-12', + 'submission_date' => '2025-01-13', + 'confirm_date' => '2025-01-15', + 'status' => 'failed', + 'bidder_name' => '이영희', + 'remarks' => '가격 경쟁력 부족', + ], + [ + 'bidding_code' => 'BID-2025-005', + 'client_name' => '야사건설', + 'project_name' => '강남센터', + 'bidding_date' => '2025-01-12', + 'total_count' => 25, + 'bidding_amount' => 95000000, + 'bid_date' => '2025-01-10', + 'submission_date' => '2025-01-11', + 'confirm_date' => null, + 'status' => 'submitted', + 'bidder_name' => '홍길동', + 'remarks' => '', + ], + [ + 'bidding_code' => 'BID-2025-006', + 'client_name' => '여의건설', + 'project_name' => '목동센터', + 'bidding_date' => '2025-01-10', + 'total_count' => 12, + 'bidding_amount' => 78000000, + 'bid_date' => '2025-01-08', + 'submission_date' => '2025-01-09', + 'confirm_date' => '2025-01-10', + 'status' => 'invalid', + 'bidder_name' => '김철수', + 'remarks' => '입찰 조건 미충족', + ], + [ + 'bidding_code' => 'BID-2025-007', + 'client_name' => '이사대표', + 'project_name' => '서초타워', + 'bidding_date' => '2025-01-08', + 'total_count' => 35, + 'bidding_amount' => 150000000, + 'bid_date' => '2025-01-05', + 'submission_date' => null, + 'confirm_date' => null, + 'status' => 'waiting', + 'bidder_name' => '이영희', + 'remarks' => '', + ], + [ + 'bidding_code' => 'BID-2025-008', + 'client_name' => '야사건설', + 'project_name' => '청담프로젝트', + 'bidding_date' => '2025-01-05', + 'total_count' => 40, + 'bidding_amount' => 200000000, + 'bid_date' => '2025-01-03', + 'submission_date' => '2025-01-04', + 'confirm_date' => '2025-01-05', + 'status' => 'awarded', + 'bidder_name' => '홍길동', + 'remarks' => '', + ], + [ + 'bidding_code' => 'BID-2025-009', + 'client_name' => '여의건설', + 'project_name' => '잠실센터', + 'bidding_date' => '2025-01-03', + 'total_count' => 20, + 'bidding_amount' => 88000000, + 'bid_date' => '2025-01-01', + 'submission_date' => null, + 'confirm_date' => null, + 'status' => 'hold', + 'bidder_name' => '김철수', + 'remarks' => '검토 대기 중', + ], + [ + 'bidding_code' => 'BID-2025-010', + 'client_name' => '이사대표', + 'project_name' => '역삼빌딩', + 'bidding_date' => '2025-01-01', + 'total_count' => 10, + 'bidding_amount' => 65000000, + 'bid_date' => '2024-12-28', + 'submission_date' => null, + 'confirm_date' => null, + 'status' => 'waiting', + 'bidder_name' => '이영희', + 'remarks' => '', + ], +]; + +// 통계 요약: +// - total: 10건 +// - waiting: 3건 (BID-002, 007, 010) +// - awarded: 3건 (BID-001, 003, 008) +// - submitted: 1건 (BID-005) +// - failed: 1건 (BID-004) +// - invalid: 1건 (BID-006) +// - hold: 1건 (BID-009) +``` + +--- + +## 4. 상세 작업 내용 + +> 각 Phase 진행 후 이 섹션에 상세 내용 추가 + +### 4.1 Phase 1: Database & Model + +#### 1.1 마이그레이션 파일 생성 +- **상태**: ⏳ 대기 +- **파일**: `api/database/migrations/2026_01_19_XXXXXX_create_biddings_table.php` + +#### 1.2 Model 생성 +- **상태**: ⏳ 대기 +- **파일**: `api/app/Models/Bidding/Bidding.php` + +#### 1.3 Seeder 생성 +- **상태**: ⏳ 대기 +- **파일**: `api/database/seeders/BiddingSeeder.php` +- **데이터**: React 목업 기준 10건 + +--- + +## 5. 컨펌 대기 목록 + +> API 내부 로직 변경 등 승인 필요 항목 + +| # | 항목 | 변경 내용 | 영향 범위 | 상태 | +|---|------|----------|----------|------| +| 1 | QuoteService 수정 | `convertToBidding()` 메서드 추가 | api/Quote | ⏳ 대기 | + +--- + +## 6. 변경 이력 + +| 날짜 | 항목 | 변경 내용 | 파일 | 승인 | +|------|------|----------|------|------| +| 2026-01-19 | - | 문서 초안 작성 | - | - | + +--- + +## 7. 참고 문서 + +- **SAM API Rules**: `api/CLAUDE.md` +- **품질 체크리스트**: `docs/standards/quality-checklist.md` +- **Swagger 가이드**: `docs/guides/swagger-guide.md` +- **React 목업 타입**: `react/src/components/business/construction/bidding/types.ts` +- **React 목업 데이터**: `react/src/components/business/construction/bidding/actions.ts` +- **기존 견적 API**: `react/src/components/business/construction/estimates/actions.ts` + +--- + +## 8. 세션 및 메모리 관리 정책 (Serena Optimized) + +### 8.1 세션 시작 시 (Load Strategy) +```javascript +read_memory("bidding-api-state") // 1. 상태 파악 +read_memory("bidding-api-snapshot") // 2. 사고 흐름 복구 +``` + +### 8.2 작업 중 관리 (Context Defense) +| 컨텍스트 잔량 | Action | 내용 | +|--------------|--------|------| +| **30% 이하** | 🛠 Snapshot | 현재까지 코드 변경점 저장 | +| **20% 이하** | 🧹 Context Purge | 활성 심볼 저장 | +| **10% 이하** | 🛑 Stop & Save | 최종 상태 저장 | + +### 8.3 Serena 메모리 구조 +- `bidding-api-state`: { phase, progress, next_step } +- `bidding-api-snapshot`: 현재까지의 코드 변경점 요약 + +--- + +## 9. 검증 결과 + +> 작업 완료 후 이 섹션에 검증 결과 추가 + +### 9.1 API 테스트 케이스 + +| 엔드포인트 | 입력 | 예상 결과 | 실제 결과 | 상태 | +|-----------|------|----------|----------|------| +| GET /biddings | - | 목록 반환 | | ⏳ | +| GET /biddings/stats | - | 통계 반환 | | ⏳ | +| GET /biddings/{id} | id=1 | 단건 반환 | | ⏳ | +| PUT /biddings/{id} | 수정 데이터 | 수정 성공 | | ⏳ | +| POST /quotes/{id}/convert-to-bidding | quote_id | 입찰 생성 | | ⏳ | + +### 9.2 성공 기준 달성 현황 + +| 기준 | 달성 | 비고 | +|------|------|------| +| Bidding API CRUD 동작 | ⏳ | | +| 견적 → 입찰 전환 동작 | ⏳ | | +| 더미데이터 10건 생성 | ⏳ | | +| Swagger 문서 완성 | ⏳ | | +| Pint 통과 | ⏳ | | + +--- + +## 10. 자기완결성 점검 결과 + +### 10.1 체크리스트 검증 + +| # | 검증 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 1 | 작업 목적이 명확한가? | ✅ | 견적→입찰 전환 + 더미데이터 | +| 2 | 성공 기준이 정의되어 있는가? | ✅ | 9.2 참조 | +| 3 | 작업 범위가 구체적인가? | ✅ | Phase 1-4 정의 | +| 4 | 의존성이 명시되어 있는가? | ✅ | quotes API 의존 | +| 5 | 참고 파일 경로가 정확한가? | ✅ | 7. 참고 문서 | +| 6 | 단계별 절차가 실행 가능한가? | ✅ | 3.1 절차 | +| 7 | 검증 방법이 명시되어 있는가? | ✅ | 9.1 테스트 케이스 | +| 8 | 모호한 표현이 없는가? | ✅ | 구체적 파일/API 명시 | + +### 10.2 새 세션 시뮬레이션 테스트 + +| 질문 | 답변 가능 | 참조 섹션 | +|------|:--------:|----------| +| Q1. 이 작업의 목적은 무엇인가? | ✅ | 1.1 배경 | +| Q2. 어디서부터 시작해야 하는가? | ✅ | 현재 진행 상태 + 3.1 | +| Q3. 어떤 파일을 수정해야 하는가? | ✅ | 2. 대상 범위 | +| Q4. 작업 완료 확인 방법은? | ✅ | 9. 검증 결과 | +| Q5. 막혔을 때 참고 문서는? | ✅ | 7. 참고 문서 | + +**결과**: 5/5 통과 → ✅ 자기완결성 확보 + +--- + +*이 문서는 /sc:plan 스킬로 생성되었습니다.* \ No newline at end of file diff --git a/plans/construction-api-integration-plan.md b/plans/archive/construction-api-integration-plan.md similarity index 100% rename from plans/construction-api-integration-plan.md rename to plans/archive/construction-api-integration-plan.md diff --git a/plans/erp-api-development-plan-d1.0-changes.md b/plans/archive/erp-api-development-plan-d1.0-changes.md similarity index 100% rename from plans/erp-api-development-plan-d1.0-changes.md rename to plans/archive/erp-api-development-plan-d1.0-changes.md diff --git a/plans/l2-permission-management-plan.md b/plans/archive/l2-permission-management-plan.md similarity index 100% rename from plans/l2-permission-management-plan.md rename to plans/archive/l2-permission-management-plan.md diff --git a/plans/mes-integration-analysis-plan.md b/plans/archive/mes-integration-analysis-plan.md similarity index 100% rename from plans/mes-integration-analysis-plan.md rename to plans/archive/mes-integration-analysis-plan.md diff --git a/plans/mng-quote-formula-development-plan.md b/plans/archive/mng-quote-formula-development-plan.md similarity index 100% rename from plans/mng-quote-formula-development-plan.md rename to plans/archive/mng-quote-formula-development-plan.md diff --git a/plans/notification-sound-system-plan.md b/plans/archive/notification-sound-system-plan.md similarity index 100% rename from plans/notification-sound-system-plan.md rename to plans/archive/notification-sound-system-plan.md diff --git a/plans/order-management-plan.md b/plans/archive/order-management-plan.md similarity index 100% rename from plans/order-management-plan.md rename to plans/archive/order-management-plan.md diff --git a/plans/process-management-plan.md b/plans/archive/process-management-plan.md similarity index 100% rename from plans/process-management-plan.md rename to plans/archive/process-management-plan.md diff --git a/plans/quote-auto-calculation-development-plan.md b/plans/archive/quote-auto-calculation-development-plan.md similarity index 100% rename from plans/quote-auto-calculation-development-plan.md rename to plans/archive/quote-auto-calculation-development-plan.md diff --git a/plans/react-fcm-push-notification-plan.md b/plans/archive/react-fcm-push-notification-plan.md similarity index 100% rename from plans/react-fcm-push-notification-plan.md rename to plans/archive/react-fcm-push-notification-plan.md diff --git a/plans/react-server-component-audit-plan.md b/plans/archive/react-server-component-audit-plan.md similarity index 100% rename from plans/react-server-component-audit-plan.md rename to plans/archive/react-server-component-audit-plan.md diff --git a/plans/work-order-plan.md b/plans/archive/work-order-plan.md similarity index 100% rename from plans/work-order-plan.md rename to plans/archive/work-order-plan.md diff --git a/plans/dashboard-api-integration-plan.md b/plans/dashboard-api-integration-plan.md new file mode 100644 index 0000000..63d035c --- /dev/null +++ b/plans/dashboard-api-integration-plan.md @@ -0,0 +1,578 @@ +# Dashboard API 연동 개발 계획 + +> **작성일**: 2026-01-20 +> **목적**: CEO Dashboard 페이지의 목업 데이터 → 실제 API 연동 +> **Serena ID**: dashboard-api-state + +--- + +## 📍 현재 상태 요약 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 전체 진행률: 45% (5/11 섹션 완료) │ +├─────────────────────────────────────────────────────────────────┤ +│ ✅ Phase 1 완료 - 기존 API 연동 (프론트엔드) │ +│ ⏳ Phase 2 대기 - 신규 API 개발 필요 (백엔드) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +| 구분 | 섹션 | 데이터 소스 | 상태 | +|:---:|------|:----------:|:----:| +| Phase 1 | 일일 일보 (DailyReport) | API | ✅ | +| Phase 1 | 미수금 현황 (Receivable) | API | ✅ | +| Phase 1 | 채권추심 현황 (DebtCollection) | API | ✅ | +| Phase 1 | 당월 예상 지출 (MonthlyExpense) | API | ✅ | +| Phase 1 | 카드/가지급금 관리 (CardManagement) | API | ✅ | +| **Phase 2** | **오늘의 이슈 (TodayIssue)** | mockData | ⏳ | +| **Phase 2** | **현황판 (StatusBoard)** | mockData | ⏳ | +| **Phase 2** | **접대비 현황 (Entertainment)** | mockData | ⏳ | +| **Phase 2** | **복리후생비 현황 (Welfare)** | mockData | ⏳ | +| **Phase 2** | **부가세 현황 (Vat)** | mockData | ⏳ | +| **Phase 2** | **캘린더 (Calendar)** | mockData | ⏳ | + +--- + +## Phase 1 완료 내역 + +### 생성된 파일 + +| 파일 | 설명 | +|------|------| +| `react/src/lib/api/dashboard/types.ts` | API 응답 타입 정의 (5개 엔드포인트) | +| `react/src/lib/api/dashboard/transformers.ts` | API → Frontend 변환 함수 + CheckPoint 생성 | +| `react/src/hooks/useCEODashboard.ts` | 통합 Dashboard Hook (병렬 API 호출) | +| `react/src/lib/api/dashboard/index.ts` | 모듈 export | + +### 수정된 파일 + +| 파일 | 변경 내용 | +|------|----------| +| `CEODashboard.tsx` | `useCEODashboard` Hook 연동, mockData fallback 패턴 | + +### 연동된 API 엔드포인트 + +| 섹션 | 프론트 호출 경로 | 백엔드 실제 경로 | +|------|-----------------|-----------------| +| DailyReport | `/api/proxy/daily-report/summary` | `DailyReportService::summary()` | +| Receivable | `/api/proxy/receivables/summary` | `ReceivablesService::summary()` | +| DebtCollection | `/api/proxy/bad-debts/summary` | `BadDebtService::summary()` | +| MonthlyExpense | `/api/proxy/expected-expenses/summary` | `ExpectedExpenseService::summary()` | +| CardManagement | `/api/proxy/card-transactions/summary` | `CardTransactionService::summary()` | + +### API 불일치 사항 (fallback 처리) + +| 섹션 | 이슈 | 처리 방식 | +|------|------|----------| +| MonthlyExpense | `by_transaction_type` 필드로 제공 | purchase/card/bill 키로 분류 | +| CardManagement | 가지급금, 법인세 예상 가중 등 미제공 | mockData fallback 사용 | + +--- + +## Phase 2 개발 계획 (신규 API 필요) + +### 2.1 오늘의 이슈 (TodayIssue) + +#### 기능 설명 + +대시보드 상단에 표시되는 실시간 이벤트 목록. 각 이벤트는 뱃지, 내용, 시간, 관련 페이지 링크로 구성. + +#### 현재 mockData 구조 + +``` +todayIssueList: [ + { + id: string, + badge: string, // "수주 성공", "주식 이슈", "직정 제고", "세금 신고", "결재 요청", "기타" + content: string, // "A전자 신규 수주 450,000,000원 확정" + time: string, // "10분 전", "1시간 전", "어제" + date: string, // "2026-01-16" + needsApproval: boolean, // 결재 필요 여부 + path: string // 관련 페이지 경로 + } +] +``` + +#### 개발 방향 제안 + +**방향 A: 통합 이벤트 테이블 신규 생성** + +| 장점 | 단점 | +|------|------| +| 단일 API로 모든 이슈 조회 가능 | 신규 테이블 설계 필요 | +| 이벤트 타입별 필터링 용이 | 각 도메인에서 이벤트 생성 로직 추가 필요 | +| 확장성 좋음 | 실시간성 유지를 위한 트리거/큐 필요 | + +``` +테이블: dashboard_events +- id, tenant_id +- event_type: enum (order, receivable, stock, tax, approval, etc.) +- badge: string +- content: string +- metadata: json (금액, 거래처명 등) +- related_path: string +- needs_approval: boolean +- created_at +``` + +**방향 B: 각 도메인 API 조합 (Aggregation)** + +| 장점 | 단점 | +|------|------| +| 기존 API 재활용 | 여러 API 호출 필요 (성능) | +| 신규 테이블 불필요 | 프론트에서 데이터 병합 로직 필요 | +| 도메인별 독립성 유지 | 일관된 포맷 변환 필요 | + +``` +호출할 API 목록: +- /orders/recent-events (수주) +- /receivables/overdue-alerts (미수금 연체) +- /stock/low-alerts (재고 부족) +- /tax/deadlines (세금 신고 기한) +- /approvals/pending (결재 대기) +``` + +**방향 C: 이벤트 큐 기반 실시간 시스템** + +| 장점 | 단점 | +|------|------| +| 실시간 푸시 가능 | 인프라 복잡도 증가 | +| 확장성 최고 | Redis/Queue 추가 필요 | +| 알림 시스템과 통합 가능 | 개발 공수 큼 | + +#### 데이터 소스 후보 + +| 뱃지 | 데이터 소스 | 조건 | +|------|------------|------| +| 수주 성공 | `orders` 테이블 | status = 'confirmed', 최근 N일 | +| 주식 이슈 (미수금) | `receivables` 테이블 | overdue_days > 0 | +| 직정 제고 (재고) | `stock_items` 테이블 | quantity < safety_stock | +| 세금 신고 | `tax_schedules` 테이블 | deadline 임박 | +| 결재 요청 | `approvals` 테이블 | status = 'pending' | +| 지출예상내역서 | `expense_requests` 테이블 | status = 'pending' | + +#### 권장 사항 + +- **MVP**: 방향 B (기존 API 조합)로 시작 +- **확장**: 추후 방향 A로 마이그레이션 고려 + +--- + +### 2.2 현황판 (StatusBoard) + +#### 기능 설명 + +각 업무 영역별 미처리 건수를 카드 형태로 표시. 클릭 시 해당 페이지로 이동. + +#### 현재 mockData 구조 + +``` +todayIssue: [ + { + id: string, + label: string, // "수주", "채권 추심", "안전 재고" 등 + count: number|string, // 3 또는 "부가세 신고 D-15" + path: string, // 이동할 페이지 경로 + isHighlighted: boolean // 강조 표시 여부 + } +] +``` + +#### 개발 방향 제안 + +**방향 A: 단일 집계 API** + +``` +GET /api/dashboard/status-board + +응답: +{ + items: [ + { key: "orders", label: "수주", count: 3, path: "/sales/order-management-sales" }, + { key: "debt_collection", label: "채권 추심", count: 3, path: "/accounting/bad-debt-collection" }, + { key: "safety_stock", label: "안전 재고", count: 3, path: "/material/stock-status", isHighlighted: true }, + { key: "tax_report", label: "세금 신고", count: "부가세 신고 D-15", path: "/accounting/tax" }, + ... + ] +} +``` + +| 장점 | 단점 | +|------|------| +| 단일 API 호출 | 백엔드에서 여러 테이블 집계 필요 | +| 프론트 로직 단순 | 새 항목 추가 시 백엔드 수정 필요 | + +**방향 B: 설정 기반 동적 집계** + +``` +1. dashboard_status_items 테이블에 항목 정의 +2. 각 항목별 count_query (SQL 또는 서비스 메서드) 지정 +3. API에서 동적으로 집계하여 반환 +``` + +| 장점 | 단점 | +|------|------| +| 관리자가 항목 추가/수정 가능 | 구현 복잡도 증가 | +| 유연성 높음 | 쿼리 성능 관리 필요 | + +#### 집계 대상 테이블 + +| 항목 | 테이블 | 집계 조건 | +|------|--------|----------| +| 수주 | `orders` | status = 'pending' AND tenant_id | +| 채권 추심 | `bad_debts` | status IN ('collecting', 'legal_action') | +| 안전 재고 | `stock_items` | quantity < safety_stock | +| 세금 신고 | `tax_schedules` | D-day 계산 | +| 신규 업체 등록 | `vendors` | status = 'pending_approval' | +| 연차 | `vacation_requests` | status = 'pending' | +| 발주 | `purchase_orders` | status = 'pending' | +| 결재 요청 | `approvals` | status = 'pending' | + +#### 권장 사항 + +- 방향 A로 시작 (단일 집계 API) +- 항목은 하드코딩으로 시작, 추후 설정 테이블로 분리 가능 + +--- + +### 2.3 접대비 현황 (Entertainment) + +#### 기능 설명 + +세무 규정에 따른 접대비 한도 및 사용 현황. 분기별 한도 관리 필요. + +#### 현재 mockData 구조 + +``` +entertainment: { + cards: [ + { label: "매출", amount: 30530000000 }, + { label: "{1사분기} 접대비 총 한도", amount: 40123000 }, + { label: "{1사분기} 접대비 잔여한도", amount: 30123000 }, + { label: "{1사분기} 접대비 사용금액", amount: 10000000 } + ], + checkPoints: [...] // AI 분석 메시지 +} +``` + +#### 세무 규정 (접대비 한도 계산) + +``` +기본 한도: 3,600만원 (중소기업 기준) +매출 추가 한도: +- 100억 이하: 매출 × 0.3% +- 100~500억: 100억 초과분 × 0.2% +- 500억 초과: 500억 초과분 × 0.03% + +분기별 한도 = 연간 한도 ÷ 4 +``` + +#### 개발 방향 제안 + +**방향 A: 전용 서비스 클래스** + +``` +EntertainmentExpenseService +├── getQuarterlyLimit(year, quarter) // 분기별 한도 계산 +├── getUsedAmount(year, quarter) // 분기별 사용액 집계 +├── getRemainingLimit(year, quarter) // 잔여 한도 +├── getSummary() // 대시보드용 요약 +└── generateCheckPoints() // AI 분석 메시지 생성 +``` + +**방향 B: 기존 회계 시스템 확장** + +- `expenses` 테이블에서 접대비 계정 필터링 +- 한도 계산 로직만 별도 서비스로 분리 + +#### 필요 데이터 + +| 데이터 | 소스 | 비고 | +|--------|------|------| +| 연간 매출 | `orders` 또는 `sales_summary` | 한도 계산용 | +| 접대비 사용액 | `expenses` | account_code = '접대비' | +| 거래처 정보 | `expense_details` | 접대비 증빙용 | + +#### CheckPoint 생성 규칙 + +| 상황 | 타입 | 메시지 예시 | +|------|------|------------| +| 한도 85% 미만 | info | "여유 있게 운영 중입니다" | +| 한도 85~100% | warning | "잔여 한도 600만원입니다. 점검 필요" | +| 한도 초과 | error | "초과분은 손금불산입됩니다" | +| 거래처 정보 누락 | error | "3건의 거래처 정보가 누락되었습니다" | + +--- + +### 2.4 복리후생비 현황 (Welfare) + +#### 기능 설명 + +복리후생비 한도 및 사용 현황. 직원 수 기반 한도 계산. + +#### 현재 mockData 구조 + +``` +welfare: { + cards: [ + { label: "당해년도 복리후생비 한도", amount: 30123000 }, + { label: "{1사분기} 복리후생비 총 한도", amount: 10123000 }, + { label: "{1사분기} 복리후생비 잔여한도", amount: 5123000 }, + { label: "{1사분기} 복리후생비 사용금액", amount: 5123000 } + ], + checkPoints: [...] +} +``` + +#### 한도 계산 방식 옵션 + +**방식 1: 고정 금액 기준** +- 설정된 연간 한도를 분기별로 분배 +- 예: 연간 3,000만원 → 분기당 750만원 + +**방식 2: 직원 수 기준 (비율)** +- 직원 1인당 월 N만원 기준 +- 예: 50명 × 20만원 × 3개월 = 3,000만원/분기 + +#### 개발 방향 제안 + +``` +WelfareExpenseService +├── getAnnualLimit() // 연간 한도 (설정값 또는 계산) +├── getQuarterlyLimit(quarter) // 분기별 한도 +├── getUsedAmount(quarter) // 분기별 사용액 +├── getPerEmployeeAverage() // 1인당 평균 사용액 +├── getSummary() // 대시보드용 요약 +└── generateCheckPoints() // AI 분석 메시지 +``` + +#### 필요 데이터 + +| 데이터 | 소스 | 비고 | +|--------|------|------| +| 직원 수 | `employees` | active 상태 | +| 복리후생비 사용액 | `expenses` | account_code = '복리후생비' | +| 한도 설정 | `company_settings` | 연간 한도 또는 1인당 기준 | + +#### CheckPoint 생성 규칙 + +| 상황 | 타입 | 메시지 예시 | +|------|------|------------| +| 1인당 업계 평균 이내 | success | "업계 평균(15~25만원) 내 정상 운영 중" | +| 식대 비과세 한도 초과 | error | "식대가 월 25만원으로 비과세 한도(20만원) 초과" | +| 분기 한도 85% 이상 | warning | "한도 소진 임박" | + +--- + +### 2.5 부가세 현황 (Vat) + +#### 기능 설명 + +부가세 신고 예상 금액 및 관련 이슈 표시. + +#### 현재 mockData 구조 + +``` +vat: { + cards: [ + { label: "매출세액", amount: 3050000000 }, + { label: "매입세액", amount: 2050000000 }, + { label: "예상 납부세액", amount: 110000000 }, + { label: "세금계산서 미발행", amount: 3, unit: "건" } + ], + checkPoints: [...] +} +``` + +#### 개발 방향 제안 + +**방향 A: 전용 부가세 서비스** + +``` +VatService +├── getSalesTax(period) // 매출세액 집계 +├── getPurchaseTax(period) // 매입세액 집계 +├── getEstimatedPayment(period)// 예상 납부/환급세액 +├── getUnissuedInvoices() // 미발행 세금계산서 +├── getSummary() // 대시보드용 요약 +└── generateCheckPoints() // AI 분석 메시지 +``` + +**방향 B: 기존 세금계산서 시스템 확장** + +- 발행/수취 세금계산서에서 세액 집계 +- 미발행 건수 조회 추가 + +#### 필요 데이터 + +| 데이터 | 소스 | 비고 | +|--------|------|------| +| 매출세액 | `tax_invoices` (발행) | type = 'sales', 합계 × 10% | +| 매입세액 | `tax_invoices` (수취) | type = 'purchase', 합계 × 10% | +| 미발행 건 | `orders` 또는 `sales` | 세금계산서 미연결 건 | + +#### CheckPoint 생성 규칙 + +| 상황 | 타입 | 메시지 예시 | +|------|------|------------| +| 환급 예상 | success | "예상 환급세액 520만원" | +| 납부 예상 (전기 대비 증가) | info | "전기 대비 12.9% 증가" | +| 미발행 세금계산서 존재 | warning | "3건 미발행, 발행 필요" | +| 신고 기한 임박 | error | "신고 기한 D-3" | + +#### 부가세 신고 기간 + +| 신고 유형 | 과세 기간 | 신고 기한 | +|----------|----------|----------| +| 1기 예정 | 1/1 ~ 3/31 | 4/25 | +| 1기 확정 | 1/1 ~ 6/30 | 7/25 | +| 2기 예정 | 7/1 ~ 9/30 | 10/25 | +| 2기 확정 | 7/1 ~ 12/31 | 다음해 1/25 | + +--- + +### 2.6 캘린더 (Calendar) + +#### 기능 설명 + +회사 일정 표시 및 관리. 부서별, 개인별 일정 지원. + +#### 현재 mockData 구조 + +``` +calendarSchedules: [ + { + id: string, + title: string, + startDate: string, // "2026-01-01" + endDate: string, // "2026-01-04" + startTime?: string, // "09:00" + endTime?: string, // "12:00" + type: string, // "schedule", "order", "construction" + department?: string, // 부서명 + personName?: string // 담당자명 + } +] +``` + +#### 개발 방향 제안 + +**방향 A: 전용 일정 테이블** + +``` +테이블: calendar_schedules +- id, tenant_id +- title +- start_date, end_date +- start_time, end_time (nullable, 종일 여부) +- type: enum (schedule, order, construction, vacation, tax, etc.) +- department_id (nullable) +- user_id (nullable, 담당자) +- color (nullable) +- content (nullable, 상세 내용) +- created_by, created_at, updated_at +``` + +**방향 B: 기존 데이터 연동 (읽기 전용)** + +각 도메인의 기존 데이터를 캘린더 형식으로 변환 + +| 타입 | 소스 테이블 | 변환 규칙 | +|------|------------|----------| +| 수주 납기 | `orders` | due_date → startDate | +| 공사 일정 | `constructions` | start_date, end_date | +| 세금 신고 | `tax_schedules` | deadline → startDate | +| 휴가 | `vacation_requests` | start_date, end_date | + +**방향 C: 하이브리드 (A + B)** + +- 직접 등록 일정: 전용 테이블 +- 시스템 일정: 각 도메인에서 자동 생성 +- 통합 API에서 병합하여 제공 + +#### API 설계 + +``` +GET /api/calendar/schedules?year=2026&month=1 +POST /api/calendar/schedules (일정 생성) +PUT /api/calendar/schedules/{id} (일정 수정) +DELETE /api/calendar/schedules/{id} (일정 삭제) +``` + +#### 권장 사항 + +- **MVP**: 방향 A (전용 테이블)로 CRUD 구현 +- **확장**: 추후 방향 C로 시스템 일정 연동 + +--- + +## 개발 우선순위 제안 + +| 순위 | 섹션 | 이유 | +|:---:|------|------| +| 1 | **현황판 (StatusBoard)** | 기존 데이터 집계만으로 구현 가능, 공수 적음 | +| 2 | **캘린더 (Calendar)** | 독립적인 CRUD, 다른 섹션과 의존성 없음 | +| 3 | **오늘의 이슈 (TodayIssue)** | StatusBoard 로직 재활용 가능 | +| 4 | **부가세 현황 (Vat)** | 세금계산서 데이터 기반, 로직 명확 | +| 5 | **접대비 현황 (Entertainment)** | 세무 로직 포함, 한도 계산 복잡 | +| 6 | **복리후생비 현황 (Welfare)** | 접대비와 유사한 패턴, 함께 개발 권장 | + +--- + +## 공통 개발 패턴 + +### API 응답 형식 + +```json +{ + "success": true, + "data": { + "cards": [...], + "checkPoints": [...] + }, + "message": null +} +``` + +### CheckPoint 구조 + +```json +{ + "id": "unique-id", + "type": "error|warning|success|info", + "message": "메시지 내용", + "highlights": [ + { "text": "강조할 텍스트", "color": "red|green|blue" } + ] +} +``` + +### 색상 체계 (AI 리포트) + +| 색상 | 의미 | 적용 기준 | +|:---:|:---:|----------| +| 🔴 error | 경고 | 한도 초과, 즉각 조치 필요 | +| 🟠 warning | 주의 | 한도 85~100%, 기한 임박 | +| 🟢 success | 긍정 | 목표 달성, 입금/회수 발생 | +| 🔵 info | 양호 | 정상 범위, 안정적 | + +--- + +## 참고 문서 + +| 문서 | 경로 | +|------|------| +| AI 리포트 색상 체계 | `docs/plans/AI_리포트_키워드_색상체계_가이드_v1.4.md` | +| Hook 패턴 예제 | `react/src/hooks/useClientList.ts` | +| Transform 예제 | `react/src/lib/api/dashboard/transformers.ts` | +| Proxy 라우트 | `react/src/app/api/proxy/[...path]/route.ts` | + +--- + +## 변경 이력 + +| 날짜 | 내용 | +|------|------| +| 2026-01-20 | 초기 분석 문서 작성 | +| 2026-01-20 | Phase 1 완료 (5개 섹션 API 연동) | +| 2026-01-20 | Phase 2 개발 계획 상세화: 각 섹션별 개발 방향, 데이터 소스, 권장 사항 추가 | \ No newline at end of file diff --git a/plans/dev-toolbar-plan.md b/plans/dev-toolbar-plan.md new file mode 100644 index 0000000..89a9873 --- /dev/null +++ b/plans/dev-toolbar-plan.md @@ -0,0 +1,358 @@ +# DevToolbar - 견적→출하 테스트 자동화 도구 계획 + +> **작성일**: 2026-01-20 +> **목적**: 견적→수주→작업지시→완료→출하 전체 플로우를 빠르게 테스트하기 위한 자동 데이터 입력 도구 +> **기준 문서**: 각 폼 컴포넌트 (QuoteRegistration, OrderRegistration, WorkOrderCreate, ShipmentCreate) +> **상태**: 🔄 진행중 (Serena ID: dev-toolbar-state) + +--- + +## 📍 현재 진행 상태 + +| 항목 | 내용 | +|------|------| +| **마지막 완료 작업** | Phase 1 완료 (기반 구조 생성) | +| **다음 작업** | 2.1 QuoteRegistration에 useDevFill 연결 | +| **진행률** | 3/8 (37.5%) | +| **마지막 업데이트** | 2026-01-20 | + +--- + +## 1. 개요 + +### 1.1 배경 +- 견적 → 수주 → 작업지시 → 완료 → 출하까지 전체 프로세스 테스트 시 매번 수동 데이터 입력 필요 +- 영업 데모 시 빠른 플로우 시연 필요 +- 테스트 완료 후 쉽게 제거 가능해야 함 + +### 1.2 기준 원칙 +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 🎯 핵심 원칙 │ +├─────────────────────────────────────────────────────────────────┤ +│ 1. 독립적 구현 - 기존 컴포넌트 최소 수정 │ +│ 2. 온/오프 제어 - 환경변수로 완전 비활성화 가능 │ +│ 3. 쉬운 제거 - 테스트 후 폴더 삭제 + import 제거로 완전 제거 │ +│ 4. 플로우 연결 - 이전 단계 ID를 다음 단계에 자동 전달 │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 1.3 변경 승인 정책 + +| 분류 | 예시 | 승인 | +|------|------|------| +| ✅ 즉시 가능 | dev 폴더 내 파일 추가/수정, 환경변수 추가 | 불필요 | +| ⚠️ 컨펌 필요 | 기존 컴포넌트에 useDevFill hook 추가, layout.tsx 수정 | **필수** | +| 🔴 금지 | 기존 컴포넌트 로직 변경, 프로덕션 코드 영향 | 별도 협의 | + +### 1.4 준수 규칙 +- 프론트엔드 전용 작업 (API 변경 없음) +- 환경변수 `NEXT_PUBLIC_DEV_TOOLBAR_ENABLED=true`로 제어 + +--- + +## 2. 대상 범위 + +### 2.1 Phase 1: 기반 구조 (완료) + +| # | 작업 항목 | 상태 | 파일 | +|---|----------|:----:|------| +| 1.1 | DevFillContext.tsx 생성 | ✅ | `react/src/components/dev/DevFillContext.tsx` | +| 1.2 | useDevFill.ts hook 생성 | ✅ | `react/src/components/dev/useDevFill.ts` | +| 1.3 | DevToolbar.tsx 생성 | ✅ | `react/src/components/dev/DevToolbar.tsx` | +| 1.4 | 샘플 데이터 생성기 | ✅ | `react/src/components/dev/generators/*.ts` | +| 1.5 | index.ts export 정리 | ✅ | `react/src/components/dev/index.ts` | + +### 2.2 Phase 2: 컴포넌트 연결 (진행중) + +| # | 작업 항목 | 상태 | 파일 | +|---|----------|:----:|------| +| 2.1 | QuoteRegistration에 useDevFill 연결 | ⏳ | `react/src/components/quotes/QuoteRegistration.tsx` | +| 2.2 | OrderRegistration에 useDevFill 연결 | ⏳ | `react/src/components/orders/OrderRegistration.tsx` | +| 2.3 | WorkOrderCreate에 useDevFill 연결 | ⏳ | `react/src/components/production/WorkOrders/WorkOrderCreate.tsx` | +| 2.4 | WorkOrderDetail에 완료 버튼 연결 | ⏳ | `react/src/components/production/WorkOrders/WorkOrderDetail.tsx` | +| 2.5 | ShipmentCreate에 useDevFill 연결 | ⏳ | `react/src/components/outbound/ShipmentManagement/ShipmentCreate.tsx` | + +### 2.3 Phase 3: 통합 및 설정 + +| # | 작업 항목 | 상태 | 파일 | +|---|----------|:----:|------| +| 3.1 | DevFillProvider를 layout.tsx에 추가 | ⏳ | `react/src/app/[locale]/(protected)/layout.tsx` | +| 3.2 | DevToolbar를 layout.tsx에 추가 | ⏳ | `react/src/app/[locale]/(protected)/layout.tsx` | +| 3.3 | 환경변수 설정 (.env.local) | ⏳ | `react/.env.local` | + +### 2.4 Phase 4: 테스트 및 검증 + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 4.1 | 견적 페이지 테스트 | ⏳ | `/sales/quote-management/new` | +| 4.2 | 수주 페이지 테스트 | ⏳ | `/sales/order-management-sales/new` | +| 4.3 | 작업지시 페이지 테스트 | ⏳ | `/production/work-orders/create` | +| 4.4 | 작업완료 테스트 | ⏳ | `/production/work-orders/[id]` | +| 4.5 | 출하 페이지 테스트 | ⏳ | `/outbound/shipments/new` | +| 4.6 | 전체 플로우 테스트 | ⏳ | 견적→수주→작업지시→완료→출하 | + +--- + +## 3. 아키텍처 + +### 3.1 파일 구조 +``` +react/src/components/dev/ +├── DevFillContext.tsx # Context Provider (상태 관리) +├── useDevFill.ts # Hook (각 폼에서 사용) +├── DevToolbar.tsx # 플로팅 UI (화면 하단) +├── index.ts # Export 정리 +└── generators/ + ├── index.ts # 공통 유틸 (randomPick, randomInt 등) + ├── quoteData.ts # 견적 샘플 데이터 + ├── orderData.ts # 수주 샘플 데이터 + ├── workOrderData.ts # 작업지시 샘플 데이터 + └── shipmentData.ts # 출하 샘플 데이터 +``` + +### 3.2 데이터 흐름 +``` +┌─────────────────────────────────────────────────────────────────┐ +│ DevFillProvider (Context) │ +│ ├── isEnabled: 환경변수 기반 활성화 상태 │ +│ ├── isVisible: 툴바 표시 상태 (localStorage) │ +│ ├── currentPage: 현재 페이지 타입 │ +│ ├── flowData: { quoteId, orderId, workOrderId, lotNo } │ +│ └── fillFunctions: Map │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ DevToolbar (UI) │ +│ [견적] → [수주] → [작업지시] → [완료] → [출하] │ +│ 현재 페이지에 해당하는 버튼만 활성화 │ +│ 클릭 시 fillForm(pageType) 호출 │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ 각 폼 컴포넌트 (useDevFill hook) │ +│ useDevFill('quote', (data) => setFormData(generateQuoteData())) │ +│ - 마운트 시 fillFunction 등록 │ +│ - DevToolbar 클릭 시 등록된 함수 실행 │ +│ - 폼에 샘플 데이터 자동 채움 │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 3.3 각 단계별 입력 필드 + +| 단계 | 주요 필드 | 샘플 데이터 | +|------|----------|------------| +| **견적** | 발주처, 현장명, 담당자, 연락처, 납기일, 품목(층수/부호/카테고리/제품명/사이즈/수량) | 랜덤 거래처, +7일 납기, 1~5개 품목 | +| **수주** | 견적선택 + 배송방식, 배송일, 수신자 | +14일 출고, +21일 납기 | +| **작업지시** | 수주선택 + 공정, 출고예정일, 우선순위 | 랜덤 공정, 1~3주 후 | +| **완료** | 버튼 클릭 | handleStatusChange('completed') | +| **출하** | 로트번호, 출고예정일, 우선순위, 배송방식 | 랜덤 로트, 오늘 날짜 | + +--- + +## 4. 상세 작업 내용 + +### 4.1 Phase 1: 기반 구조 (✅ 완료) + +#### 1.1 DevFillContext.tsx +- **상태**: ✅ 완료 +- **파일**: `react/src/components/dev/DevFillContext.tsx` +- **주요 기능**: + - `isEnabled`: 환경변수 기반 활성화 + - `isVisible`: localStorage 기반 표시 상태 + - `registerFillForm/unregisterFillForm`: 폼 함수 등록/해제 + - `fillForm`: 폼 채우기 실행 + - `flowData`: 플로우 간 데이터 전달 + +#### 1.2 useDevFill.ts +- **상태**: ✅ 완료 +- **파일**: `react/src/components/dev/useDevFill.ts` +- **사용법**: +```typescript +useDevFill('quote', (data) => { + setFormData(generateQuoteData({ clients, products })); +}); +``` + +#### 1.3 DevToolbar.tsx +- **상태**: ✅ 완료 +- **파일**: `react/src/components/dev/DevToolbar.tsx` +- **주요 기능**: + - 화면 하단 플로팅 UI + - 현재 페이지 자동 감지 (URL 기반) + - 플로우 단계 버튼 (견적→수주→작업지시→완료→출하) + - 숨기기/보이기 토글 + +#### 1.4 샘플 데이터 생성기 +- **상태**: ✅ 완료 +- **파일들**: + - `generators/index.ts`: 공통 유틸 (randomPick, randomInt, randomPhone 등) + - `generators/quoteData.ts`: 견적 데이터 (QuoteFormData) + - `generators/orderData.ts`: 수주 데이터 (OrderFormData) + - `generators/workOrderData.ts`: 작업지시 데이터 + - `generators/shipmentData.ts`: 출하 데이터 (ShipmentCreateFormData) + +### 4.2 Phase 2: 컴포넌트 연결 (⏳ 대기) + +각 컴포넌트에 다음 패턴으로 useDevFill 연결: + +```typescript +// 1. import 추가 +import { useDevFill } from '@/components/dev'; +import { generateQuoteData } from '@/components/dev/generators/quoteData'; + +// 2. 컴포넌트 내부에서 hook 사용 +useDevFill('quote', useCallback(() => { + const sampleData = generateQuoteData({ clients, products }); + setFormData(sampleData); +}, [clients, products])); +``` + +### 4.3 Phase 3: 통합 및 설정 (⏳ 대기) + +#### layout.tsx 수정 +```typescript +import { DevFillProvider, DevToolbar } from '@/components/dev'; + +export default function ProtectedLayout({ children }) { + return ( + + {/* 기존 레이아웃 */} + {children} + + + ); +} +``` + +#### 환경변수 설정 +```bash +# react/.env.local +NEXT_PUBLIC_DEV_TOOLBAR_ENABLED=true +``` + +--- + +## 5. 컨펌 대기 목록 + +| # | 항목 | 변경 내용 | 영향 범위 | 상태 | +|---|------|----------|----------|------| +| 1 | QuoteRegistration.tsx | useDevFill hook 추가 (약 10줄) | 견적 등록 | ⏳ | +| 2 | OrderRegistration.tsx | useDevFill hook 추가 (약 10줄) | 수주 등록 | ⏳ | +| 3 | WorkOrderCreate.tsx | useDevFill hook 추가 (약 10줄) | 작업지시 등록 | ⏳ | +| 4 | WorkOrderDetail.tsx | useDevFill hook 추가 (약 10줄) | 작업지시 상세 | ⏳ | +| 5 | ShipmentCreate.tsx | useDevFill hook 추가 (약 10줄) | 출하 등록 | ⏳ | +| 6 | layout.tsx | DevFillProvider, DevToolbar 추가 | 전체 레이아웃 | ⏳ | + +--- + +## 6. 변경 이력 + +| 날짜 | 항목 | 변경 내용 | 파일 | 승인 | +|------|------|----------|------|------| +| 2026-01-20 | 1.1~1.5 | Phase 1 기반 구조 생성 완료 | dev/*.ts, dev/*.tsx | ✅ | +| 2026-01-20 | - | 계획 문서 작성 | docs/plans/dev-toolbar-plan.md | - | + +--- + +## 7. 참고 문서 + +### 7.1 관련 컴포넌트 경로 +- **견적**: `react/src/components/quotes/QuoteRegistration.tsx` +- **수주**: `react/src/components/orders/OrderRegistration.tsx` +- **작업지시**: `react/src/components/production/WorkOrders/WorkOrderCreate.tsx` +- **작업상세**: `react/src/components/production/WorkOrders/WorkOrderDetail.tsx` +- **출하**: `react/src/components/outbound/ShipmentManagement/ShipmentCreate.tsx` + +### 7.2 폼 데이터 타입 +- `QuoteFormData`: 견적 폼 데이터 (QuoteRegistration.tsx 내 정의) +- `OrderFormData`: 수주 폼 데이터 (OrderRegistration.tsx 내 정의) +- `ShipmentCreateFormData`: 출하 폼 데이터 (types.ts 내 정의) + +--- + +## 8. 제거 방법 + +테스트 완료 후 다음 단계로 제거: + +### Step 1: 환경변수 비활성화 (즉시 효과) +```bash +# react/.env.local +NEXT_PUBLIC_DEV_TOOLBAR_ENABLED=false +``` + +### Step 2: 코드 완전 제거 (선택) +```bash +# 1. dev 폴더 삭제 +rm -rf react/src/components/dev/ + +# 2. layout.tsx에서 import 및 컴포넌트 제거 +# - DevFillProvider 제거 +# - DevToolbar 제거 + +# 3. 각 폼 컴포넌트에서 useDevFill 관련 코드 제거 +# - import 문 제거 +# - useDevFill hook 호출 제거 +``` + +--- + +## 9. 검증 결과 + +> 작업 완료 후 이 섹션에 검증 결과 추가 + +### 9.1 테스트 케이스 + +| 페이지 | 테스트 항목 | 예상 결과 | 실제 결과 | 상태 | +|--------|----------|----------|----------|------| +| 견적 | DevToolbar "견적 채우기" 클릭 | 폼에 샘플 데이터 채워짐 | | ⏳ | +| 수주 | DevToolbar "수주 채우기" 클릭 | 폼에 샘플 데이터 채워짐 | | ⏳ | +| 작업지시 | DevToolbar "작업지시 채우기" 클릭 | 폼에 샘플 데이터 채워짐 | | ⏳ | +| 작업상세 | DevToolbar "완료 채우기" 클릭 | 완료 처리 실행 | | ⏳ | +| 출하 | DevToolbar "출하 채우기" 클릭 | 폼에 샘플 데이터 채워짐 | | ⏳ | +| 전체 플로우 | 견적→수주→작업지시→완료→출하 | 저장 버튼만 클릭하며 완료 | | ⏳ | + +### 9.2 성공 기준 + +| 기준 | 달성 | 비고 | +|------|------|------| +| 각 페이지에서 DevToolbar 표시 | ⏳ | | +| 현재 페이지 자동 감지 | ⏳ | | +| 클릭 시 폼 데이터 자동 채움 | ⏳ | | +| 환경변수로 비활성화 가능 | ⏳ | | +| 전체 플로우 3분 내 완료 | ⏳ | 기존 15분 → 3분 | + +--- + +## 10. 자기완결성 점검 결과 + +### 10.1 체크리스트 검증 + +| # | 검증 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 1 | 작업 목적이 명확한가? | ✅ | 1.1 배경에 명시 | +| 2 | 성공 기준이 정의되어 있는가? | ✅ | 9.2에 명시 | +| 3 | 작업 범위가 구체적인가? | ✅ | 2. 대상 범위에 파일 경로 포함 | +| 4 | 의존성이 명시되어 있는가? | ✅ | 프론트엔드 전용, API 없음 | +| 5 | 참고 파일 경로가 정확한가? | ✅ | 7.1에 명시 | +| 6 | 단계별 절차가 실행 가능한가? | ✅ | 4. 상세 작업 내용에 코드 예시 포함 | +| 7 | 검증 방법이 명시되어 있는가? | ✅ | 9.1 테스트 케이스 | +| 8 | 모호한 표현이 없는가? | ✅ | 구체적 파일 경로 및 코드 포함 | + +### 10.2 새 세션 시뮬레이션 테스트 + +| 질문 | 답변 가능 | 참조 섹션 | +|------|:--------:|----------| +| Q1. 이 작업의 목적은 무엇인가? | ✅ | 1.1 배경 | +| Q2. 어디서부터 시작해야 하는가? | ✅ | 📍 현재 진행 상태 | +| Q3. 어떤 파일을 수정해야 하는가? | ✅ | 2. 대상 범위 | +| Q4. 작업 완료 확인 방법은? | ✅ | 9.1 테스트 케이스 | +| Q5. 막혔을 때 참고 문서는? | ✅ | 7. 참고 문서 | + +**결과**: 5/5 통과 → ✅ 자기완결성 확보 + +--- + +*이 문서는 /plan 스킬로 생성되었습니다.* \ No newline at end of file diff --git a/plans/index_plans.md b/plans/index_plans.md index dc17c63..fc4fedb 100644 --- a/plans/index_plans.md +++ b/plans/index_plans.md @@ -1,7 +1,7 @@ # 기획 문서 인덱스 > SAM 시스템 개발 계획 및 기획 문서 모음 -> **최종 업데이트**: 2025-01-09 +> **최종 업데이트**: 2026-01-20 --- @@ -9,30 +9,29 @@ | 분류 | 개수 | 설명 | |------|------|------| -| 개발 계획서 | 21개 | 기능별 API 개발 계획 | +| 진행중/대기 계획서 | 16개 | 기능별 API 개발 계획 | +| 완료 아카이브 | 15개 | `archive/` 폴더에 보관 | | 스토리보드 | 1개 | ERP 화면 설계 (D1.0) | | 플로우 테스트 | 32개 | API 검증용 JSON 테스트 케이스 | > **Note**: D0.8 스토리보드는 `docs/history/2025-12/`로 아카이브됨 > **Note**: E2E 버그 수정 계획은 `docs/history/2026-01/`로 아카이브됨 (2026-01-15 완료) +> **Note**: 완료된 계획 15개는 `archive/` 폴더로 이동됨 (2026-01-20) --- -## 개발 계획서 +## 개발 계획서 (진행중/대기) ### ERP API 개발 | 문서 | 상태 | 설명 | |------|------|------| | [erp-api-development-plan.md](./erp-api-development-plan.md) | 🟡 Phase 3 진행중 | SAM ERP API 전체 개발 계획 (D0.8 기준) | -| [erp-api-development-plan-d1.0-changes.md](./erp-api-development-plan-d1.0-changes.md) | 🟢 완료 | D1.0 변경사항 (Phase 5-8 완료) | ### 기능별 계획 | 문서 | 상태 | 설명 | |------|------|------| -| [mng-quote-formula-development-plan.md](./mng-quote-formula-development-plan.md) | 🟢 완료 | mng 견적 수식 관리 | -| [quote-auto-calculation-development-plan.md](./quote-auto-calculation-development-plan.md) | 🟢 완료 | 견적 자동 계산 (2025-12-22 완료) | | [simulator-calculation-logic-mapping.md](./simulator-calculation-logic-mapping.md) | 📚 참조 | 견적 시뮬레이터 계산 로직 매핑 분석 | | [mng-item-field-management-plan.md](./mng-item-field-management-plan.md) | ⚪ 계획수립 | 품목 필드 관리 (미착수) | | [items-table-unification-plan.md](./items-table-unification-plan.md) | ⚪ 계획수립 | items 테이블 통합 (롤백 후 대기) | @@ -45,35 +44,52 @@ | [5130-to-mng-migration-plan.md](./5130-to-mng-migration-plan.md) | 🟡 진행중 | 5130 → mng 마이그레이션 (5/38 완료) | | [react-api-integration-plan.md](./react-api-integration-plan.md) | 🟡 진행중 | React ↔ API 연동 테스트 | | [react-mock-to-api-migration-plan.md](./react-mock-to-api-migration-plan.md) | 🟡 진행중 | Mock → API 전환 (Phase A 부분 완료) | +| [dashboard-api-integration-plan.md](./dashboard-api-integration-plan.md) | 🟡 Phase 1 대기 | CEO Dashboard API 연동 | ### 영업/생산 (Sales/Production) | 문서 | 상태 | 설명 | |------|------|------| -| [order-management-plan.md](./order-management-plan.md) | 🟢 완료 | 수주관리 API 연동 (2025-01-09 완료) | -| [work-order-plan.md](./work-order-plan.md) | 🟡 진행중 | 작업지시 검증 (API 연동 완료, 테스트 중) | -| [process-management-plan.md](./process-management-plan.md) | 🟢 완료 | 공정관리 API 연동 | | [quote-management-8issues-plan.md](./quote-management-8issues-plan.md) | 🟡 진행중 | 견적관리 8개 이슈 | | [quote-calculation-api-plan.md](./quote-calculation-api-plan.md) | 🟡 진행중 | 견적 계산 API | - -### 시공사 (Construction) - -| 문서 | 상태 | 설명 | -|------|------|------| -| [construction-api-integration-plan.md](./construction-api-integration-plan.md) | 🟡 진행중 | 시공사 9개 페이지 API 연동 (1/9 완료) | +| [order-workorder-shipment-integration-plan.md](./order-workorder-shipment-integration-plan.md) | 🔵 계획수립 | 수주-작업지시-출하 연동 | +| [quote-system-development-plan.md](./quote-system-development-plan.md) | 🟡 진행중 | 견적 시스템 개발 | +| [simulator-ui-enhancement-plan.md](./simulator-ui-enhancement-plan.md) | 🔵 계획수립 | 시뮬레이터 UI 개선 | ### 시스템/기타 | 문서 | 상태 | 설명 | |------|------|------| -| [notification-sound-system-plan.md](./notification-sound-system-plan.md) | 🟢 완료 | 알림음 시스템 (2025-01-07 완료) | -| [l2-permission-management-plan.md](./l2-permission-management-plan.md) | 🔵 계획수립 | L2 권한 관리 | -| [react-fcm-push-notification-plan.md](./react-fcm-push-notification-plan.md) | 🔵 계획수립 | FCM 푸시 알림 | | [dummy-data-seeding-plan.md](./dummy-data-seeding-plan.md) | ⚪ 계획수립 | 더미 데이터 시딩 (2025-12-23 작성) | | [api-explorer-development-plan.md](./api-explorer-development-plan.md) | 🔵 계획수립 | API Explorer 개발 (설계 완료, 구현 대기) | | [employee-user-linkage-plan.md](./employee-user-linkage-plan.md) | 🔵 계획수립 | 사원-회원 연결 기능 (2025-12-25 작성) | | [docs-update-plan.md](./docs-update-plan.md) | 🟡 진행중 | 문서 업데이트 계획 (Phase 4 진행중) | | [react-mock-remaining-tasks.md](./react-mock-remaining-tasks.md) | 📚 참조 | Mock 전환 잔여 작업 목록 | +| [hotfix-20260119-action-plan.md](./hotfix-20260119-action-plan.md) | 🟡 진행중 | Hotfix 액션 플랜 (2026-01-19) | + +--- + +## 완료 아카이브 (archive/) + +> 완료된 계획 문서들 - 참조용으로 보관 + +| 문서 | 완료일 | 설명 | +|------|--------|------| +| [erp-api-development-plan-d1.0-changes.md](./archive/erp-api-development-plan-d1.0-changes.md) | 2025-12 | D1.0 변경사항 (Phase 5-8) | +| [mng-quote-formula-development-plan.md](./archive/mng-quote-formula-development-plan.md) | 2025-12 | mng 견적 수식 관리 | +| [quote-auto-calculation-development-plan.md](./archive/quote-auto-calculation-development-plan.md) | 2025-12-22 | 견적 자동 계산 | +| [order-management-plan.md](./archive/order-management-plan.md) | 2025-01-09 | 수주관리 API 연동 | +| [work-order-plan.md](./archive/work-order-plan.md) | 2025-01-11 | 작업지시 검증 | +| [process-management-plan.md](./archive/process-management-plan.md) | 2025-12 | 공정관리 API 연동 | +| [construction-api-integration-plan.md](./archive/construction-api-integration-plan.md) | 2026-01 | 시공사 API 연동 | +| [notification-sound-system-plan.md](./archive/notification-sound-system-plan.md) | 2025-01-07 | 알림음 시스템 | +| [l2-permission-management-plan.md](./archive/l2-permission-management-plan.md) | 2025-12 | L2 권한 관리 | +| [react-fcm-push-notification-plan.md](./archive/react-fcm-push-notification-plan.md) | 2025-12 | FCM 푸시 알림 | +| [react-server-component-audit-plan.md](./archive/react-server-component-audit-plan.md) | 2025-12 | Server Component 점검 | +| [5130-bom-migration-plan.md](./archive/5130-bom-migration-plan.md) | 2025-12 | 5130 BOM 마이그레이션 | +| [5130-sam-data-migration-plan.md](./archive/5130-sam-data-migration-plan.md) | 2025-12 | 5130 데이터 마이그레이션 | +| [bidding-api-implementation-plan.md](./archive/bidding-api-implementation-plan.md) | 2025-12 | 입찰 API 구현 | +| [mes-integration-analysis-plan.md](./archive/mes-integration-analysis-plan.md) | 2025-01-09 | MES 연동 분석 | --- diff --git a/plans/order-workorder-shipment-integration-plan.md b/plans/order-workorder-shipment-integration-plan.md new file mode 100644 index 0000000..105c5c3 --- /dev/null +++ b/plans/order-workorder-shipment-integration-plan.md @@ -0,0 +1,659 @@ +# 수주-작업지시-출하 하이브리드 연동 구조 구현 계획 + +> **작성일**: 2025-01-19 +> **목적**: Order → WorkOrder → Shipment 간 FK 연결 강화 및 상태 동기화 로직 구현 +> **기준 문서**: `api/app/Models/Orders/Order.php`, `api/app/Models/Production/WorkOrder.php`, `api/app/Models/Tenants/Shipment.php` +> **상태**: 📋 계획 수립 완료 (Serena ID: order-integration-state) + +--- + +## 📍 현재 진행 상태 + +| 항목 | 내용 | +|------|------| +| **마지막 완료 작업** | Phase 4: 작업완료 시 자동 출하 생성 기능 구현 | +| **다음 작업** | ✅ 모든 Phase 완료 | +| **진행률** | 4/4 Phase (100%) | +| **마지막 업데이트** | 2025-01-19 | + +--- + +## 1. 개요 + +### 1.1 배경 + +현재 SAM 시스템은 수주(Order), 작업지시(WorkOrder), 출하(Shipment)가 독립적으로 운영되고 있습니다. + +**현재 문제점:** +- `shipments` 테이블에 `work_order_id` FK가 없음 +- 작업 완료 시 출하로 자동 연결되지 않음 +- Order의 전체 진행 상태를 추적할 수 없음 +- 데이터 정합성 보장이 어려움 + +**목표:** +- 하이브리드 마스터-디테일 구조로 전환 +- `orders.status_code`로 전체 진행 상태 추적 +- 각 단계별 상태 변경 시 연관 테이블 자동 동기화 + +### 1.2 목표 구조 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 목표 구조 │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ orders (마스터) │ +│ ├─ status_code: 전체 진행상태 추적 │ +│ │ DRAFT → CONFIRMED → IN_PRODUCTION → PRODUCED │ +│ │ → SHIPPING → SHIPPED → COMPLETED │ +│ │ │ +│ ├──(1:N)──▶ work_orders (생산 상세) │ +│ │ ├─ sales_order_id FK ✅ (기존) │ +│ │ └─ status: 생산 프로세스 상태 │ +│ │ │ +│ └──(1:N)──▶ shipments (출하 상세) │ +│ ├─ order_id FK ✅ (기존) │ +│ ├─ work_order_id FK 🆕 (신규 추가) │ +│ └─ status: 출하 프로세스 상태 │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 1.3 기준 원칙 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 🎯 핵심 원칙 │ +├─────────────────────────────────────────────────────────────────┤ +│ 1. orders.status_code = 전체 프로세스의 Single Source of Truth │ +│ 2. 하위 테이블(work_orders, shipments)은 상세 정보만 관리 │ +│ 3. 상태 변경 시 상위 테이블 자동 동기화 │ +│ 4. 기존 데이터 호환성 유지 (work_order_id는 nullable) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 1.4 변경 승인 정책 + +| 분류 | 예시 | 승인 | +|------|------|------| +| ✅ 즉시 가능 | 모델 관계 추가, 상수 추가, 문서 수정 | 불필요 | +| ⚠️ 컨펌 필요 | 마이그레이션, 서비스 로직 변경, 상태 동기화 | **필수** | +| 🔴 금지 | 기존 테이블 구조 파괴적 변경, 기존 API 삭제 | 별도 협의 | + +### 1.5 준수 규칙 + +- `docs/quickstart/quick-start.md` - 빠른 시작 가이드 +- `docs/standards/quality-checklist.md` - 품질 체크리스트 +- `CLAUDE.md` - SAM API Development Rules + +--- + +## 2. 대상 범위 + +### 2.1 Phase 1: DB 스키마 수정 + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 1.1 | `shipments` 테이블에 `work_order_id` FK 추가 마이그레이션 | ⏳ | nullable, index 포함 | + +### 2.2 Phase 2: 모델 관계 추가 + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 2.1 | Order 모델에 `shipments()` HasMany 관계 추가 | ⏳ | | +| 2.2 | WorkOrder 모델에 `shipments()` HasMany 관계 추가 | ⏳ | | +| 2.3 | Shipment 모델에 `workOrder()` BelongsTo 관계 추가 | ⏳ | | +| 2.4 | Shipment 모델에 `work_order_id` fillable 추가 | ⏳ | | + +### 2.3 Phase 3: Order 상태 확장 및 동기화 로직 + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 3.1 | Order 모델에 생산/출하 관련 상태 상수 추가 | ⏳ | IN_PRODUCTION, PRODUCED, SHIPPING, SHIPPED | +| 3.2 | WorkOrderService에 Order 상태 동기화 로직 추가 | ⏳ | 상태 변경 시 Order.status_code 업데이트 | +| 3.3 | ShipmentService에 Order 상태 동기화 로직 추가 | ⏳ | 상태 변경 시 Order.status_code 업데이트 | + +### 2.4 Phase 4: 연동 기능 (선택) + +| # | 작업 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 4.1 | ShipmentService.store()에 work_order_id 연결 로직 추가 | ⏳ | 출하 생성 시 WorkOrder 선택 가능 | +| 4.2 | WorkOrder 완료 시 Shipment 자동 생성 옵션 | ⏳ | 선택적 기능 | + +--- + +## 3. 작업 절차 + +### 3.1 단계별 절차 + +``` +Phase 1: DB 스키마 수정 +└── 1.1 마이그레이션 생성 및 실행 + ├── add_work_order_id_to_shipments_table.php + ├── work_order_id FK (nullable) + └── index 추가 + +Phase 2: 모델 관계 추가 +├── 2.1 Order.php - shipments() HasMany +├── 2.2 WorkOrder.php - shipments() HasMany +├── 2.3 Shipment.php - workOrder() BelongsTo +└── 2.4 Shipment.php - fillable에 work_order_id 추가 + +Phase 3: 상태 동기화 +├── 3.1 Order.php - 상태 상수 확장 +│ ├── STATUS_IN_PRODUCTION = 'IN_PRODUCTION' +│ ├── STATUS_PRODUCED = 'PRODUCED' +│ ├── STATUS_SHIPPING = 'SHIPPING' +│ └── STATUS_SHIPPED = 'SHIPPED' +├── 3.2 WorkOrderService.php - syncOrderStatus() 메서드 추가 +│ ├── in_progress → Order: IN_PRODUCTION +│ ├── completed → Order: PRODUCED +│ └── shipped → Order: (Shipment 생성 시) +└── 3.3 ShipmentService.php - syncOrderStatus() 메서드 추가 + ├── scheduled/ready → Order: SHIPPING (첫 출하 생성 시) + └── completed → Order: SHIPPED (모든 출하 완료 시) + +Phase 4: 연동 기능 (선택) +├── 4.1 ShipmentService.store() - work_order_id 파라미터 추가 +└── 4.2 WorkOrderService.updateStatus() - 자동 Shipment 생성 옵션 +``` + +### 3.2 상태 흐름도 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 전체 상태 흐름 │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ [Order] │ +│ DRAFT ──▶ CONFIRMED ──▶ IN_PRODUCTION ──▶ PRODUCED │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ WorkOrder WorkOrder WorkOrder │ +│ 생성 in_progress completed │ +│ │ │ +│ ▼ │ +│ ──────────────────────▶ SHIPPING ──▶ SHIPPED ──▶ COMPLETED │ +│ │ │ │ +│ ▼ ▼ │ +│ Shipment Shipment │ +│ 생성 completed │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 4. 상세 작업 내용 + +### 4.1 Phase 1: DB 스키마 수정 + +#### 1.1 마이그레이션: shipments 테이블에 work_order_id 추가 + +**파일**: `api/database/migrations/2025_01_19_XXXXXX_add_work_order_id_to_shipments_table.php` + +```php +foreignId('work_order_id') + ->nullable() + ->after('order_id') + ->comment('작업지시 ID'); + + $table->index(['tenant_id', 'work_order_id']); + }); + } + + public function down(): void + { + Schema::table('shipments', function (Blueprint $table) { + $table->dropIndex(['tenant_id', 'work_order_id']); + $table->dropColumn('work_order_id'); + }); + } +}; +``` + +--- + +### 4.2 Phase 2: 모델 관계 추가 + +#### 2.1 Order 모델 - shipments() 관계 + +**파일**: `api/app/Models/Orders/Order.php` + +```php +use App\Models\Tenants\Shipment; + +/** + * 출하 목록 + */ +public function shipments(): HasMany +{ + return $this->hasMany(Shipment::class, 'order_id'); +} +``` + +#### 2.2 WorkOrder 모델 - shipments() 관계 + +**파일**: `api/app/Models/Production/WorkOrder.php` + +```php +use App\Models\Tenants\Shipment; + +/** + * 출하 목록 + */ +public function shipments(): HasMany +{ + return $this->hasMany(Shipment::class); +} +``` + +#### 2.3-2.4 Shipment 모델 수정 + +**파일**: `api/app/Models/Tenants/Shipment.php` + +```php +use App\Models\Production\WorkOrder; + +// fillable에 추가 +protected $fillable = [ + // ... 기존 필드들 + 'work_order_id', // 추가 +]; + +// casts에 추가 +protected $casts = [ + // ... 기존 캐스트들 + 'work_order_id' => 'integer', // 추가 +]; + +/** + * 작업지시 관계 + */ +public function workOrder(): BelongsTo +{ + return $this->belongsTo(WorkOrder::class); +} +``` + +--- + +### 4.3 Phase 3: Order 상태 확장 및 동기화 로직 + +#### 3.1 Order 모델 - 상태 상수 확장 + +**파일**: `api/app/Models/Orders/Order.php` + +```php +// 기존 상태 +public const STATUS_DRAFT = 'DRAFT'; +public const STATUS_CONFIRMED = 'CONFIRMED'; +public const STATUS_IN_PROGRESS = 'IN_PROGRESS'; +public const STATUS_COMPLETED = 'COMPLETED'; +public const STATUS_CANCELLED = 'CANCELLED'; + +// 신규 상태 추가 +public const STATUS_IN_PRODUCTION = 'IN_PRODUCTION'; // 생산중 +public const STATUS_PRODUCED = 'PRODUCED'; // 생산완료 +public const STATUS_SHIPPING = 'SHIPPING'; // 출하중 +public const STATUS_SHIPPED = 'SHIPPED'; // 출하완료 + +/** + * 전체 상태 목록 + */ +public const STATUSES = [ + self::STATUS_DRAFT, + self::STATUS_CONFIRMED, + self::STATUS_IN_PRODUCTION, + self::STATUS_PRODUCED, + self::STATUS_SHIPPING, + self::STATUS_SHIPPED, + self::STATUS_COMPLETED, + self::STATUS_CANCELLED, +]; + +/** + * 상태 라벨 + */ +public const STATUS_LABELS = [ + self::STATUS_DRAFT => '임시저장', + self::STATUS_CONFIRMED => '확정', + self::STATUS_IN_PRODUCTION => '생산중', + self::STATUS_PRODUCED => '생산완료', + self::STATUS_SHIPPING => '출하중', + self::STATUS_SHIPPED => '출하완료', + self::STATUS_COMPLETED => '완료', + self::STATUS_CANCELLED => '취소', +]; +``` + +#### 3.2 WorkOrderService - Order 상태 동기화 + +**파일**: `api/app/Services/WorkOrderService.php` + +```php +use App\Models\Orders\Order; + +/** + * Order 상태 동기화 + * WorkOrder 상태 변경 시 Order.status_code 업데이트 + */ +private function syncOrderStatus(WorkOrder $workOrder): void +{ + if (!$workOrder->sales_order_id) { + return; + } + + $order = Order::find($workOrder->sales_order_id); + if (!$order) { + return; + } + + $newStatus = null; + + switch ($workOrder->status) { + case WorkOrder::STATUS_IN_PROGRESS: + case WorkOrder::STATUS_WAITING: + case WorkOrder::STATUS_PENDING: + // 하나라도 진행중이면 생산중 + $newStatus = Order::STATUS_IN_PRODUCTION; + break; + + case WorkOrder::STATUS_COMPLETED: + // 모든 작업지시가 완료되었는지 확인 + $allCompleted = WorkOrder::where('sales_order_id', $order->id) + ->whereNotIn('status', [WorkOrder::STATUS_COMPLETED, WorkOrder::STATUS_SHIPPED]) + ->doesntExist(); + + if ($allCompleted) { + $newStatus = Order::STATUS_PRODUCED; + } + break; + } + + if ($newStatus && $order->status_code !== $newStatus) { + $order->update(['status_code' => $newStatus]); + + $this->auditLogger->log( + $order->tenant_id, + 'order', + $order->id, + 'status_synced_from_work_order', + ['status_code' => $order->getOriginal('status_code')], + ['status_code' => $newStatus, 'work_order_id' => $workOrder->id] + ); + } +} +``` + +**updateStatus() 메서드에 호출 추가:** + +```php +public function updateStatus(int $id, string $status, ?array $resultData = null) +{ + // ... 기존 로직 ... + + return DB::transaction(function () use ($workOrder, $status, $resultData, $tenantId, $userId) { + // ... 기존 상태 변경 로직 ... + + $workOrder->save(); + + // Order 상태 동기화 추가 + $this->syncOrderStatus($workOrder); + + // ... 나머지 로직 ... + }); +} +``` + +#### 3.3 ShipmentService - Order 상태 동기화 + +**파일**: `api/app/Services/ShipmentService.php` + +```php +use App\Models\Orders\Order; + +/** + * Order 상태 동기화 + * Shipment 상태 변경 시 Order.status_code 업데이트 + */ +private function syncOrderStatus(Shipment $shipment): void +{ + if (!$shipment->order_id) { + return; + } + + $order = Order::find($shipment->order_id); + if (!$order) { + return; + } + + $newStatus = null; + + switch ($shipment->status) { + case 'scheduled': + case 'ready': + case 'shipping': + // 출하 프로세스 시작 + if (!in_array($order->status_code, [Order::STATUS_SHIPPING, Order::STATUS_SHIPPED, Order::STATUS_COMPLETED])) { + $newStatus = Order::STATUS_SHIPPING; + } + break; + + case 'completed': + // 모든 출하가 완료되었는지 확인 + $allCompleted = Shipment::where('order_id', $order->id) + ->where('status', '!=', 'completed') + ->doesntExist(); + + if ($allCompleted) { + $newStatus = Order::STATUS_SHIPPED; + } + break; + } + + if ($newStatus && $order->status_code !== $newStatus) { + $order->update(['status_code' => $newStatus]); + } +} +``` + +**store() 및 updateStatus() 메서드에 호출 추가:** + +```php +public function store(array $data): Shipment +{ + // ... 기존 로직 ... + + return DB::transaction(function () use ($data, $tenantId, $userId) { + // ... 기존 생성 로직 ... + + // Order 상태 동기화 추가 + $this->syncOrderStatus($shipment); + + return $shipment->load('items'); + }); +} + +public function updateStatus(int $id, string $status, ?array $additionalData = null): Shipment +{ + // ... 기존 로직 ... + + $shipment->update($updateData); + + // Order 상태 동기화 추가 + $this->syncOrderStatus($shipment); + + return $shipment->load('items'); +} +``` + +--- + +### 4.4 Phase 4: 연동 기능 (선택) + +#### 4.1 ShipmentService.store() - work_order_id 연결 + +**파일**: `api/app/Services/ShipmentService.php` + +```php +public function store(array $data): Shipment +{ + return DB::transaction(function () use ($data, $tenantId, $userId) { + $shipment = Shipment::create([ + // ... 기존 필드들 ... + 'work_order_id' => $data['work_order_id'] ?? null, // 추가 + ]); + + // WorkOrder가 있으면 상태를 shipped로 변경 + if ($shipment->work_order_id) { + $workOrder = WorkOrder::find($shipment->work_order_id); + if ($workOrder && $workOrder->status === WorkOrder::STATUS_COMPLETED) { + $workOrder->update([ + 'status' => WorkOrder::STATUS_SHIPPED, + 'shipped_at' => now(), + ]); + } + } + + // ... 나머지 로직 ... + }); +} +``` + +#### 4.2 ShipmentStoreRequest - work_order_id 검증 + +**파일**: `api/app/Http/Requests/Shipment/ShipmentStoreRequest.php` + +```php +public function rules(): array +{ + return [ + // ... 기존 규칙들 ... + 'work_order_id' => ['nullable', 'integer', 'exists:work_orders,id'], + ]; +} +``` + +--- + +## 5. 컨펌 대기 목록 + +| # | 항목 | 변경 내용 | 영향 범위 | 상태 | +|---|------|----------|----------|------| +| 1 | 마이그레이션 | shipments에 work_order_id FK 추가 | DB | ⏳ 컨펌 필요 | +| 2 | Order 상태 확장 | 4개 상태 추가 (IN_PRODUCTION, PRODUCED, SHIPPING, SHIPPED) | Order 모델, API | ⏳ 컨펌 필요 | +| 3 | 상태 동기화 로직 | WorkOrder/Shipment 상태 변경 시 Order 자동 업데이트 | 서비스 로직 | ⏳ 컨펌 필요 | + +--- + +## 6. 변경 이력 + +| 날짜 | 항목 | 변경 내용 | 파일 | 승인 | +|------|------|----------|------|------| +| 2025-01-19 | - | 계획 문서 초안 작성 | - | - | + +--- + +## 7. 참고 문서 + +- **빠른 시작**: `docs/quickstart/quick-start.md` +- **품질 체크리스트**: `docs/standards/quality-checklist.md` +- **SAM API 규칙**: `CLAUDE.md` +- **DB 스키마**: `docs/specs/database-schema.md` + +### 분석된 기존 파일 + +| 파일 | 역할 | +|------|------| +| `api/app/Models/Orders/Order.php` | 수주 마스터 모델 | +| `api/app/Models/Production/WorkOrder.php` | 작업지시 모델 | +| `api/app/Models/Tenants/Shipment.php` | 출하 모델 | +| `api/app/Services/WorkOrderService.php` | 작업지시 비즈니스 로직 | +| `api/app/Services/ShipmentService.php` | 출하 비즈니스 로직 | +| `api/database/migrations/2025_12_26_100000_create_work_orders_table.php` | 작업지시 테이블 | +| `api/database/migrations/2025_12_26_150604_create_shipments_table.php` | 출하 테이블 | + +--- + +## 8. 세션 및 메모리 관리 정책 + +### 8.1 세션 시작 시 +```javascript +read_memory("order-integration-state") // 상태 파악 +read_memory("order-integration-snapshot") // 사고 흐름 복구 +``` + +### 8.2 Serena 메모리 구조 +- `order-integration-state`: { phase, progress, next_step, last_decision } +- `order-integration-snapshot`: 현재까지의 논의 및 코드 변경점 요약 +- `order-integration-rules`: 해당 작업에서 결정된 규칙들 + +--- + +## 9. 검증 결과 + +> 작업 완료 후 이 섹션에 검증 결과 추가 + +### 9.1 테스트 케이스 + +| 시나리오 | 예상 결과 | 실제 결과 | 상태 | +|----------|----------|----------|------| +| WorkOrder 생성 (in_progress) | Order.status = IN_PRODUCTION | - | ⏳ | +| WorkOrder 완료 (completed) | Order.status = PRODUCED | - | ⏳ | +| Shipment 생성 | Order.status = SHIPPING | - | ⏳ | +| Shipment 완료 | Order.status = SHIPPED | - | ⏳ | +| 모든 프로세스 완료 | Order.status = COMPLETED | - | ⏳ | + +### 9.2 성공 기준 + +| 기준 | 달성 | 비고 | +|------|------|------| +| shipments.work_order_id FK 추가 완료 | ⏳ | | +| 모델 관계 정상 동작 | ⏳ | | +| Order 상태 자동 동기화 | ⏳ | | +| 기존 데이터 호환성 유지 | ⏳ | | + +--- + +## 10. 자기완결성 점검 결과 + +### 10.1 체크리스트 검증 + +| # | 검증 항목 | 상태 | 비고 | +|---|----------|:----:|------| +| 1 | 작업 목적이 명확한가? | ✅ | 섹션 1.1 배경 | +| 2 | 성공 기준이 정의되어 있는가? | ✅ | 섹션 9.2 | +| 3 | 작업 범위가 구체적인가? | ✅ | 섹션 2 대상 범위 | +| 4 | 의존성이 명시되어 있는가? | ✅ | Phase별 순서 정의 | +| 5 | 참고 파일 경로가 정확한가? | ✅ | 섹션 7 참고 문서 | +| 6 | 단계별 절차가 실행 가능한가? | ✅ | 섹션 3, 4 상세 코드 포함 | +| 7 | 검증 방법이 명시되어 있는가? | ✅ | 섹션 9.1 테스트 케이스 | +| 8 | 모호한 표현이 없는가? | ✅ | 구체적 코드 예시 포함 | + +### 10.2 새 세션 시뮬레이션 테스트 + +| 질문 | 답변 가능 | 참조 섹션 | +|------|:--------:|----------| +| Q1. 이 작업의 목적은 무엇인가? | ✅ | 1.1 배경 | +| Q2. 어디서부터 시작해야 하는가? | ✅ | 현재 진행 상태, 3.1 절차 | +| Q3. 어떤 파일을 수정해야 하는가? | ✅ | 섹션 4 상세 작업 내용 | +| Q4. 작업 완료 확인 방법은? | ✅ | 9. 검증 결과 | +| Q5. 막혔을 때 참고 문서는? | ✅ | 7. 참고 문서 | + +**결과**: 5/5 통과 → ✅ 자기완결성 확보 + +--- + +*이 문서는 /sc:plan 스킬로 생성되었습니다.* \ No newline at end of file diff --git a/plans/sub/handover-report-plan.md b/plans/sub/archive/handover-report-plan.md similarity index 100% rename from plans/sub/handover-report-plan.md rename to plans/sub/archive/handover-report-plan.md diff --git a/plans/sub/labor-plan.md b/plans/sub/archive/labor-plan.md similarity index 100% rename from plans/sub/labor-plan.md rename to plans/sub/archive/labor-plan.md