# 수주 등록 컴포넌트 ## 개요 수주 등록 기능을 위한 전용 컴포넌트들입니다. 이 컴포넌트들은 범용 컴포넌트를 래핑하여 수주 등록에 최적화된 고정 설정을 제공합니다. ## 컴포넌트 구조 ``` frontend/components/order/ ├── OrderRegistrationModal.tsx # 수주 등록 메인 모달 ├── OrderCustomerSearch.tsx # 거래처 검색 (전용) ├── OrderItemRepeaterTable.tsx # 품목 반복 테이블 (전용) └── README.md # 문서 (현재 파일) ``` ## 1. OrderRegistrationModal 수주 등록 메인 모달 컴포넌트입니다. ### Props ```typescript interface OrderRegistrationModalProps { /** 모달 열림/닫힘 상태 */ open: boolean; /** 모달 상태 변경 핸들러 */ onOpenChange: (open: boolean) => void; /** 수주 등록 성공 시 콜백 */ onSuccess?: () => void; } ``` ### 사용 예시 ```tsx import { OrderRegistrationModal } from "@/components/order/OrderRegistrationModal"; function MyComponent() { const [isOpen, setIsOpen] = useState(false); return ( <> { console.log("수주 등록 완료!"); // 목록 새로고침 등 }} /> ); } ``` ### 기능 - **입력 방식 선택**: 거래처 우선, 견적 방식, 단가 방식 - **거래처 검색**: 자동완성 드롭다운으로 거래처 검색 및 선택 - **품목 관리**: 모달에서 품목 검색 및 추가, 수량/단가 입력, 금액 자동 계산 - **전체 금액 표시**: 추가된 품목들의 총 금액 계산 - **유효성 검사**: 거래처 및 품목 필수 입력 체크 --- ## 2. OrderCustomerSearch 수주 등록 전용 거래처 검색 컴포넌트입니다. ### 특징 - `customer_mng` 테이블만 조회 (고정) - 거래처명, 거래처코드, 사업자번호로 검색 (고정) - 추가 정보 표시 (주소, 연락처) ### Props ```typescript interface OrderCustomerSearchProps { /** 현재 선택된 거래처 코드 */ value: string; /** 거래처 선택 시 콜백 (거래처 코드, 전체 데이터) */ onChange: (customerCode: string | null, fullData?: any) => void; /** 비활성화 여부 */ disabled?: boolean; } ``` ### 사용 예시 ```tsx import { OrderCustomerSearch } from "@/components/order/OrderCustomerSearch"; function MyForm() { const [customerCode, setCustomerCode] = useState(""); const [customerName, setCustomerName] = useState(""); return ( { setCustomerCode(code || ""); setCustomerName(fullData?.customer_name || ""); }} /> ); } ``` ### 고정 설정 | 설정 | 값 | 설명 | |------|-----|------| | `tableName` | `customer_mng` | 거래처 테이블 | | `displayField` | `customer_name` | 표시 필드 | | `valueField` | `customer_code` | 값 필드 | | `searchFields` | `["customer_name", "customer_code", "business_number"]` | 검색 대상 필드 | | `additionalFields` | `["customer_code", "address", "contact_phone"]` | 추가 표시 필드 | --- ## 3. OrderItemRepeaterTable 수주 등록 전용 품목 반복 테이블 컴포넌트입니다. ### 특징 - `item_info` 테이블만 조회 (고정) - 수주에 필요한 컬럼만 표시 (품번, 품명, 수량, 단가, 금액 등) - 금액 자동 계산 (`수량 * 단가`) ### Props ```typescript interface OrderItemRepeaterTableProps { /** 현재 선택된 품목 목록 */ value: any[]; /** 품목 목록 변경 시 콜백 */ onChange: (items: any[]) => void; /** 비활성화 여부 */ disabled?: boolean; } ``` ### 사용 예시 ```tsx import { OrderItemRepeaterTable } from "@/components/order/OrderItemRepeaterTable"; function MyForm() { const [items, setItems] = useState([]); return ( ); } ``` ### 고정 컬럼 설정 | 필드 | 라벨 | 타입 | 편집 | 필수 | 계산 | 설명 | |------|------|------|------|------|------|------| | `id` | 품번 | text | ❌ | - | - | 품목 ID | | `item_name` | 품명 | text | ❌ | - | - | 품목명 | | `item_number` | 품목번호 | text | ❌ | - | - | 품목 번호 | | `quantity` | 수량 | number | ✅ | ✅ | - | 주문 수량 (기본값: 1) | | `selling_price` | 단가 | number | ✅ | ✅ | - | 판매 단가 | | `amount` | 금액 | number | ❌ | - | ✅ | 자동 계산 (수량 * 단가) | | `delivery_date` | 납품일 | date | ✅ | - | - | 납품 예정일 | | `note` | 비고 | text | ✅ | - | - | 추가 메모 | ### 계산 규칙 ```javascript amount = quantity * selling_price ``` --- ## 범용 컴포넌트 vs 전용 컴포넌트 ### 왜 전용 컴포넌트를 만들었나? | 항목 | 범용 컴포넌트 | 전용 컴포넌트 | |------|--------------|--------------| | **목적** | 화면 편집기에서 다양한 용도로 사용 | 수주 등록 전용 | | **설정** | ConfigPanel에서 자유롭게 변경 가능 | 하드코딩으로 고정 | | **유연성** | 높음 (모든 테이블/필드 지원) | 낮음 (수주에 최적화) | | **안정성** | 사용자 실수 가능 | 설정 변경 불가로 안전 | | **위치** | `lib/registry/components/` | `components/order/` | ### 범용 컴포넌트 (화면 편집기용) ```tsx // ❌ 수주 등록에서 사용 금지 ``` **문제점:** - 사용자가 `tableName`을 `item_info`로 변경하면 거래처가 아닌 품목이 조회됨 - `valueField`를 변경하면 `formData.customerCode`에 잘못된 값 저장 - 수주 로직이 깨짐 ### 전용 컴포넌트 (수주 등록용) ```tsx // ✅ 수주 등록에서 사용 ``` **장점:** - 설정이 하드코딩되어 있어 변경 불가 - 수주 등록 로직에 최적화 - 안전하고 예측 가능 --- ## API 엔드포인트 ### 거래처 검색 ``` GET /api/entity-search/customer_mng Query Parameters: - searchText: 검색어 - searchFields: customer_name,customer_code,business_number - page: 페이지 번호 - limit: 페이지 크기 ``` ### 품목 검색 ``` GET /api/entity-search/item_info Query Parameters: - searchText: 검색어 - searchFields: item_name,id,item_number - page: 페이지 번호 - limit: 페이지 크기 ``` ### 수주 등록 ``` POST /api/orders Body: { inputMode: "customer_first" | "quotation" | "unit_price", customerCode: string, deliveryDate?: string, items: Array<{ id: string, item_name: string, quantity: number, selling_price: number, amount: number, delivery_date?: string, note?: string }>, memo?: string } Response: { success: boolean, data?: { orderNumber: string, orderId: number }, message?: string } ``` --- ## 멀티테넌시 (Multi-Tenancy) 모든 API 호출은 자동으로 `company_code` 필터링이 적용됩니다. - 거래처 검색: 현재 로그인한 사용자의 회사에 속한 거래처만 조회 - 품목 검색: 현재 로그인한 사용자의 회사에 속한 품목만 조회 - 수주 등록: 자동으로 현재 사용자의 `company_code` 추가 --- ## 트러블슈팅 ### 1. 거래처가 검색되지 않음 **원인**: `customer_mng` 테이블에 데이터가 없거나 `company_code`가 다름 **해결**: ```sql -- 거래처 데이터 확인 SELECT * FROM customer_mng WHERE company_code = 'YOUR_COMPANY_CODE'; ``` ### 2. 품목이 검색되지 않음 **원인**: `item_info` 테이블에 데이터가 없거나 `company_code`가 다름 **해결**: ```sql -- 품목 데이터 확인 SELECT * FROM item_info WHERE company_code = 'YOUR_COMPANY_CODE'; ``` ### 3. 수주 등록 실패 **원인**: 필수 필드 누락 또는 백엔드 API 오류 **해결**: 1. 브라우저 개발자 도구 콘솔 확인 2. 네트워크 탭에서 API 응답 확인 3. 백엔드 로그 확인 --- ## 개발 참고 사항 ### 새로운 전용 컴포넌트 추가 시 1. **범용 컴포넌트 활용**: 기존 범용 컴포넌트를 래핑 2. **설정 고정**: 비즈니스 로직에 필요한 설정을 하드코딩 3. **Props 최소화**: 외부에서 제어 가능한 최소한의 prop만 노출 4. **문서 작성**: README에 사용법 및 고정 설정 명시 ### 예시: 견적 등록 전용 컴포넌트 ```tsx // QuotationCustomerSearch.tsx export function QuotationCustomerSearch({ value, onChange }: Props) { return ( ); } ``` --- ## 관련 파일 - 범용 컴포넌트: - `lib/registry/components/autocomplete-search-input/` - `lib/registry/components/entity-search-input/` - `lib/registry/components/modal-repeater-table/` - 백엔드 API: - `backend-node/src/controllers/entitySearchController.ts` - `backend-node/src/controllers/orderController.ts` - 계획서: - `수주등록_화면_개발_계획서.md`