feat:개소별 자재 투입 관리 API 추가

- work_order_material_inputs 테이블 신규 생성 (개소별 자재 투입 추적)
- 개소별 자재 조회/투입/이력/삭제/수정 API 5개 추가
- StockService.increaseToLot: LOT 수량 복원 메서드 추가
- WorkOrderService에 개소별 자재 투입 비즈니스 로직 구현
- WorkOrder, WorkOrderItem 모델에 materialInputs 관계 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 03:41:35 +09:00
parent d730c2d91a
commit e4c53c7b17
9 changed files with 872 additions and 70 deletions

View File

@@ -4,6 +4,7 @@
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Http\Requests\WorkOrder\MaterialInputForItemRequest;
use App\Http\Requests\WorkOrder\StoreItemInspectionRequest;
use App\Http\Requests\WorkOrder\WorkOrderAssignRequest;
use App\Http\Requests\WorkOrder\WorkOrderIssueRequest;
@@ -288,4 +289,56 @@ public function createWorkLog(Request $request, int $id)
return $this->service->createWorkLog($id, $request->all());
}, __('message.work_order.work_log_saved'));
}
// ──────────────────────────────────────────────────────────────
// 개소별 자재 투입
// ──────────────────────────────────────────────────────────────
/**
* 개소별 자재 목록 조회
*/
public function materialsForItem(int $id, int $itemId)
{
return ApiResponse::handle(function () use ($id, $itemId) {
return $this->service->getMaterialsForItem($id, $itemId);
}, __('message.work_order.materials_fetched'));
}
/**
* 개소별 자재 투입 등록
*/
public function registerMaterialInputForItem(MaterialInputForItemRequest $request, int $id, int $itemId)
{
return ApiResponse::handle(function () use ($request, $id, $itemId) {
return $this->service->registerMaterialInputForItem($id, $itemId, $request->validated()['inputs']);
}, __('message.work_order.material_input_registered'));
}
/**
* 개소별 자재 투입 이력 조회
*/
public function materialInputsForItem(int $id, int $itemId)
{
return ApiResponse::handle(function () use ($id, $itemId) {
return $this->service->getMaterialInputsForItem($id, $itemId);
}, __('message.work_order.fetched'));
}
public function deleteMaterialInput(int $id, int $inputId)
{
return ApiResponse::handle(function () use ($id, $inputId) {
$this->service->deleteMaterialInput($id, $inputId);
}, __('message.work_order.deleted'));
}
public function updateMaterialInput(Request $request, int $id, int $inputId)
{
$data = $request->validate([
'qty' => ['required', 'numeric', 'gt:0'],
]);
return ApiResponse::handle(function () use ($id, $inputId, $data) {
return $this->service->updateMaterialInput($id, $inputId, (float) $data['qty']);
}, __('message.work_order.updated'));
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests\WorkOrder;
use Illuminate\Foundation\Http\FormRequest;
class MaterialInputForItemRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'inputs' => 'required|array|min:1',
'inputs.*.stock_lot_id' => 'required|integer',
'inputs.*.qty' => 'required|numeric|gt:0',
];
}
public function messages(): array
{
return [
'inputs.required' => __('error.validation.required', ['attribute' => '투입 목록']),
'inputs.*.stock_lot_id.required' => __('error.validation.required', ['attribute' => 'LOT ID']),
'inputs.*.qty.required' => __('error.validation.required', ['attribute' => '수량']),
'inputs.*.qty.gt' => __('error.validation.gt', ['attribute' => '수량', 'value' => 0]),
];
}
}