# 수주 등록 컴포넌트
## 개요
수주 등록 기능을 위한 전용 컴포넌트들입니다. 이 컴포넌트들은 범용 컴포넌트를 래핑하여 수주 등록에 최적화된 고정 설정을 제공합니다.
## 컴포넌트 구조
```
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`