# 품목기준관리(ItemMaster) & 품목관리(Items) API 문서

> 프론트엔드 개발자를 위한 API 스펙 문서
> 작성일: 2025-12-10

## 목차
1. [개요](#개요)
2. [품목관리 (Items) API](#품목관리-items-api)
3. [품목기준관리 (ItemMaster) API](#품목기준관리-itemmaster-api)
4. [공통 응답 형식](#공통-응답-형식)
5. [에러 처리](#에러-처리)

---

## 개요

### 품목관리 (Items) vs 품목기준관리 (ItemMaster)

| 구분 | 품목관리 (Items) | 품목기준관리 (ItemMaster) |
|------|------------------|---------------------------|
| **역할** | 실제 품목 데이터 CRUD | 품목 입력 화면/폼 구성 관리 |
| **대상 데이터** | products, materials 테이블 | item_pages, item_sections, item_fields 테이블 |
| **사용자** | 품목 등록/수정하는 일반 사용자 | 화면 구성을 설정하는 관리자 |
| **비유** | 엑셀 데이터 | 엑셀 양식(템플릿) |

### 품목 유형 코드 (item_type / product_type)

| 코드 | 설명 | 저장 테이블 |
|------|------|-------------|
| `FG` | 완제품 (Finished Goods) | products |
| `PT` | 반제품/부품 (Part) | products |
| `SM` | 반자재 (Semi-Material) | materials |
| `RM` | 원자재 (Raw Material) | materials |
| `CS` | 소모품 (Consumables) | materials |

---

## 품목관리 (Items) API

실제 품목 데이터를 조회/생성/수정/삭제하는 API입니다.

### 1. 통합 품목 목록 조회

**역할**: products와 materials를 통합하여 조회 (UNION 방식)

```
GET /api/v1/items
```

#### Request Parameters (Query)

| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `type` | string | N | 품목 유형 필터 (쉼표 구분). 기본값: `FG,PT,SM,RM,CS` |
| `search` 또는 `q` | string | N | 검색어 (코드, 이름, 태그) |
| `category_id` | integer | N | 카테고리 ID 필터 |
| `size` | integer | N | 페이지당 항목 수. 기본값: 20 |
| `page` | integer | N | 페이지 번호. 기본값: 1 |
| `include_deleted` | boolean | N | 삭제된 항목 포함 여부. 기본값: false |

#### Request 예시
```http
GET /api/v1/items?type=FG,PT&search=모터&size=10&page=1
```

#### Response
```json
{
  "success": true,
  "message": "조회되었습니다.",
  "data": {
    "data": [
      {
        "id": 1,
        "item_type": "FG",
        "code": "P-001",
        "name": "완제품A",
        "specification": null,
        "unit": "EA",
        "category_id": 5,
        "type_code": "FG",
        "created_at": "2025-01-01T00:00:00.000000Z",
        "deleted_at": null,
        "safety_stock": 100,
        "lead_time": 7
      }
    ],
    "current_page": 1,
    "per_page": 20,
    "total": 150,
    "last_page": 8
  }
}
```

#### Response 필드 설명

| 필드 | 설명 |
|------|------|
| `id` | 품목 고유 ID (테이블별 독립) |
| `item_type` | 품목 유형 코드 |
| `code` | 품목 코드 |
| `name` | 품목명 |
| `specification` | 규격 (materials만 해당) |
| `unit` | 단위 (EA, KG, M 등) |
| `category_id` | 카테고리 ID |
| `type_code` | 품목 유형 코드 (item_type과 동일) |
| `created_at` | 생성일시 |
| `deleted_at` | 삭제일시 (Soft Delete) |
| `safety_stock` | 안전재고 (attributes에서 플랫 전개) |
| `lead_time` | 리드타임 (attributes에서 플랫 전개) |

> **Note**: `attributes` JSON 필드는 자동으로 최상위로 플랫 전개되어 반환됩니다.

---

### 2. 단일 품목 조회 (ID 기반)

**역할**: 특정 ID의 품목 상세 정보 조회 (가격 정보 옵션)

```
GET /api/v1/items/{id}
```

#### Path Parameters

| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `id` | integer | Y | 품목 ID |

#### Query Parameters

| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `item_type` | string | N | 품목 유형 코드. 기본값: `FG` |
| `include_price` | boolean | N | 가격 정보 포함 여부. 기본값: false |
| `client_id` | integer | N | 고객 ID (가격 조회 시) |
| `price_date` | string | N | 가격 기준일 (YYYY-MM-DD) |

#### Request 예시
```http
GET /api/v1/items/123?item_type=SM&include_price=true&client_id=5
```

#### Response
```json
{
  "success": true,
  "message": "조회되었습니다.",
  "data": {
    "id": 123,
    "item_type": "SM",
    "code": "M-001",
    "name": "반자재A",
    "specification": "10mm x 20mm",
    "unit": "EA",
    "category_id": 10,
    "category": {
      "id": 10,
      "name": "철강류"
    },
    "type_code": "SM",
    "prices": {
      "sale": {
        "unit_price": 15000,
        "currency": "KRW",
        "effective_from": "2025-01-01"
      },
      "purchase": {
        "unit_price": 10000,
        "currency": "KRW",
        "effective_from": "2025-01-01"
      }
    }
  }
}
```

---

### 3. 단일 품목 조회 (코드 기반)

**역할**: 품목 코드로 상세 정보 조회 (Product → Material 순서로 검색)

```
GET /api/v1/items/code/{code}
```

#### Path Parameters

| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `code` | string | Y | 품목 코드 |

#### Query Parameters

| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `include_bom` | boolean | N | BOM 정보 포함 여부. 기본값: false |

#### Request 예시
```http
GET /api/v1/items/code/P-001?include_bom=true
```

---

### 4. 품목 생성

**역할**: 새 품목 등록 (product_type에 따라 products 또는 materials에 저장)

```
POST /api/v1/items
```

#### Request Body

| 필드 | 타입 | 필수 | 설명 |
|------|------|------|------|
| `code` | string | Y | 품목 코드 (최대 50자, 중복 시 자동 증가) |
| `name` | string | Y | 품목명 (최대 255자) |
| `product_type` | string | Y | 품목 유형: `FG`, `PT`, `SM`, `RM`, `CS` |
| `unit` | string | Y | 단위 (최대 20자) |
| `category_id` | integer | N | 카테고리 ID |
| `description` | string | N | 설명 |
| `is_sellable` | boolean | N | 판매 가능 여부. 기본값: true |
| `is_purchasable` | boolean | N | 구매 가능 여부. 기본값: false |
| `is_producible` | boolean | N | 생산 가능 여부. 기본값: false |
| `safety_stock` | integer | N | 안전재고 (0 이상) |
| `lead_time` | integer | N | 리드타임 (0 이상) |
| `is_variable_size` | boolean | N | 가변 사이즈 여부 |
| `product_category` | string | N | 제품 분류 (최대 20자) |
| `part_type` | string | N | 부품 유형 (최대 20자) |
| `attributes` | object | N | 동적 필드 (JSON) |
| `material_code` | string | N | 자재 코드 (Material 전용) |
| `item_name` | string | N | 품명 (Material 전용) |
| `specification` | string | N | 규격 (Material 전용) |
| `is_inspection` | string | N | 검수 여부: `Y`, `N` |
| `search_tag` | string | N | 검색 태그 |
| `remarks` | string | N | 비고 |
| `options` | object | N | 옵션 (JSON) |

#### Request 예시
```json
{
  "code": "P-NEW-001",
  "name": "신규 완제품",
  "product_type": "FG",
  "unit": "EA",
  "category_id": 5,
  "is_sellable": true,
  "safety_stock": 50,
  "attributes": {
    "color": "red",
    "size": "L"
  }
}
```

#### Response
```json
{
  "success": true,
  "message": "품목이 생성되었습니다.",
  "data": {
    "id": 999,
    "code": "P-NEW-001",
    "name": "신규 완제품",
    "product_type": "FG",
    "unit": "EA",
    "category_id": 5,
    "is_active": true,
    "is_sellable": true,
    "is_purchasable": false,
    "is_producible": false,
    "created_by": 1,
    "created_at": "2025-12-10T10:00:00.000000Z"
  }
}
```

> **중복 코드 처리**: 코드가 이미 존재하면 자동으로 증가합니다.
> - `P-001` 중복 → `P-002`
> - `ABC` 중복 → `ABC-001`

---

### 5. 품목 수정

**역할**: 기존 품목 정보 수정

```
PUT /api/v1/items/{id}
```

#### Path Parameters

| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `id` | integer | Y | 품목 ID |

#### Request Body

| 필드 | 타입 | 필수 | 설명 |
|------|------|------|------|
| `item_type` | string | Y | 품목 유형 (테이블 분기용) |
| `code` | string | N | 품목 코드 |
| `name` | string | N | 품목명 |
| ... | | | (생성과 동일한 필드, 모두 선택) |

#### Request 예시
```json
{
  "item_type": "FG",
  "name": "수정된 완제품명",
  "safety_stock": 100
}
```

#### Response
```json
{
  "success": true,
  "message": "품목이 수정되었습니다.",
  "data": {
    "id": 999,
    "code": "P-NEW-001",
    "name": "수정된 완제품명",
    "safety_stock": 100,
    "updated_by": 1,
    "updated_at": "2025-12-10T11:00:00.000000Z"
  }
}
```

---

### 6. 품목 삭제 (Soft Delete)

**역할**: 품목 삭제 (BOM 구성품으로 사용 중이면 삭제 불가)

```
DELETE /api/v1/items/{id}
```

#### Path Parameters

| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `id` | integer | Y | 품목 ID |

#### Query Parameters

| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `item_type` | string | N | 품목 유형. 기본값: `FG` |

#### Response
```json
{
  "success": true,
  "message": "품목이 삭제되었습니다.",
  "data": "success"
}
```

#### 에러 Response (BOM 사용 중)
```json
{
  "success": false,
  "message": "해당 품목은 3건의 BOM에서 구성품으로 사용 중입니다."
}
```

---

### 7. 품목 일괄 삭제

**역할**: 여러 품목 일괄 삭제

```
DELETE /api/v1/items/batch
```

#### Request Body

| 필드 | 타입 | 필수 | 설명 |
|------|------|------|------|
| `item_type` | string | Y | 품목 유형 |
| `ids` | array | Y | 삭제할 품목 ID 배열 |

#### Request 예시
```json
{
  "item_type": "FG",
  "ids": [1, 2, 3, 4, 5]
}
```

---

## 품목기준관리 (ItemMaster) API

품목 입력 화면의 구성(페이지, 섹션, 필드)을 관리하는 API입니다.

### 구조 개요

```
ItemMaster 계층 구조:

Page (페이지)
├── Section (섹션) - fields 타입
│   ├── Field (필드)
│   ├── Field (필드)
│   └── Field (필드)
└── Section (섹션) - bom 타입
    ├── BomItem (BOM 항목)
    └── BomItem (BOM 항목)
```

- **Page**: 품목 유형별 입력 화면 (예: 완제품 등록 페이지)
- **Section**: 페이지 내 영역 구분 (예: 기본정보, BOM 구성)
- **Field**: 입력 필드 정의 (예: 품목코드, 품목명)
- **BomItem**: BOM 섹션의 구성품 정의

### 1. 초기화 데이터 로드

**역할**: 프론트엔드 앱 초기화 시 필요한 전체 ItemMaster 데이터 로드

```
GET /api/v1/item-master/init
```

#### Response
```json
{
  "success": true,
  "message": "조회되었습니다.",
  "data": {
    "pages": [
      {
        "id": 1,
        "tenant_id": 1,
        "group_id": null,
        "page_name": "완제품 등록",
        "item_type": "FG",
        "absolute_path": "/items/fg/create",
        "is_active": true,
        "sections": [
          {
            "id": 10,
            "title": "기본정보",
            "type": "fields",
            "order_no": 1,
            "is_locked": false,
            "fields": [
              {
                "id": 100,
                "field_name": "품목코드",
                "field_key": "code",
                "field_type": "textbox",
                "is_required": true,
                "order_no": 1,
                "is_locked": true
              }
            ]
          }
        ]
      }
    ],
    "sections": [...],
    "fields": [...],
    "customTabs": [...],
    "unitOptions": [...]
  }
}
```

#### Response 필드 설명

| 필드 | 설명 |
|------|------|
| `pages` | 페이지 목록 (섹션, 필드 중첩 포함) |
| `sections` | 모든 독립 섹션 목록 (재사용 가능) |
| `fields` | 모든 독립 필드 목록 (재사용 가능) |
| `customTabs` | 커스텀 탭 목록 (컬럼 설정 포함) |
| `unitOptions` | 단위 옵션 목록 |

---

### 2. 페이지 API

#### 페이지 목록 조회

```
GET /api/v1/item-master/pages
```

| Query 파라미터 | 타입 | 필수 | 설명 |
|----------------|------|------|------|
| `item_type` | string | N | 품목 유형 필터 |

#### 페이지 생성

```
POST /api/v1/item-master/pages
```

| Request Body | 타입 | 필수 | 설명 |
|--------------|------|------|------|
| `page_name` | string | Y | 페이지명 (최대 255자) |
| `item_type` | string | Y | 품목 유형: `FG`, `PT`, `SM`, `RM`, `CS` |
| `absolute_path` | string | N | 절대 경로 (최대 500자) |

#### 페이지 수정

```
PUT /api/v1/item-master/pages/{id}
```

#### 페이지 삭제

```
DELETE /api/v1/item-master/pages/{id}
```

---

### 3. 섹션 API

#### 독립 섹션 목록 조회

```
GET /api/v1/item-master/sections
```

| Query 파라미터 | 타입 | 필수 | 설명 |
|----------------|------|------|------|
| `is_template` | boolean | N | 템플릿 섹션 필터 |

#### 독립 섹션 생성 (페이지 연결 없음)

```
POST /api/v1/item-master/sections
```

| Request Body | 타입 | 필수 | 설명 |
|--------------|------|------|------|
| `group_id` | integer | N | 계층 번호 |
| `title` | string | Y | 섹션 제목 (최대 255자) |
| `type` | string | Y | 섹션 타입: `fields`, `bom` |

#### 페이지에 섹션 생성 (연결)

```
POST /api/v1/item-master/pages/{pageId}/sections
```

#### 섹션 복제

```
POST /api/v1/item-master/sections/{id}/clone
```

#### 섹션 사용처 조회

```
GET /api/v1/item-master/sections/{id}/usage
```

**역할**: 해당 섹션이 어떤 페이지에서 사용되고 있는지 조회

#### 섹션 수정

```
PUT /api/v1/item-master/sections/{id}
```

#### 섹션 삭제

```
DELETE /api/v1/item-master/sections/{id}
```

#### 섹션 순서 변경

```
PUT /api/v1/item-master/pages/{pageId}/sections/reorder
```

| Request Body | 타입 | 필수 | 설명 |
|--------------|------|------|------|
| `items` | array | Y | `[{id: 1, order_no: 1}, {id: 2, order_no: 2}]` |

---

### 4. 필드 API

#### 독립 필드 목록 조회

```
GET /api/v1/item-master/fields
```

#### 독립 필드 생성 (섹션 연결 없음)

```
POST /api/v1/item-master/fields
```

| Request Body | 타입 | 필수 | 설명 |
|--------------|------|------|------|
| `group_id` | integer | N | 계층 번호 |
| `field_name` | string | Y | 필드 표시명 (최대 255자) |
| `field_key` | string | N | 필드 키 (최대 80자, 영문 시작) |
| `field_type` | string | Y | 필드 타입 (아래 참조) |
| `is_required` | boolean | N | 필수 입력 여부 |
| `default_value` | string | N | 기본값 |
| `placeholder` | string | N | 플레이스홀더 (최대 255자) |
| `display_condition` | object | N | 표시 조건 (JSON) |
| `validation_rules` | object | N | 검증 규칙 (JSON) |
| `options` | array | N | 드롭다운 옵션 등 (JSON) |
| `properties` | object | N | 추가 속성 (JSON) |
| `is_locked` | boolean | N | 잠금 여부 |

#### 필드 타입 (field_type)

| 타입 | 설명 |
|------|------|
| `textbox` | 한 줄 텍스트 입력 |
| `number` | 숫자 입력 |
| `dropdown` | 드롭다운 선택 |
| `checkbox` | 체크박스 |
| `date` | 날짜 선택 |
| `textarea` | 여러 줄 텍스트 입력 |

#### 섹션에 필드 생성 (연결)

```
POST /api/v1/item-master/sections/{sectionId}/fields
```

#### 필드 복제

```
POST /api/v1/item-master/fields/{id}/clone
```

#### 필드 사용처 조회

```
GET /api/v1/item-master/fields/{id}/usage
```

**역할**: 해당 필드가 어떤 섹션에서 사용되고 있는지 조회

#### 필드 수정

```
PUT /api/v1/item-master/fields/{id}
```

#### 필드 삭제

```
DELETE /api/v1/item-master/fields/{id}
```

---

## 공통 응답 형식

모든 API는 동일한 응답 구조를 따릅니다.

### 성공 응답

```json
{
  "success": true,
  "message": "처리되었습니다.",
  "data": { ... }
}
```

### 페이지네이션 응답

```json
{
  "success": true,
  "message": "조회되었습니다.",
  "data": {
    "data": [...],
    "current_page": 1,
    "per_page": 20,
    "total": 100,
    "last_page": 5,
    "from": 1,
    "to": 20
  }
}
```

---

## 에러 처리

### 에러 응답 형식

```json
{
  "success": false,
  "message": "에러 메시지",
  "errors": {
    "field_name": ["검증 오류 메시지"]
  }
}
```

### 주요 HTTP 상태 코드

| 코드 | 설명 |
|------|------|
| `200` | 성공 |
| `201` | 생성 성공 |
| `400` | 잘못된 요청 (검증 실패, 중복 코드 등) |
| `401` | 인증 필요 |
| `403` | 권한 없음 |
| `404` | 리소스 없음 |
| `422` | 검증 실패 (Validation Error) |
| `500` | 서버 오류 |

### 주요 에러 케이스

| 상황 | 메시지 예시 |
|------|-------------|
| 품목 없음 | "해당 품목을 찾을 수 없습니다." |
| 코드 중복 | "이미 사용 중인 품목코드입니다." |
| BOM 사용 중 | "해당 품목은 N건의 BOM에서 구성품으로 사용 중입니다." |
| 필수 필드 누락 | "품목코드는 필수입니다." |
| 잘못된 품목 유형 | "품목 유형은 FG, PT, SM, RM, CS 중 하나여야 합니다." |

---

## 인증

모든 API는 다음 인증이 필요합니다:

1. **API Key**: `X-API-KEY` 헤더
2. **Bearer Token**: `Authorization: Bearer {token}` 헤더

```http
GET /api/v1/items
X-API-KEY: your-api-key
Authorization: Bearer your-access-token
```

---

## 변경 이력

| 날짜 | 버전 | 변경 내용 |
|------|------|----------|
| 2025-12-10 | 1.0 | 최초 작성 |
