feat: [material] 부적합관리 결재 연동 구현
- Migration: approval_id FK 추가
- Model: approval() BelongsTo 관계
- Service: submitForApproval() 결재상신 (결재문서+결재선 생성)
- ApprovalService: 승인→CLOSED, 반려/회수→approval_id 해제
- Controller: POST /{id}/submit-approval 엔드포인트
- Route: submit-approval 라우트 등록
This commit is contained in:
@@ -4,6 +4,9 @@
|
||||
|
||||
use App\Models\Materials\NonconformingReport;
|
||||
use App\Models\Materials\NonconformingReportItem;
|
||||
use App\Models\Tenants\Approval;
|
||||
use App\Models\Tenants\ApprovalLine;
|
||||
use App\Models\Tenants\ApprovalStep;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class NonconformingReportService extends Service
|
||||
@@ -68,6 +71,9 @@ public function show(int $id): NonconformingReport
|
||||
'actionManager:id,name',
|
||||
'relatedEmployee:id,name',
|
||||
'files',
|
||||
'approval:id,document_number,status,completed_at',
|
||||
'approval.steps:id,approval_id,step_order,step_type,approver_id,status,comment,acted_at',
|
||||
'approval.steps.approver:id,name',
|
||||
])
|
||||
->findOrFail($id);
|
||||
}
|
||||
@@ -235,6 +241,80 @@ public function stats(array $params): array
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 결재상신 (RESOLVED 상태에서만 가능)
|
||||
*/
|
||||
public function submitForApproval(int $id, array $data): NonconformingReport
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
return DB::transaction(function () use ($id, $data, $tenantId, $userId) {
|
||||
$report = NonconformingReport::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->findOrFail($id);
|
||||
|
||||
if ($report->status !== NonconformingReport::STATUS_RESOLVED) {
|
||||
abort(422, __('error.nonconforming.must_be_resolved_for_approval'));
|
||||
}
|
||||
|
||||
if ($report->approval_id) {
|
||||
abort(422, __('error.nonconforming.approval_already_exists'));
|
||||
}
|
||||
|
||||
$steps = $data['steps'] ?? [];
|
||||
if (empty($steps)) {
|
||||
abort(422, __('error.approval.steps_required'));
|
||||
}
|
||||
|
||||
// 결재 문서 생성
|
||||
$approval = Approval::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'document_number' => $this->generateApprovalNumber($tenantId),
|
||||
'form_id' => $data['form_id'] ?? null,
|
||||
'title' => $data['title'] ?? "부적합 처리 결재 - {$report->nc_number}",
|
||||
'content' => [
|
||||
'nc_number' => $report->nc_number,
|
||||
'nc_type' => $report->nc_type,
|
||||
'site_name' => $report->site_name,
|
||||
'defect_description' => $report->defect_description,
|
||||
'cause_analysis' => $report->cause_analysis,
|
||||
'corrective_action' => $report->corrective_action,
|
||||
'total_cost' => $report->total_cost,
|
||||
],
|
||||
'status' => Approval::STATUS_PENDING,
|
||||
'drafter_id' => $userId,
|
||||
'department_id' => $report->department_id,
|
||||
'drafted_at' => now(),
|
||||
'current_step' => 1,
|
||||
'linkable_type' => NonconformingReport::class,
|
||||
'linkable_id' => $report->id,
|
||||
'created_by' => $userId,
|
||||
'updated_by' => $userId,
|
||||
]);
|
||||
|
||||
// 결재선 단계 생성
|
||||
foreach ($steps as $index => $step) {
|
||||
ApprovalStep::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'approval_id' => $approval->id,
|
||||
'step_order' => $index + 1,
|
||||
'step_type' => $step['step_type'] ?? ApprovalLine::STEP_TYPE_APPROVAL,
|
||||
'approver_id' => $step['approver_id'],
|
||||
'status' => ApprovalStep::STATUS_PENDING,
|
||||
]);
|
||||
}
|
||||
|
||||
// 보고서에 결재 연결
|
||||
$report->update([
|
||||
'approval_id' => $approval->id,
|
||||
'updated_by' => $userId,
|
||||
]);
|
||||
|
||||
return $this->show($report->id);
|
||||
});
|
||||
}
|
||||
|
||||
// ── private ──
|
||||
|
||||
/**
|
||||
@@ -298,6 +378,26 @@ private function sumItemAmounts(array $items): int
|
||||
return $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* 결재 문서번호 생성
|
||||
*/
|
||||
private function generateApprovalNumber(int $tenantId): string
|
||||
{
|
||||
$prefix = 'AP';
|
||||
$date = now()->format('Ymd');
|
||||
$pattern = "{$prefix}-{$date}-";
|
||||
|
||||
$lastNumber = Approval::withTrashed()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('document_number', 'like', "{$pattern}%")
|
||||
->orderByDesc('document_number')
|
||||
->value('document_number');
|
||||
|
||||
$seq = $lastNumber ? ((int) substr($lastNumber, -4) + 1) : 1;
|
||||
|
||||
return sprintf('%s-%s-%04d', $prefix, $date, $seq);
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태 전이 검증
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user