Files
sam-api/app/Swagger/v1/ItemsBomApi.php
hskwon 2f2fffb6f0 feat: Items BOM API 추가 (Code 기반 Adapter) - BP-MES Phase 1 Day 6-9
- ItemsBomController 생성 (code 기반 BOM 관리)
- 기존 ProductBomService 100% 재사용 (Adapter 패턴)
- Code → ID 변환 후 기존 비즈니스 로직 활용
- 프론트엔드 요구사항 완벽 대응 (itemCode 기반 API)
- 10개 엔드포인트 추가:
  * GET /items/{code}/bom - BOM 목록 (flat)
  * GET /items/{code}/bom/tree - BOM 트리 (계층)
  * POST /items/{code}/bom - BOM 추가 (bulk upsert)
  * PUT /items/{code}/bom/{lineId} - BOM 수정
  * DELETE /items/{code}/bom/{lineId} - BOM 삭제
  * GET /items/{code}/bom/summary - BOM 요약
  * GET /items/{code}/bom/validate - BOM 검증
  * POST /items/{code}/bom/replace - BOM 전체 교체
  * POST /items/{code}/bom/reorder - BOM 정렬
  * GET /items/{code}/bom/categories - 카테고리 목록
- Swagger 문서 완성 (ItemsBomApi.php)
- i18n 메시지 키 추가 (message.bom.created/updated/deleted)
- Hybrid 구조 지원 (quantity_formula, condition, attributes)
2025-11-17 11:45:16 +09:00

451 lines
15 KiB
PHP

