From 341f2b3e3f10ff711370d41e4d5f84362d7a501d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EB=B3=91=EC=B2=A0?= Date: Wed, 18 Mar 2026 15:50:55 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[build]=20TS=20=EC=97=90=EB=9F=AC=206?= =?UTF-8?q?=EA=B1=B4=20=EC=88=98=EC=A0=95=20(=EC=88=98=EC=A3=BC=20status?= =?UTF-8?q?=20=ED=83=80=EC=9E=85,=20=EC=B6=9C=ED=95=98=20cancelled=20?= =?UTF-8?q?=EB=88=84=EB=9D=BD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - page.tsx: draft→order_registered, in_progress→production_ordered (OrderStatus 타입 일치) - actions.ts: ApiOrderStats에 in_production, produced optional 필드 추가 - ShipmentDetail.tsx: STATUS_TRANSITIONS에 cancelled 추가 - ShipmentList.tsx: colorMap에 cancelled 추가 --- .../architecture/module-separation-guide.md | 83 ++++++++++++++++++- .../sales/order-management-sales/page.tsx | 4 +- src/components/orders/actions.ts | 2 + .../ShipmentManagement/ShipmentDetail.tsx | 1 + .../ShipmentManagement/ShipmentList.tsx | 1 + 5 files changed, 88 insertions(+), 3 deletions(-) diff --git a/claudedocs/architecture/module-separation-guide.md b/claudedocs/architecture/module-separation-guide.md index eab259f8..c35dadc0 100644 --- a/claudedocs/architecture/module-separation-guide.md +++ b/claudedocs/architecture/module-separation-guide.md @@ -5,6 +5,87 @@ --- +## 0. 왜 산업군별 모듈 분리가 필요한가 (협의 필요) + +### 현재 상황: 하나의 ERP, 다른 업종의 고객사 + +SAM ERP는 **하나의 코드베이스**로 여러 회사(테넌트)에 서비스를 제공합니다. +그런데 고객사마다 업종이 다릅니다: + +``` +경동 → 셔터 제조업 (MES) → 생산관리, 품질관리, 차량관리가 필요 +주일 → 건설업 → 시공관리, 차량관리가 필요 +A사 → 유통/서비스업 → 공통 ERP(회계/인사/영업)만 필요 +``` + +현재는 **모든 테넌트에게 모든 메뉴가 보입니다.** +경동 직원에게 시공관리 메뉴가 보이고, 주일 직원에게 생산관리 메뉴가 보입니다. +→ 사용하지 않는 메뉴가 노출되어 혼란을 주고, 대시보드에도 불필요한 섹션이 나타남. + +### 제안: 업종(industry) 기반 모듈 ON/OFF + +``` +┌─────────────────────────────────────────────────┐ +│ SAM ERP (공통) │ +│ 회계 · 인사 · 영업 · 결재 · 게시판 · 설정 │ +├──────────┬──────────┬───────────┬───────────────┤ +│ 생산관리 │ 품질관리 │ 시공관리 │ 차량관리 │ +│ (MES) │ │ (건설) │ (선택) │ +├──────────┴──────────┼───────────┤ │ +│ 셔터 MES 업종 │ 건설 업종 │ │ +│ (경동) │ (주일) │ │ +└─────────────────────┴───────────┴───────────────┘ +``` + +- **공통 모듈**: 모든 테넌트가 사용 (회계, 인사, 영업 등) +- **업종 모듈**: 테넌트의 업종에 따라 자동으로 켜짐/꺼짐 +- **선택 모듈**: 업종과 관계없이 개별 선택 가능 (차량관리 등) + +### 협의가 필요한 부분 + +이 구조로 가려면 다음 사항의 합의가 필요합니다: + +#### 1) 업종 분류 체계 +현재 프론트엔드에 하드코딩된 매핑: + +| 업종 코드 | 의미 | 활성 모듈 | +|-----------|------|-----------| +| `shutter_mes` | 셔터 제조 (MES) | 생산관리 + 품질관리 + 차량관리 | +| `construction` | 건설업 | 시공관리 + 차량관리 | + +**Q. 이 분류가 맞는지? 추가할 업종이 있는지?** +예: 일반 제조업, 도소매업, 서비스업 등 + +#### 2) 모듈 경계 +현재 정의된 모듈 단위: + +| 모듈 | 포함 기능 | 비고 | +|------|----------|------| +| 공통 ERP | 대시보드, 회계, 인사, 영업, 결재, 게시판, 설정 등 | 항상 ON | +| 생산관리 | 생산지시, 작업지시, 작업일보 | 경동 전용 | +| 품질관리 | 설비점검, 수리요청, 검사 | 경동 전용 | +| 시공관리 | 프로젝트, 계약, 기성, 시공일보 | 주일 전용 | +| 차량관리 | 차량등록, 운행일지, 지게차 | 선택적 | + +**Q. 모듈 단위 범위가 적절한지? 분리/통합이 필요한 모듈이 있는지?** + +#### 3) 활성화 방식 +| 방식 | 장점 | 단점 | +|------|------|------| +| **A. 업종 자동** (현재) | 간단, 실수 방지 | 유연성 낮음 | +| **B. 모듈 개별 선택** (향후) | 유연함 | 관리 복잡 | +| **C. 업종 기본값 + 개별 재정의** | 균형 | 구현 복잡도 중간 | + +**Q. 어떤 방식을 채택할 것인지?** +현재 프론트엔드는 A 방식으로 구현, B/C로 확장 가능하도록 설계됨. + +#### 4) 적용 시점과 범위 +- 백엔드에서 `tenant.options.industry` 값만 세팅하면 즉시 동작 +- 값을 안 넣으면 기존과 100% 동일 (부작용 제로) +- **Q. 언제부터, 어떤 테넌트부터 적용할 것인지?** + +--- + ## 1. 개요 ### 목표 @@ -197,7 +278,7 @@ function MyComponent() { | Phase 0 | 크로스 모듈 의존성 해소 | `a99c3b39` | ✅ 완료 | | Phase 1 | 모듈 레지스트리 + 라우트 가드 | `0a65609e` | ✅ 완료 | | Phase 2 | CEO 대시보드 모듈 디커플링 | `46501214` | ✅ 완료 | -| Phase 3 | 물리적 분리 (경계 마커, 검증, 가드, 문서) | (미커밋) | ✅ 완료 | +| Phase 3 | 물리적 분리 (경계 마커, 검증, 가드, 문서) | `4b8ca09e` | ✅ 완료 | --- diff --git a/src/app/[locale]/(protected)/sales/order-management-sales/page.tsx b/src/app/[locale]/(protected)/sales/order-management-sales/page.tsx index ca3114fa..f3572858 100644 --- a/src/app/[locale]/(protected)/sales/order-management-sales/page.tsx +++ b/src/app/[locale]/(protected)/sales/order-management-sales/page.tsx @@ -299,12 +299,12 @@ function OrderListContent() { // 수주: 생산에 넘어가지 않은 건 (DRAFT + CONFIRMED) const orderCount = orders.filter( - (o) => o.status === "draft" || o.status === "order_registered" || o.status === "order_confirmed" + (o) => o.status === "order_registered" || o.status === "order_confirmed" ).length; // 생산: 생산지시대기 + 생산중 (IN_PROGRESS + IN_PRODUCTION) const productionCount = orders.filter( - (o) => o.status === "in_progress" || o.status === "in_production" + (o) => o.status === "production_ordered" || o.status === "in_production" ).length; // 출하: 출하대기 ~ 출고중 (PRODUCED + SHIPPING) diff --git a/src/components/orders/actions.ts b/src/components/orders/actions.ts index 917609c0..3d28bec6 100644 --- a/src/components/orders/actions.ts +++ b/src/components/orders/actions.ts @@ -212,6 +212,8 @@ interface ApiOrderStats { draft: number; confirmed: number; in_progress: number; + in_production?: number; + produced?: number; completed: number; cancelled: number; total_amount: number; diff --git a/src/components/outbound/ShipmentManagement/ShipmentDetail.tsx b/src/components/outbound/ShipmentManagement/ShipmentDetail.tsx index 0696094a..516835d5 100644 --- a/src/components/outbound/ShipmentManagement/ShipmentDetail.tsx +++ b/src/components/outbound/ShipmentManagement/ShipmentDetail.tsx @@ -85,6 +85,7 @@ const STATUS_TRANSITIONS: Record = { ready: 'shipping', shipping: 'completed', completed: null, + cancelled: null, }; export function ShipmentDetail({ id }: ShipmentDetailProps) { diff --git a/src/components/outbound/ShipmentManagement/ShipmentList.tsx b/src/components/outbound/ShipmentManagement/ShipmentList.tsx index 1105f072..d4f19d27 100644 --- a/src/components/outbound/ShipmentManagement/ShipmentList.tsx +++ b/src/components/outbound/ShipmentManagement/ShipmentList.tsx @@ -129,6 +129,7 @@ export function ShipmentList() { ready: 'yellow', shipping: 'blue', completed: 'green', + cancelled: 'red', }; return {