// 품목기준관리 API 타입 정의 // API 응답 기준 snake_case 사용 // ============================================ // 공통 타입 // ============================================ /** * 표준 API 응답 래퍼 */ export interface ApiResponse { success: boolean; message: string; data: T; } /** * 페이지네이션 메타데이터 */ export interface PaginationMeta { current_page: number; per_page: number; total: number; last_page: number; } // ============================================ // 초기화 API // ============================================ /** * 초기화 API 응답 - 화면 진입 시 전체 데이터 로드 * GET /v1/item-master/init * * 2025-11-27 변경사항: * - masterFields → 독립 필드(item_fields WHERE section_id IS NULL)로 대체 예정 * - fields 필드 추가: 모든 필드 목록 (독립 필드 + 섹션 내 필드) */ export interface InitResponse { pages: ItemPageResponse[]; sections?: ItemSectionResponse[]; // 2025-11-26 추가: 모든 섹션 (독립 섹션 포함) fields?: ItemFieldResponse[]; // 2025-11-27 추가: 모든 필드 (독립 필드 = section_id IS NULL) sectionTemplates: SectionTemplateResponse[]; /** @deprecated 2025-11-27: item_fields로 통합됨. fields 필드 사용 권장 */ masterFields: MasterFieldResponse[]; customTabs: CustomTabResponse[]; tabColumns: Record; // tab_id를 key로 사용 unitOptions: UnitOptionResponse[]; } // ============================================ // 페이지 관리 // ============================================ /** * 페이지 생성/수정 요청 * POST /v1/item-master/pages * PUT /v1/item-master/pages/{id} */ export interface ItemPageRequest { page_name: string; item_type: 'FG' | 'PT' | 'SM' | 'RM' | 'CS'; absolute_path?: string; is_active?: boolean; } /** * 페이지 응답 */ export interface ItemPageResponse { id: number; tenant_id: number; page_name: string; item_type: string; description: string | null; // 2025-11-26 추가: 페이지 설명 absolute_path: string | null; is_active: boolean; order_no: number; // 2025-11-26 추가: 순서 번호 created_by: number | null; updated_by: number | null; created_at: string; updated_at: string; sections?: ItemSectionResponse[]; // Nested 조회 시 포함 } /** * 페이지 순서 변경 요청 * PUT /v1/item-master/pages/reorder (향후 구현 가능성) */ export interface PageReorderRequest { page_orders: Array<{ id: number; order_no: number; }>; } // ============================================ // 섹션 관리 // ============================================ /** * 섹션 생성/수정 요청 * POST /v1/item-master/pages/{pageId}/sections * PUT /v1/item-master/sections/{id} */ export interface ItemSectionRequest { title: string; type: 'fields' | 'bom'; } /** * 섹션 응답 * 2025-11-26: section_templates 테이블 통합으로 is_template, is_default, description, group_id 추가 */ export interface ItemSectionResponse { id: number; tenant_id: number; group_id: number | null; // 그룹 ID (독립 섹션 그룹화용) page_id: number | null; // 페이지 ID (null이면 독립 섹션) title: string; type: 'fields' | 'bom'; order_no: number; is_template: boolean; // 템플릿 여부 (section_templates 통합) is_default: boolean; // 기본 템플릿 여부 description: string | null; // 섹션 설명 created_by: number | null; updated_by: number | null; created_at: string; updated_at: string; fields?: ItemFieldResponse[]; // Nested 조회 시 포함 bomItems?: BomItemResponse[]; // Nested 조회 시 포함 (camelCase) bom_items?: BomItemResponse[]; // 2025-11-27: 백엔드가 snake_case로 반환 } /** * 섹션 순서 변경 요청 * PUT /v1/item-master/pages/{pageId}/sections/reorder * 백엔드 API는 'items' 필드를 기대함 */ export interface SectionReorderRequest { items: Array<{ id: number; order_no: number; }>; } /** * 독립 섹션 생성 요청 * POST /v1/item-master/sections * 2025-11-26 신규 API */ export interface IndependentSectionRequest { group_id?: number; title: string; type: 'fields' | 'bom'; is_template?: boolean; is_default?: boolean; description?: string; } /** * 섹션 사용처 응답 * GET /v1/item-master/sections/{id}/usage * 2025-11-26 신규 API */ export interface SectionUsageResponse { section_id: number; direct_page: { id: number; page_name: string; item_type: string; } | null; linked_pages: Array<{ id: number; page_name: string; item_type: string; }>; total_usage_count: number; } /** * 페이지에 섹션 연결 요청 * POST /v1/item-master/pages/{id}/link-section * 2025-11-26 신규 API */ export interface LinkSectionRequest { child_id: number; // 연결할 섹션 ID (백엔드: child_id) order_no?: number; } // ============================================ // 필드 관리 // ============================================ /** * 필드 생성/수정 요청 * POST /v1/item-master/sections/{sectionId}/fields * PUT /v1/item-master/fields/{id} */ export interface ItemFieldRequest { field_name: string; field_key?: string; // 2025-11-28: 필드 키 (영문, 숫자, 언더스코어만 허용, 영문으로 시작) field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea'; is_required?: boolean; placeholder?: string; default_value?: string; display_condition?: Record; // {"field_id": "1", "operator": "equals", "value": "true"} validation_rules?: Record; // {"min": 0, "max": 100, "pattern": "regex"} options?: Array<{ label: string; value: string }>; // dropdown 옵션 properties?: Record; // {"unit": "mm", "precision": 2, "format": "YYYY-MM-DD"} is_locked?: boolean; // 2025-11-28: 잠금 여부 } /** * 필드 응답 * 2025-11-26: section_id를 nullable로 변경 (독립 필드 지원) * 2025-11-27: item_master_fields 테이블 통합으로 category, description, is_common 추가 */ export interface ItemFieldResponse { id: number; tenant_id: number; group_id: number | null; // 그룹 ID (독립 필드 그룹화용) section_id: number | null; // 섹션 ID (null이면 독립 필드/마스터 항목) master_field_id?: number | null; // 마스터 항목 ID (마스터에서 가져온 경우) field_name: string; field_key: string | null; // 2025-11-28: 필드 키 (형식: {ID}_{사용자입력}) field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea'; order_no: number; is_required: boolean; placeholder: string | null; default_value: string | null; display_condition: Record | null; validation_rules: Record | null; options: Array<{ label: string; value: string }> | null; properties: Record | null; // 2025-11-27 추가: item_master_fields 통합으로 추가된 필드 category: string | null; // 카테고리 (예: "공통", "완제품", "부품") description: string | null; // 필드 설명 is_common: boolean; // 공통 필드 여부 // 2025-11-28 추가: 잠금 기능 is_locked: boolean; // 잠금 여부 locked_by: number | null; // 잠금 설정자 locked_at: string | null; // 잠금 시간 created_by: number | null; updated_by: number | null; created_at: string; updated_at: string; } /** * 필드 순서 변경 요청 * PUT /v1/item-master/sections/{sectionId}/fields/reorder */ export interface FieldReorderRequest { field_orders: Array<{ id: number; order_no: number; }>; } /** * 독립 필드 생성 요청 * POST /v1/item-master/fields * 2025-11-26 신규 API * 2025-11-27: item_master_fields 통합으로 category, description, is_common 추가 */ export interface IndependentFieldRequest { group_id?: number; field_name: string; field_key?: string; // 2025-11-28: 필드 키 (영문, 숫자, 언더스코어만 허용, 영문으로 시작) field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea'; is_required?: boolean; default_value?: string; placeholder?: string; options?: Array<{ label: string; value: string }>; properties?: Record; validation_rules?: Record; display_condition?: Record; // 2025-11-27 추가: item_master_fields 통합으로 추가된 필드 category?: string; description?: string; is_common?: boolean; is_locked?: boolean; // 2025-11-28: 잠금 여부 } /** * 필드 사용처 응답 * GET /v1/item-master/fields/{id}/usage * 2025-11-26 신규 API */ export interface FieldUsageResponse { field_id: number; direct_section: { id: number; title: string; type: 'fields' | 'bom'; } | null; linked_sections: Array<{ id: number; title: string; type: 'fields' | 'bom'; }>; total_usage_count: number; } /** * 섹션에 필드 연결 요청 * POST /v1/item-master/sections/{id}/link-field * 2025-11-26 신규 API */ export interface LinkFieldRequest { child_id: number; // 연결할 필드 ID (백엔드: child_id) order_no?: number; } // ============================================ // BOM 관리 // ============================================ /** * BOM 항목 생성/수정 요청 * POST /v1/item-master/sections/{sectionId}/bom-items * PUT /v1/item-master/bom-items/{id} */ export interface BomItemRequest { item_code?: string; item_name: string; quantity: number; unit?: string; unit_price?: number; total_price?: number; spec?: string; note?: string; } /** * BOM 항목 응답 * 2025-11-26: section_id를 nullable로 변경 (독립 BOM 지원) */ export interface BomItemResponse { id: number; tenant_id: number; group_id: number | null; // 그룹 ID (독립 BOM 그룹화용) section_id: number | null; // 섹션 ID (null이면 독립 BOM) item_code: string | null; item_name: string; quantity: number; unit: string | null; unit_price: number | null; total_price: number | null; spec: string | null; note: string | null; created_by: number | null; updated_by: number | null; created_at: string; updated_at: string; } /** * 독립 BOM 항목 생성 요청 * POST /v1/item-master/bom-items * 2025-11-26 신규 API */ export interface IndependentBomItemRequest { group_id?: number; item_code?: string; item_name: string; quantity?: number; unit?: string; unit_price?: number; spec?: string; note?: string; } // ============================================ // 엔티티 관계 (Entity Relationships) // 2025-11-27 신규: 독립 엔티티 + 링크 테이블 구조 // ============================================ /** * 엔티티 타입 상수 */ export type EntityType = 'page' | 'section' | 'field' | 'bom'; export type ParentEntityType = 'page' | 'section'; export type ChildEntityType = 'section' | 'field' | 'bom'; /** * 엔티티 관계 응답 * entity_relationships 테이블의 레코드 */ export interface EntityRelationshipResponse { id: number; tenant_id: number; group_id: number; // 그룹 ID (1: 품목관리) parent_type: ParentEntityType; parent_id: number; child_type: ChildEntityType; child_id: number; order_no: number; metadata: Record | null; created_at: string; updated_at: string; } /** * 엔티티 연결 요청 (공통) * POST /pages/{pageId}/link-section * POST /pages/{pageId}/link-field * POST /sections/{sectionId}/link-field * POST /sections/{sectionId}/link-bom */ export interface LinkEntityRequest { child_id: number; order_no?: number; } /** * 관계 순서 변경 요청 * POST /v1/item-master/relationships/reorder */ export interface ReorderRelationshipsRequest { parent_type: ParentEntityType; parent_id: number; ordered_items: Array<{ child_type: ChildEntityType; child_id: number; }>; } /** * 섹션에 BOM 연결 요청 * POST /v1/item-master/sections/{id}/link-bom */ export interface LinkBomRequest { child_id: number; order_no?: number; } // ============================================ // 페이지 구조 조회 // ============================================ /** * 페이지 전체 구조 응답 * GET /v1/item-master/pages/{id}/structure * 2025-11-27 업데이트: 링크 테이블 기반 구조 */ export interface PageStructureResponse { page: ItemPageResponse; sections: Array<{ section: ItemSectionResponse; order_no: number; fields: Array<{ field: ItemFieldResponse; order_no: number; }>; bom_items: Array<{ bom_item: BomItemResponse; order_no: number; }>; }>; direct_fields: Array<{ field: ItemFieldResponse; order_no: number; }>; } // ============================================ // 섹션 템플릿 // ============================================ /** * 섹션 템플릿 생성/수정 요청 * POST /v1/item-master/section-templates * PUT /v1/item-master/section-templates/{id} */ export interface SectionTemplateRequest { title: string; type: 'fields' | 'bom'; description?: string; is_default?: boolean; } /** * 섹션 템플릿 응답 */ export interface SectionTemplateResponse { id: number; tenant_id: number; title: string; type: 'fields' | 'bom'; description: string | null; is_default: boolean; created_by: number | null; updated_by: number | null; created_at: string; updated_at: string; } // ============================================ // 마스터 필드 (DEPRECATED) // 2025-11-27: item_master_fields 테이블이 item_fields로 통합됨 // 독립 필드 = item_fields WHERE section_id IS NULL // IndependentFieldRequest 및 ItemFieldResponse 사용 권장 // ============================================ /** * 마스터 필드 생성/수정 요청 * @deprecated 2025-11-27: item_fields로 통합됨. IndependentFieldRequest 사용 권장 * POST /v1/item-master/master-fields → POST /v1/item-master/fields 사용 * PUT /v1/item-master/master-fields/{id} → PUT /v1/item-master/fields/{id} 사용 */ export interface MasterFieldRequest { field_name: string; field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea'; category?: string; description?: string; is_common?: boolean; default_value?: string; options?: Array<{ label: string; value: string }>; validation_rules?: Record; properties?: Record; } /** * 마스터 필드 응답 * @deprecated 2025-11-27: item_fields로 통합됨. ItemFieldResponse 사용 권장 * 독립 필드 조회: GET /v1/item-master/fields?section_id=null */ export interface MasterFieldResponse { id: number; tenant_id: number; field_name: string; field_key: string | null; // 2025-11-28: 필드 키 추가 (형식: {ID}_{사용자입력}) field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea'; category: string | null; description: string | null; is_common: boolean; default_value: string | null; options: Array<{ label: string; value: string }> | null; validation_rules: Record | null; properties: Record | null; created_by: number | null; updated_by: number | null; created_at: string; updated_at: string; } // ============================================ // 커스텀 탭 // ============================================ /** * 커스텀 탭 생성/수정 요청 * POST /v1/item-master/custom-tabs * PUT /v1/item-master/custom-tabs/{id} */ export interface CustomTabRequest { label: string; icon?: string; is_default?: boolean; } /** * 커스텀 탭 응답 */ export interface CustomTabResponse { id: number; tenant_id: number; label: string; icon: string | null; is_default: boolean; order_no: number; created_by: number | null; updated_by: number | null; created_at: string; updated_at: string; } /** * 탭 순서 변경 요청 * PUT /v1/item-master/custom-tabs/reorder */ export interface TabReorderRequest { tab_orders: Array<{ id: number; order_no: number; }>; } /** * 탭 컬럼 설정 업데이트 요청 * PUT /v1/item-master/custom-tabs/{id}/columns */ export interface TabColumnUpdateRequest { columns: Array<{ key: string; label: string; visible: boolean; order: number; }>; } /** * 탭 컬럼 응답 */ export interface TabColumnResponse { key: string; label: string; visible: boolean; order: number; } // ============================================ // 단위 옵션 // ============================================ /** * 단위 옵션 생성 요청 * POST /v1/item-master/units */ export interface UnitOptionRequest { label: string; value: string; } /** * 단위 옵션 응답 */ export interface UnitOptionResponse { id: number; tenant_id: number; label: string; value: string; created_by: number | null; created_at: string; updated_at: string; } // ============================================ // 에러 타입 // ============================================ /** * API 에러 응답 */ export interface ApiErrorResponse { success: false; message: string; errors?: Record; // Validation 에러 } /** * API 에러 클래스용 타입 */ export interface ApiErrorData { status: number; message: string; errors?: Record; }