Files
sam-api/app/Http/Controllers/Api/V1/Design/BomCalculationController.php
hskwon bd678dfea9 feat: 업체별 동적 BOM 계산 시스템 구현
- 데이터베이스 스키마 확장: BOM 테이블에 계산 관련 필드 추가
- 계산 엔진 구현: CalculationEngine, FormulaParser, ParameterValidator
- API 구현: 견적 파라미터 추출, 실시간 BOM 계산, 업체별 산출식 관리
- FormRequest 검증: 모든 입력 데이터 검증 및 한국어 에러 메시지
- 라우트 등록: 5개 BOM 계산 API 엔드포인트 추가

주요 기능:
• BOM에서 필요한 조건만 동적 추출하여 견적 화면에 표시
• 경동기업 하드코딩 산출식을 동적 시스템으로 전환
• 업체별 산출식 버전 관리 및 실시간 테스트 지원

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-22 22:09:42 +09:00

288 lines
9.5 KiB
PHP

<?php
namespace App\Http\Controllers\Api\V1\Design;
use App\Http\Controllers\Controller;
use App\Services\Design\BomCalculationService;
use App\Http\Requests\Design\GetEstimateParametersRequest;
use App\Http\Requests\Design\CalculateBomRequest;
use App\Http\Requests\Design\SaveCompanyFormulaRequest;
use App\Http\Resources\ApiResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
/**
* @group Design/BOM Calculation
*
* BOM 기반 동적 산출식 계산 API
*/
class BomCalculationController extends Controller
{
protected BomCalculationService $bomCalculationService;
public function __construct(BomCalculationService $bomCalculationService)
{
$this->bomCalculationService = $bomCalculationService;
}
/**
* 견적 파라미터 조회
*
* 특정 모델의 견적 시 필요한 입력 파라미터 스키마를 조회합니다.
* BOM에 정의된 조건만 동적으로 추출하여 반환합니다.
*
* @urlParam model_id int required 모델 ID Example: 1
* @queryParam company_name string 업체명 (선택사항) Example: 경동기업
*
* @response 200 {
* "success": true,
* "message": "견적 파라미터를 성공적으로 조회했습니다.",
* "data": {
* "model_info": {
* "id": 1,
* "name": "스크린셔터 표준형",
* "version": "v1.0",
* "bom_template_id": 1
* },
* "company_info": {
* "company_type": "경동기업",
* "formula_version": "v2.0",
* "requested_company": "경동기업"
* },
* "parameters": {
* "required_parameters": [
* {
* "key": "W0",
* "label": "오픈사이즈 가로(mm)",
* "type": "integer",
* "required": true,
* "min": 500,
* "max": 15000
* }
* ]
* }
* }
* }
*/
public function getEstimateParameters(GetEstimateParametersRequest $request, int $modelId): JsonResponse
{
return ApiResponse::handle(function () use ($request, $modelId) {
$companyName = $request->query('company_name');
$result = $this->bomCalculationService->getEstimateParameters($modelId, $companyName);
return [
'success' => true,
'data' => $result,
'message' => __('message.fetched')
];
});
}
/**
* BOM 계산 실행
*
* 입력된 파라미터를 기반으로 BOM 수량을 동적으로 계산합니다.
* 업체별 산출식을 적용하여 실시간 견적을 생성합니다.
*
* @urlParam bom_template_id int required BOM 템플릿 ID Example: 1
*
* @bodyParam parameters object required 계산 파라미터
* @bodyParam parameters.W0 int required 오픈사이즈 가로(mm) Example: 3000
* @bodyParam parameters.H0 int required 오픈사이즈 세로(mm) Example: 2500
* @bodyParam parameters.product_type string required 제품타입 Example: screen
* @bodyParam company_name string 업체명 (선택사항) Example: 경동기업
*
* @response 200 {
* "success": true,
* "message": "BOM 계산이 완료되었습니다.",
* "data": {
* "bom_template": {
* "id": 1,
* "name": "Main",
* "company_type": "경동기업",
* "formula_version": "v2.0"
* },
* "input_parameters": {
* "W0": 3000,
* "H0": 2500,
* "product_type": "screen"
* },
* "calculated_values": {
* "W1": 3160,
* "H1": 2850,
* "area": 9.006,
* "weight": 60.42
* },
* "bom_items": [
* {
* "item_id": 1,
* "ref_type": "MATERIAL",
* "ref_id": 101,
* "original_qty": 1,
* "calculated_qty": 2,
* "is_calculated": true,
* "calculation_formula": "bracket_quantity"
* }
* ]
* }
* }
*/
public function calculateBom(CalculateBomRequest $request, int $bomTemplateId): JsonResponse
{
return ApiResponse::handle(function () use ($request, $bomTemplateId) {
$data = $request->validated();
$result = $this->bomCalculationService->calculateBomEstimate(
$bomTemplateId,
$data['parameters'],
$data['company_name'] ?? null
);
if (!$result['success']) {
return [
'success' => false,
'message' => __('error.calculation_failed'),
'error' => $result['error']
];
}
return [
'success' => true,
'data' => $result['data'],
'message' => __('message.calculation.completed')
];
});
}
/**
* 업체별 산출식 목록 조회
*
* 특정 업체의 등록된 산출식 목록을 조회합니다.
*
* @urlParam company_name string required 업체명 Example: 경동기업
*
* @response 200 {
* "success": true,
* "message": "업체 산출식 목록을 조회했습니다.",
* "data": {
* "company_name": "경동기업",
* "total_formulas": 5,
* "formulas": [
* {
* "type": "manufacturing_size",
* "version": "v2.0",
* "description": "제작사이즈 계산식",
* "updated_at": "2025-09-22T15:30:00Z"
* }
* ]
* }
* }
*/
public function getCompanyFormulas(string $companyName): JsonResponse
{
return ApiResponse::handle(function () use ($companyName) {
$result = $this->bomCalculationService->getCompanyFormulas($companyName);
return [
'success' => true,
'data' => $result,
'message' => __('message.fetched')
];
});
}
/**
* 업체별 산출식 등록/수정
*
* 특정 업체의 산출식을 등록하거나 수정합니다.
* 기존 산출식이 있으면 새 버전으로 업데이트됩니다.
*
* @urlParam company_name string required 업체명 Example: 경동기업
* @urlParam formula_type string required 산출식 타입 Example: manufacturing_size
*
* @bodyParam formula_expression string required 계산식 표현식
* @bodyParam parameters array required 필요한 파라미터 정의
* @bodyParam conditions array 적용 조건 (선택사항)
* @bodyParam validation_rules array 검증 규칙 (선택사항)
* @bodyParam description string 산출식 설명 (선택사항)
*
* @response 201 {
* "success": true,
* "message": "업체 산출식이 등록되었습니다.",
* "data": {
* "id": 1,
* "company_name": "경동기업",
* "formula_type": "manufacturing_size",
* "version": "v1.0",
* "is_active": true,
* "created_at": "2025-09-22T15:30:00Z"
* }
* }
*/
public function saveCompanyFormula(SaveCompanyFormulaRequest $request, string $companyName, string $formulaType): JsonResponse
{
return ApiResponse::handle(function () use ($request, $companyName, $formulaType) {
$data = $request->validated();
$result = $this->bomCalculationService->saveCompanyFormula($companyName, $formulaType, $data);
return [
'success' => true,
'data' => $result,
'message' => __('message.company_formula.created')
];
});
}
/**
* 계산식 테스트 실행
*
* 산출식을 실제 적용하기 전에 테스트해볼 수 있습니다.
*
* @bodyParam formula_expression string required 테스트할 계산식
* @bodyParam test_parameters object required 테스트 파라미터
*
* @response 200 {
* "success": true,
* "message": "계산식 테스트가 완료되었습니다.",
* "data": {
* "formula_expression": "bracket_quantity",
* "input_parameters": {"W1": 3000},
* "result": {"result": 2},
* "execution_time_ms": 1.5
* }
* }
*/
public function testFormula(Request $request): JsonResponse
{
return ApiResponse::handle(function () use ($request) {
$request->validate([
'formula_expression' => 'required|string',
'test_parameters' => 'required|array'
]);
$startTime = microtime(true);
// 직접 FormulaParser를 사용하여 테스트
$parser = app(\App\Services\Calculation\FormulaParser::class);
$result = $parser->execute(
$request->input('formula_expression'),
$request->input('test_parameters')
);
$executionTime = (microtime(true) - $startTime) * 1000; // ms로 변환
return [
'success' => true,
'data' => [
'formula_expression' => $request->input('formula_expression'),
'input_parameters' => $request->input('test_parameters'),
'result' => $result,
'execution_time_ms' => round($executionTime, 2)
],
'message' => __('message.formula.test_completed')
];
});
}
}