<?php
namespace App\Swagger\v1;
/**
* @OA\Tag(name="Items BOM", description="품목 BOM 관리 (Code 기반)")
*
* @OA\Schema(
* schema="BOMLine",
* type="object",
* required={"id","ref_type","ref_id","quantity"},
*
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="ref_type", type="string", example="PRODUCT", description="PRODUCT|MATERIAL"),
* @OA\Property(property="ref_id", type="integer", example=10),
* @OA\Property(property="code", type="string", example="P-001"),
* @OA\Property(property="name", type="string", example="가이드레일"),
* @OA\Property(property="quantity", type="number", example=2.5),
* @OA\Property(property="sort_order", type="integer", example=1),
* @OA\Property(property="category_id", type="integer", nullable=true, example=1),
* @OA\Property(property="category_name", type="string", nullable=true, example="조립품"),
* @OA\Property(property="quantity_formula", type="string", nullable=true, example="W * 2", description="수량 계산 수식"),
* @OA\Property(property="condition", type="string", nullable=true, example="MOTOR='Y'", description="조건부 BOM")
* )
*
* @OA\Schema(
* schema="BOMTree",
* type="object",
*
* @OA\Property(property="type", type="string", example="PRODUCT"),
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="code", type="string", example="P-001"),
* @OA\Property(property="name", type="string", example="스크린 세트"),
* @OA\Property(property="quantity", type="number", example=1),
* @OA\Property(property="depth", type="integer", example=0),
* @OA\Property(
* property="children",
* type="array",
*
* @OA\Items(ref="#/components/schemas/BOMTree")
* )
* )
*
* @OA\Schema(
* schema="BOMCreateRequest",
* type="object",
* required={"items"},
*
* @OA\Property(
* property="items",
* type="array",
*
* @OA\Items(
* type="object",
* required={"ref_type","ref_id","quantity"},
*
* @OA\Property(property="id", type="integer", nullable=true, example=1, description="기존 라인 ID (업데이트 시)"),
* @OA\Property(property="ref_type", type="string", example="PRODUCT", description="PRODUCT|MATERIAL"),
* @OA\Property(property="ref_id", type="integer", example=10),
* @OA\Property(property="quantity", type="number", example=2.5),
* @OA\Property(property="sort_order", type="integer", nullable=true, example=1),
* @OA\Property(property="quantity_formula", type="string", nullable=true, example="W * 2"),
* @OA\Property(property="condition", type="string", nullable=true, example="MOTOR='Y'")
* )
* )
* )
*
* @OA\Schema(
* schema="BOMUpdateRequest",
* type="object",
*
* @OA\Property(property="ref_type", type="string", example="PRODUCT", description="PRODUCT|MATERIAL"),
* @OA\Property(property="ref_id", type="integer", example=10),
* @OA\Property(property="quantity", type="number", example=2.5),
* @OA\Property(property="sort_order", type="integer", example=1),
* @OA\Property(property="quantity_formula", type="string", nullable=true, example="W * 2"),
* @OA\Property(property="condition", type="string", nullable=true, example="MOTOR='Y'")
* )
*/
class ItemsBomApi
{
/**
* @OA\Get(
* path="/api/v1/items/{code}/bom",
* tags={"Items BOM"},
* summary="BOM 목록 조회 (flat list)",
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
*
* @OA\Parameter(name="code", in="path", required=true, @OA\Schema(type="string"), example="P-001"),
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="BOM 항목 조회"),
* @OA\Property(
* property="data",
* type="array",
*
* @OA\Items(ref="#/components/schemas/BOMLine")
* )
* )
* )
* )
*/
public function index() {}
/**
* @OA\Get(
* path="/api/v1/items/{code}/bom/tree",
* tags={"Items BOM"},
* summary="BOM 트리 구조 조회 (계층적)",
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
*
* @OA\Parameter(name="code", in="path", required=true, @OA\Schema(type="string"), example="P-001"),
* @OA\Parameter(name="depth", in="query", @OA\Schema(type="integer"), example=10, description="최대 깊이 (기본값: 10)"),
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="BOM 항목 조회"),
* @OA\Property(property="data", ref="#/components/schemas/BOMTree")
* )
* )
* )
*/
public function tree() {}
/**
* @OA\Post(
* path="/api/v1/items/{code}/bom",
* tags={"Items BOM"},
* summary="BOM 라인 추가 (bulk upsert)",
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
*
* @OA\Parameter(name="code", in="path", required=true, @OA\Schema(type="string"), example="P-001"),
*
* @OA\RequestBody(
* required=true,
*
* @OA\JsonContent(ref="#/components/schemas/BOMCreateRequest")
* ),
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="BOM 항목이 등록되었습니다."),
* @OA\Property(
* property="data",
* type="object",
* @OA\Property(property="created", type="integer", example=3),
* @OA\Property(property="updated", type="integer", example=1)
* )
* )
* )
* )
*/
public function store() {}
/**
* @OA\Put(
* path="/api/v1/items/{code}/bom/{lineId}",
* tags={"Items BOM"},
* summary="BOM 라인 수정",
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
*
* @OA\Parameter(name="code", in="path", required=true, @OA\Schema(type="string"), example="P-001"),
* @OA\Parameter(name="lineId", in="path", required=true, @OA\Schema(type="integer"), example=1),
*
* @OA\RequestBody(
* required=true,
*
* @OA\JsonContent(ref="#/components/schemas/BOMUpdateRequest")
* ),
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="BOM 항목이 수정되었습니다."),
* @OA\Property(property="data", ref="#/components/schemas/BOMLine")
* )
* )
* )
*/
public function update() {}
/**
* @OA\Delete(
* path="/api/v1/items/{code}/bom/{lineId}",
* tags={"Items BOM"},
* summary="BOM 라인 삭제",
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
*
* @OA\Parameter(name="code", in="path", required=true, @OA\Schema(type="string"), example="P-001"),
* @OA\Parameter(name="lineId", in="path", required=true, @OA\Schema(type="integer"), example=1),
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="BOM 항목이 삭제되었습니다."),
* @OA\Property(property="data", type="string", example="success")
* )
* )
* )
*/
public function destroy() {}
/**
* @OA\Get(
* path="/api/v1/items/{code}/bom/summary",
* tags={"Items BOM"},
* summary="BOM 요약 정보",
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
*
* @OA\Parameter(name="code", in="path", required=true, @OA\Schema(type="string"), example="P-001"),
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="BOM 항목 조회"),
* @OA\Property(
* property="data",
* type="object",
* @OA\Property(property="count", type="integer", example=10),
* @OA\Property(property="count_product", type="integer", example=5),
* @OA\Property(property="count_material", type="integer", example=5),
* @OA\Property(property="quantity_sum", type="string", example="25.5000")
* )
* )
* )
* )
*/
public function summary() {}
/**
* @OA\Get(
* path="/api/v1/items/{code}/bom/validate",
* tags={"Items BOM"},
* summary="BOM 유효성 검사",
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
*
* @OA\Parameter(name="code", in="path", required=true, @OA\Schema(type="string"), example="P-001"),
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="BOM 항목 조회"),
* @OA\Property(
* property="data",
* type="object",
* @OA\Property(property="valid", type="boolean", example=true),
* @OA\Property(
* property="errors",
* type="array",
*
* @OA\Items(
* type="object",
*
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="error", type="string", example="INVALID_QUANTITY")
* )
* )
* )
* )
* )
* )
*/
public function validate() {}
/**
* @OA\Post(
* path="/api/v1/items/{code}/bom/replace",
* tags={"Items BOM"},
* summary="BOM 전체 교체 (기존 삭제 후 재등록)",
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
*
* @OA\Parameter(name="code", in="path", required=true, @OA\Schema(type="string"), example="P-001"),
*
* @OA\RequestBody(
* required=true,
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(
* property="categories",
* type="array",
*
* @OA\Items(
* type="object",
*
* @OA\Property(property="id", type="integer", nullable=true, example=1),
* @OA\Property(property="name", type="string", example="조립품"),
* @OA\Property(
* property="items",
* type="array",
*
* @OA\Items(
* type="object",
* required={"ref_type","ref_id","quantity"},
*
* @OA\Property(property="ref_type", type="string", example="PRODUCT"),
* @OA\Property(property="ref_id", type="integer", example=10),
* @OA\Property(property="quantity", type="number", example=2.5),
* @OA\Property(property="sort_order", type="integer", nullable=true, example=1)
* )
* )
* )
* )
* )
* ),
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="BOM 항목이 등록되었습니다."),
* @OA\Property(
* property="data",
* type="object",
* @OA\Property(property="deleted_count", type="integer", example=5),
* @OA\Property(property="inserted_count", type="integer", example=8),
* @OA\Property(property="message", type="string", example="BOM 저장 성공")
* )
* )
* )
* )
*/
public function replace() {}
/**
* @OA\Post(
* path="/api/v1/items/{code}/bom/reorder",
* tags={"Items BOM"},
* summary="BOM 정렬 변경",
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
*
* @OA\Parameter(name="code", in="path", required=true, @OA\Schema(type="string"), example="P-001"),
*
* @OA\RequestBody(
* required=true,
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(
* property="items",
* type="array",
*
* @OA\Items(
* type="object",
* required={"id","sort_order"},
*
* @OA\Property(property="id", type="integer", example=1),
* @OA\Property(property="sort_order", type="integer", example=2)
* )
* )
* )
* ),
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="BOM 정렬이 변경되었습니다."),
* @OA\Property(property="data", type="string", example="success")
* )
* )
* )
*/
public function reorder() {}
/**
* @OA\Get(
* path="/api/v1/items/{code}/bom/categories",
* tags={"Items BOM"},
* summary="BOM에서 사용 중인 카테고리 목록",
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
*
* @OA\Parameter(name="code", in="path", required=true, @OA\Schema(type="string"), example="P-001"),
*
* @OA\Response(
* response=200,
* description="성공",
*
* @OA\JsonContent(
* type="object",
*
* @OA\Property(property="success", type="boolean", example=true),
* @OA\Property(property="message", type="string", example="BOM 항목 조회"),
* @OA\Property(
* property="data",
* type="array",
*
* @OA\Items(
* type="object",
*
* @OA\Property(property="category_id", type="integer", nullable=true, example=1),
* @OA\Property(property="category_name", type="string", example="조립품"),
* @OA\Property(property="count", type="integer", example=5)
* )
* )
* )
* )
* )
*/
public function listCategories() {}
}