feat: [생산관리] 중간검사 데이터 저장/조회 API 구현

- POST /work-orders/{id}/items/{itemId}/inspection: 품목별 검사 데이터 저장
- GET /work-orders/{id}/inspection-data: 전체 품목 검사 데이터 조회
- GET /work-orders/{id}/inspection-report: 검사 성적서용 데이터 조회
- WorkOrderItem 모델에 getInspectionData/setInspectionData 헬퍼 추가
- StoreItemInspectionRequest FormRequest 생성
- work_order_items.options['inspection_data']에 검사 결과 저장

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-09 09:00:57 +09:00
parent 78851ec04a
commit ee6794be1a
6 changed files with 253 additions and 0 deletions

View File

@@ -1519,4 +1519,151 @@ public function getMaterialInputHistory(int $workOrderId): array
];
})->toArray();
}
// ──────────────────────────────────────────────────────────────
// 중간검사 관련
// ──────────────────────────────────────────────────────────────
/**
* 품목별 중간검사 데이터 저장
*/
public function storeItemInspection(int $workOrderId, int $itemId, array $data): array
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
$workOrder = WorkOrder::where('tenant_id', $tenantId)->find($workOrderId);
if (! $workOrder) {
throw new NotFoundHttpException(__('error.not_found'));
}
$item = $workOrder->items()->find($itemId);
if (! $item) {
throw new NotFoundHttpException(__('error.not_found'));
}
$beforeData = $item->getInspectionData();
$inspectionData = $data['inspection_data'];
$inspectionData['process_type'] = $data['process_type'];
$inspectionData['inspected_at'] = now()->toDateTimeString();
$inspectionData['inspected_by'] = $userId;
$item->setInspectionData($inspectionData);
$item->save();
// 감사 로그
$this->auditLogger->log(
$tenantId,
self::AUDIT_TARGET,
$workOrderId,
'item_inspection_saved',
['item_id' => $itemId, 'inspection_data' => $beforeData],
['item_id' => $itemId, 'inspection_data' => $inspectionData]
);
return [
'item_id' => $item->id,
'inspection_data' => $item->getInspectionData(),
];
}
/**
* 작업지시의 전체 품목 검사 데이터 조회
*/
public function getInspectionData(int $workOrderId, array $params = []): array
{
$tenantId = $this->tenantId();
$workOrder = WorkOrder::where('tenant_id', $tenantId)->find($workOrderId);
if (! $workOrder) {
throw new NotFoundHttpException(__('error.not_found'));
}
$query = $workOrder->items()->ordered();
// 공정 유형 필터
if (! empty($params['process_type'])) {
$query->where('options->inspection_data->process_type', $params['process_type']);
}
$items = $query->get();
$inspectionMap = [];
foreach ($items as $item) {
$inspectionData = $item->getInspectionData();
if ($inspectionData) {
$inspectionMap[$item->id] = [
'item_id' => $item->id,
'item_name' => $item->item_name,
'specification' => $item->specification,
'quantity' => $item->quantity,
'sort_order' => $item->sort_order,
'options' => $item->options,
'inspection_data' => $inspectionData,
];
}
}
return [
'work_order_id' => $workOrderId,
'items' => array_values($inspectionMap),
'total' => count($inspectionMap),
];
}
/**
* 작업지시 검사 성적서용 데이터 조회 (전체 품목 + 검사 데이터 + 주문 정보)
*/
public function getInspectionReport(int $workOrderId): array
{
$tenantId = $this->tenantId();
$workOrder = WorkOrder::where('tenant_id', $tenantId)
->with(['order', 'items' => function ($q) {
$q->ordered();
}])
->find($workOrderId);
if (! $workOrder) {
throw new NotFoundHttpException(__('error.not_found'));
}
$items = $workOrder->items->map(function ($item) {
return [
'id' => $item->id,
'item_name' => $item->item_name,
'specification' => $item->specification,
'quantity' => $item->quantity,
'sort_order' => $item->sort_order,
'status' => $item->status,
'options' => $item->options,
'inspection_data' => $item->getInspectionData(),
];
});
return [
'work_order' => [
'id' => $workOrder->id,
'order_no' => $workOrder->order_no,
'status' => $workOrder->status,
'planned_date' => $workOrder->planned_date,
'due_date' => $workOrder->due_date,
],
'order' => $workOrder->order ? [
'id' => $workOrder->order->id,
'order_no' => $workOrder->order->order_no,
'client_name' => $workOrder->order->client_name ?? null,
'site_name' => $workOrder->order->site_name ?? null,
'order_date' => $workOrder->order->order_date ?? null,
] : null,
'items' => $items,
'summary' => [
'total_items' => $items->count(),
'inspected_items' => $items->filter(fn ($i) => $i['inspection_data'] !== null)->count(),
'passed_items' => $items->filter(fn ($i) => ($i['inspection_data']['judgment'] ?? null) === 'pass')->count(),
'failed_items' => $items->filter(fn ($i) => ($i['inspection_data']['judgment'] ?? null) === 'fail')->count(),
],
];
}
}