Files
sam-api/app/Http/Controllers/Api/V1/QuoteController.php
kent 4e59bbf574 feat: Phase 1.2 - 다건 BOM 기반 자동산출 API 구현
- QuoteBomBulkCalculateRequest 생성 (React camelCase → API 약어 변환)
- QuoteCalculationService.calculateBomBulk() 메서드 추가
- POST /api/v1/quotes/calculate/bom/bulk 엔드포인트 추가
- Swagger 스키마 및 문서 업데이트

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-02 13:13:50 +09:00

233 lines
7.0 KiB
PHP

<?php
namespace App\Http\Controllers\Api\V1;
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Http\Requests\Quote\QuoteBomBulkCalculateRequest;
use App\Http\Requests\Quote\QuoteBomCalculateRequest;
use App\Http\Requests\Quote\QuoteBulkDeleteRequest;
use App\Http\Requests\Quote\QuoteCalculateRequest;
use App\Http\Requests\Quote\QuoteIndexRequest;
use App\Http\Requests\Quote\QuoteSendEmailRequest;
use App\Http\Requests\Quote\QuoteSendKakaoRequest;
use App\Http\Requests\Quote\QuoteStoreRequest;
use App\Http\Requests\Quote\QuoteUpdateRequest;
use App\Services\Quote\QuoteCalculationService;
use App\Services\Quote\QuoteDocumentService;
use App\Services\Quote\QuoteNumberService;
use App\Services\Quote\QuoteService;
class QuoteController extends Controller
{
public function __construct(
private QuoteService $quoteService,
private QuoteNumberService $numberService,
private QuoteCalculationService $calculationService,
private QuoteDocumentService $documentService
) {}
/**
* 견적 목록 조회
*/
public function index(QuoteIndexRequest $request)
{
return ApiResponse::handle(function () use ($request) {
return $this->quoteService->index($request->validated());
}, __('message.quote.fetched'));
}
/**
* 견적 단건 조회
*/
public function show(int $id)
{
return ApiResponse::handle(function () use ($id) {
return $this->quoteService->show($id);
}, __('message.quote.fetched'));
}
/**
* 견적 생성
*/
public function store(QuoteStoreRequest $request)
{
return ApiResponse::handle(function () use ($request) {
return $this->quoteService->store($request->validated());
}, __('message.quote.created'));
}
/**
* 견적 수정
*/
public function update(QuoteUpdateRequest $request, int $id)
{
return ApiResponse::handle(function () use ($request, $id) {
return $this->quoteService->update($id, $request->validated());
}, __('message.quote.updated'));
}
/**
* 견적 삭제
*/
public function destroy(int $id)
{
return ApiResponse::handle(function () use ($id) {
$this->quoteService->destroy($id);
return null;
}, __('message.quote.deleted'));
}
/**
* 견적 일괄 삭제
*/
public function bulkDestroy(QuoteBulkDeleteRequest $request)
{
return ApiResponse::handle(function () use ($request) {
$count = $this->quoteService->bulkDestroy($request->validated()['ids']);
return ['deleted_count' => $count];
}, __('message.quote.bulk_deleted'));
}
/**
* 견적 확정
*/
public function finalize(int $id)
{
return ApiResponse::handle(function () use ($id) {
return $this->quoteService->finalize($id);
}, __('message.quote.finalized'));
}
/**
* 견적 확정 취소
*/
public function cancelFinalize(int $id)
{
return ApiResponse::handle(function () use ($id) {
return $this->quoteService->cancelFinalize($id);
}, __('message.quote.finalize_cancelled'));
}
/**
* 수주 전환
*/
public function convertToOrder(int $id)
{
return ApiResponse::handle(function () use ($id) {
return $this->quoteService->convertToOrder($id);
}, __('message.quote.converted'));
}
/**
* 견적번호 미리보기
*/
public function previewNumber(?string $productCategory = null)
{
return ApiResponse::handle(function () use ($productCategory) {
return $this->numberService->preview($productCategory);
}, __('message.fetched'));
}
/**
* 자동산출 미리보기
*/
public function calculate(QuoteCalculateRequest $request)
{
return ApiResponse::handle(function () use ($request) {
$validated = $request->validated();
return $this->calculationService->calculate(
$validated['inputs'] ?? $validated,
$validated['product_category'] ?? null
);
}, __('message.quote.calculated'));
}
/**
* BOM 기반 자동산출 (10단계 디버깅 포함)
*
* React 견적등록 화면에서 완제품 코드와 입력 변수를 받아
* BOM 기반으로 품목/단가/금액을 계산합니다.
*/
public function calculateBom(QuoteBomCalculateRequest $request)
{
return ApiResponse::handle(function () use ($request) {
return $this->calculationService->calculateBom(
$request->finished_goods_code,
$request->getInputVariables(),
$request->boolean('debug', false)
);
}, __('message.quote.calculated'));
}
/**
* 다건 BOM 기반 자동산출
*
* React 견적등록 화면에서 여러 품목의 완제품 코드와 입력 변수를 받아
* BOM 기반으로 일괄 계산합니다.
* React QuoteFormItem 필드명(camelCase)과 API 변수명(약어) 모두 지원합니다.
*/
public function calculateBomBulk(QuoteBomBulkCalculateRequest $request)
{
return ApiResponse::handle(function () use ($request) {
return $this->calculationService->calculateBomBulk(
$request->getInputItems(),
$request->boolean('debug', false)
);
}, __('message.quote.bulk_calculated'));
}
/**
* 자동산출 입력 스키마 조회
*/
public function calculationSchema(?string $productCategory = null)
{
return ApiResponse::handle(function () use ($productCategory) {
return $this->calculationService->getInputSchema($productCategory);
}, __('message.fetched'));
}
/**
* 견적서 PDF 생성
*/
public function generatePdf(int $id)
{
return ApiResponse::handle(function () use ($id) {
return $this->documentService->generatePdf($id);
}, __('message.quote.pdf_generated'));
}
/**
* 견적서 이메일 발송
*/
public function sendEmail(QuoteSendEmailRequest $request, int $id)
{
return ApiResponse::handle(function () use ($request, $id) {
return $this->documentService->sendEmail($id, $request->validated());
}, __('message.quote_email_sent'));
}
/**
* 견적서 카카오톡 발송
*/
public function sendKakao(QuoteSendKakaoRequest $request, int $id)
{
return ApiResponse::handle(function () use ($request, $id) {
return $this->documentService->sendKakao($id, $request->validated());
}, __('message.quote_kakao_sent'));
}
/**
* 발송 이력 조회
*/
public function sendHistory(int $id)
{
return ApiResponse::handle(function () use ($id) {
return $this->documentService->getSendHistory($id);
}, __('message.fetched'));
}
}