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