- 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)
179 lines
5.1 KiB
PHP
179 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\V1;
|
|
|
|
use App\Helpers\ApiResponse;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Products\Product;
|
|
use App\Services\ProductBomService;
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
|
|
/**
|
|
* Items BOM Controller (Code-based Adapter)
|
|
*
|
|
* 프론트엔드 요구사항에 맞춰 itemCode 기반으로 BOM을 관리하는 Adapter
|
|
* 내부적으로 code → id 변환 후 기존 ProductBomService 재사용
|
|
*/
|
|
class ItemsBomController extends Controller
|
|
{
|
|
public function __construct(private ProductBomService $service) {}
|
|
|
|
/**
|
|
* GET /api/v1/items/{code}/bom
|
|
* BOM 라인 목록 조회 (flat list)
|
|
*/
|
|
public function index(string $code, Request $request)
|
|
{
|
|
return ApiResponse::handle(function () use ($code, $request) {
|
|
$productId = $this->getProductIdByCode($code);
|
|
|
|
return $this->service->index($productId, $request->all());
|
|
}, __('message.bom.fetch'));
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/items/{code}/bom/tree
|
|
* BOM 트리 구조 조회 (계층적)
|
|
*/
|
|
public function tree(string $code, Request $request)
|
|
{
|
|
return ApiResponse::handle(function () use ($code, $request) {
|
|
$productId = $this->getProductIdByCode($code);
|
|
|
|
return $this->service->tree($request, $productId);
|
|
}, __('message.bom.fetch'));
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/items/{code}/bom
|
|
* BOM 라인 추가 (bulk upsert)
|
|
*/
|
|
public function store(string $code, Request $request)
|
|
{
|
|
return ApiResponse::handle(function () use ($code, $request) {
|
|
$productId = $this->getProductIdByCode($code);
|
|
|
|
return $this->service->bulkUpsert($productId, $request->input('items', []));
|
|
}, __('message.bom.created'));
|
|
}
|
|
|
|
/**
|
|
* PUT /api/v1/items/{code}/bom/{lineId}
|
|
* BOM 라인 수정
|
|
*/
|
|
public function update(string $code, int $lineId, Request $request)
|
|
{
|
|
return ApiResponse::handle(function () use ($code, $lineId, $request) {
|
|
$productId = $this->getProductIdByCode($code);
|
|
|
|
return $this->service->update($productId, $lineId, $request->all());
|
|
}, __('message.bom.updated'));
|
|
}
|
|
|
|
/**
|
|
* DELETE /api/v1/items/{code}/bom/{lineId}
|
|
* BOM 라인 삭제
|
|
*/
|
|
public function destroy(string $code, int $lineId)
|
|
{
|
|
return ApiResponse::handle(function () use ($code, $lineId) {
|
|
$productId = $this->getProductIdByCode($code);
|
|
|
|
$this->service->destroy($productId, $lineId);
|
|
|
|
return 'success';
|
|
}, __('message.bom.deleted'));
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/items/{code}/bom/summary
|
|
* BOM 요약 정보
|
|
*/
|
|
public function summary(string $code)
|
|
{
|
|
return ApiResponse::handle(function () use ($code) {
|
|
$productId = $this->getProductIdByCode($code);
|
|
|
|
return $this->service->summary($productId);
|
|
}, __('message.bom.fetch'));
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/items/{code}/bom/validate
|
|
* BOM 유효성 검사
|
|
*/
|
|
public function validate(string $code)
|
|
{
|
|
return ApiResponse::handle(function () use ($code) {
|
|
$productId = $this->getProductIdByCode($code);
|
|
|
|
return $this->service->validateBom($productId);
|
|
}, __('message.bom.fetch'));
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/items/{code}/bom/replace
|
|
* BOM 전체 교체
|
|
*/
|
|
public function replace(string $code, Request $request)
|
|
{
|
|
return ApiResponse::handle(function () use ($code, $request) {
|
|
$productId = $this->getProductIdByCode($code);
|
|
|
|
return $this->service->replaceBom($productId, $request->all());
|
|
}, __('message.bom.created'));
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/items/{code}/bom/reorder
|
|
* BOM 정렬 변경
|
|
*/
|
|
public function reorder(string $code, Request $request)
|
|
{
|
|
return ApiResponse::handle(function () use ($code, $request) {
|
|
$productId = $this->getProductIdByCode($code);
|
|
|
|
$this->service->reorder($productId, $request->input('items', []));
|
|
|
|
return 'success';
|
|
}, __('message.bom.reordered'));
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/items/{code}/bom/categories
|
|
* 해당 품목의 BOM에서 사용 중인 카테고리 목록
|
|
*/
|
|
public function listCategories(string $code)
|
|
{
|
|
return ApiResponse::handle(function () use ($code) {
|
|
$productId = $this->getProductIdByCode($code);
|
|
|
|
return $this->service->listCategoriesForProduct($productId);
|
|
}, __('message.bom.fetch'));
|
|
}
|
|
|
|
// ==================== Helper Methods ====================
|
|
|
|
/**
|
|
* itemCode로 product ID 조회
|
|
*
|
|
* @throws NotFoundHttpException
|
|
*/
|
|
private function getProductIdByCode(string $code): int
|
|
{
|
|
$tenantId = app('tenant_id');
|
|
|
|
$product = Product::query()
|
|
->where('tenant_id', $tenantId)
|
|
->where('code', $code)
|
|
->first(['id']);
|
|
|
|
if (! $product) {
|
|
throw new NotFoundHttpException(__('error.not_found'));
|
|
}
|
|
|
|
return $product->id;
|
|
}
|
|
}
|