findProcess($processId); return $process->steps() ->with('documentTemplate:id,name,category') ->orderBy('sort_order') ->get(); } /** * 공정 단계 상세 조회 */ public function show(int $processId, int $stepId) { $this->findProcess($processId); $step = ProcessStep::where('process_id', $processId) ->with('documentTemplate:id,name,category') ->find($stepId); if (! $step) { throw new NotFoundHttpException(__('error.not_found')); } return $step; } /** * 공정 단계 생성 */ public function store(int $processId, array $data) { $process = $this->findProcess($processId); $data['process_id'] = $process->id; $data['step_code'] = $this->generateStepCode($process->id); $data['sort_order'] = ($process->steps()->max('sort_order') ?? 0) + 1; $data['is_active'] = $data['is_active'] ?? true; return ProcessStep::create($data); } /** * 공정 단계 수정 */ public function update(int $processId, int $stepId, array $data) { $this->findProcess($processId); $step = ProcessStep::where('process_id', $processId)->find($stepId); if (! $step) { throw new NotFoundHttpException(__('error.not_found')); } $step->update($data); return $step->fresh(); } /** * 공정 단계 삭제 */ public function destroy(int $processId, int $stepId) { $this->findProcess($processId); $step = ProcessStep::where('process_id', $processId)->find($stepId); if (! $step) { throw new NotFoundHttpException(__('error.not_found')); } $step->delete(); return true; } /** * 공정 단계 순서 변경 */ public function reorder(int $processId, array $items) { $this->findProcess($processId); return DB::transaction(function () use ($processId, $items) { foreach ($items as $item) { ProcessStep::where('process_id', $processId) ->where('id', $item['id']) ->update(['sort_order' => $item['sort_order']]); } return ProcessStep::where('process_id', $processId) ->orderBy('sort_order') ->get(); }); } /** * 부모 공정 조회 (tenant scope) */ private function findProcess(int $processId): Process { $tenantId = $this->tenantId(); $process = Process::where('tenant_id', $tenantId)->find($processId); if (! $process) { throw new NotFoundHttpException(__('error.not_found')); } return $process; } /** * 단계코드 자동 생성 (STP-001, STP-002, ...) */ private function generateStepCode(int $processId): string { $lastStep = ProcessStep::where('process_id', $processId) ->orderByRaw('CAST(SUBSTRING(step_code, 5) AS UNSIGNED) DESC') ->first(); if ($lastStep && preg_match('/^STP-(\d+)$/', $lastStep->step_code, $matches)) { $nextNum = (int) $matches[1] + 1; } else { $nextNum = 1; } return sprintf('STP-%03d', $nextNum); } }