feat: 작업일지/중간검사 설정을 ProcessStep → Process 레벨로 이동
- Process 모델에 document_template_id, needs_work_log, work_log_template_id 추가 - ProcessStep에서 해당 필드 제거 - WorkOrderService의 검사 관련 3개 메서드(getInspectionTemplate, resolveInspectionDocument, createInspectionDocument) 공정 레벨 참조로 변경 - ProcessService eager loading에 documentTemplate, workLogTemplateRelation 추가 - FormRequest 검증 규칙 이동 (ProcessStep → Process) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,9 @@ public function rules(): array
|
|||||||
'process_type' => ['required', 'string', 'in:생산,검사,포장,조립'],
|
'process_type' => ['required', 'string', 'in:생산,검사,포장,조립'],
|
||||||
'department' => ['nullable', 'string', 'max:100'],
|
'department' => ['nullable', 'string', 'max:100'],
|
||||||
'work_log_template' => ['nullable', 'string', 'max:100'],
|
'work_log_template' => ['nullable', 'string', 'max:100'],
|
||||||
|
'document_template_id' => ['nullable', 'integer', 'exists:document_templates,id'],
|
||||||
|
'needs_work_log' => ['nullable', 'boolean'],
|
||||||
|
'work_log_template_id' => ['nullable', 'integer', 'exists:document_templates,id'],
|
||||||
'required_workers' => ['nullable', 'integer', 'min:1'],
|
'required_workers' => ['nullable', 'integer', 'min:1'],
|
||||||
'equipment_info' => ['nullable', 'string', 'max:255'],
|
'equipment_info' => ['nullable', 'string', 'max:255'],
|
||||||
'work_steps' => ['nullable'],
|
'work_steps' => ['nullable'],
|
||||||
@@ -48,6 +51,9 @@ public function attributes(): array
|
|||||||
'process_type' => '공정구분',
|
'process_type' => '공정구분',
|
||||||
'department' => '담당부서',
|
'department' => '담당부서',
|
||||||
'work_log_template' => '작업일지 양식',
|
'work_log_template' => '작업일지 양식',
|
||||||
|
'document_template_id' => '중간검사 양식',
|
||||||
|
'needs_work_log' => '작업일지 여부',
|
||||||
|
'work_log_template_id' => '작업일지 양식 ID',
|
||||||
'required_workers' => '필요인원',
|
'required_workers' => '필요인원',
|
||||||
'equipment_info' => '설비정보',
|
'equipment_info' => '설비정보',
|
||||||
'work_steps' => '작업단계',
|
'work_steps' => '작업단계',
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ public function rules(): array
|
|||||||
'process_type' => ['sometimes', 'required', 'string', 'in:생산,검사,포장,조립'],
|
'process_type' => ['sometimes', 'required', 'string', 'in:생산,검사,포장,조립'],
|
||||||
'department' => ['nullable', 'string', 'max:100'],
|
'department' => ['nullable', 'string', 'max:100'],
|
||||||
'work_log_template' => ['nullable', 'string', 'max:100'],
|
'work_log_template' => ['nullable', 'string', 'max:100'],
|
||||||
|
'document_template_id' => ['nullable', 'integer', 'exists:document_templates,id'],
|
||||||
|
'needs_work_log' => ['nullable', 'boolean'],
|
||||||
|
'work_log_template_id' => ['nullable', 'integer', 'exists:document_templates,id'],
|
||||||
'required_workers' => ['nullable', 'integer', 'min:1'],
|
'required_workers' => ['nullable', 'integer', 'min:1'],
|
||||||
'equipment_info' => ['nullable', 'string', 'max:255'],
|
'equipment_info' => ['nullable', 'string', 'max:255'],
|
||||||
'work_steps' => ['nullable'],
|
'work_steps' => ['nullable'],
|
||||||
@@ -48,6 +51,9 @@ public function attributes(): array
|
|||||||
'process_type' => '공정구분',
|
'process_type' => '공정구분',
|
||||||
'department' => '담당부서',
|
'department' => '담당부서',
|
||||||
'work_log_template' => '작업일지 양식',
|
'work_log_template' => '작업일지 양식',
|
||||||
|
'document_template_id' => '중간검사 양식',
|
||||||
|
'needs_work_log' => '작업일지 여부',
|
||||||
|
'work_log_template_id' => '작업일지 양식 ID',
|
||||||
'required_workers' => '필요인원',
|
'required_workers' => '필요인원',
|
||||||
'equipment_info' => '설비정보',
|
'equipment_info' => '설비정보',
|
||||||
'work_steps' => '작업단계',
|
'work_steps' => '작업단계',
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ public function rules(): array
|
|||||||
'is_required' => ['nullable', 'boolean'],
|
'is_required' => ['nullable', 'boolean'],
|
||||||
'needs_approval' => ['nullable', 'boolean'],
|
'needs_approval' => ['nullable', 'boolean'],
|
||||||
'needs_inspection' => ['nullable', 'boolean'],
|
'needs_inspection' => ['nullable', 'boolean'],
|
||||||
'document_template_id' => ['nullable', 'integer', 'exists:document_templates,id'],
|
|
||||||
'is_active' => ['nullable', 'boolean'],
|
'is_active' => ['nullable', 'boolean'],
|
||||||
'connection_type' => ['nullable', 'string', 'max:20'],
|
'connection_type' => ['nullable', 'string', 'max:20'],
|
||||||
'connection_target' => ['nullable', 'string', 'max:255'],
|
'connection_target' => ['nullable', 'string', 'max:255'],
|
||||||
@@ -33,7 +32,6 @@ public function attributes(): array
|
|||||||
'is_required' => '필수여부',
|
'is_required' => '필수여부',
|
||||||
'needs_approval' => '승인필요여부',
|
'needs_approval' => '승인필요여부',
|
||||||
'needs_inspection' => '검사필요여부',
|
'needs_inspection' => '검사필요여부',
|
||||||
'document_template_id' => '문서양식',
|
|
||||||
'is_active' => '사용여부',
|
'is_active' => '사용여부',
|
||||||
'connection_type' => '연결유형',
|
'connection_type' => '연결유형',
|
||||||
'connection_target' => '연결대상',
|
'connection_target' => '연결대상',
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ public function rules(): array
|
|||||||
'is_required' => ['nullable', 'boolean'],
|
'is_required' => ['nullable', 'boolean'],
|
||||||
'needs_approval' => ['nullable', 'boolean'],
|
'needs_approval' => ['nullable', 'boolean'],
|
||||||
'needs_inspection' => ['nullable', 'boolean'],
|
'needs_inspection' => ['nullable', 'boolean'],
|
||||||
'document_template_id' => ['nullable', 'integer', 'exists:document_templates,id'],
|
|
||||||
'is_active' => ['nullable', 'boolean'],
|
'is_active' => ['nullable', 'boolean'],
|
||||||
'connection_type' => ['nullable', 'string', 'max:20'],
|
'connection_type' => ['nullable', 'string', 'max:20'],
|
||||||
'connection_target' => ['nullable', 'string', 'max:255'],
|
'connection_target' => ['nullable', 'string', 'max:255'],
|
||||||
@@ -33,7 +32,6 @@ public function attributes(): array
|
|||||||
'is_required' => '필수여부',
|
'is_required' => '필수여부',
|
||||||
'needs_approval' => '승인필요여부',
|
'needs_approval' => '승인필요여부',
|
||||||
'needs_inspection' => '검사필요여부',
|
'needs_inspection' => '검사필요여부',
|
||||||
'document_template_id' => '문서양식',
|
|
||||||
'is_active' => '사용여부',
|
'is_active' => '사용여부',
|
||||||
'connection_type' => '연결유형',
|
'connection_type' => '연결유형',
|
||||||
'connection_target' => '연결대상',
|
'connection_target' => '연결대상',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
use App\Traits\ModelTrait;
|
use App\Traits\ModelTrait;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
@@ -26,6 +27,9 @@ class Process extends Model
|
|||||||
'process_type',
|
'process_type',
|
||||||
'department',
|
'department',
|
||||||
'work_log_template',
|
'work_log_template',
|
||||||
|
'document_template_id',
|
||||||
|
'needs_work_log',
|
||||||
|
'work_log_template_id',
|
||||||
'required_workers',
|
'required_workers',
|
||||||
'equipment_info',
|
'equipment_info',
|
||||||
'work_steps',
|
'work_steps',
|
||||||
@@ -39,9 +43,26 @@ class Process extends Model
|
|||||||
protected $casts = [
|
protected $casts = [
|
||||||
'work_steps' => 'array',
|
'work_steps' => 'array',
|
||||||
'is_active' => 'boolean',
|
'is_active' => 'boolean',
|
||||||
|
'needs_work_log' => 'boolean',
|
||||||
'required_workers' => 'integer',
|
'required_workers' => 'integer',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 중간검사 양식
|
||||||
|
*/
|
||||||
|
public function documentTemplate(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Documents\DocumentTemplate::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 작업일지 양식 (관계명: work_log_template 컬럼과 충돌 방지)
|
||||||
|
*/
|
||||||
|
public function workLogTemplateRelation(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Documents\DocumentTemplate::class, 'work_log_template_id');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 공정 자동 분류 규칙 (패턴 규칙)
|
* 공정 자동 분류 규칙 (패턴 규칙)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Models\Documents\DocumentTemplate;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
@@ -18,7 +17,6 @@ class ProcessStep extends Model
|
|||||||
'is_required',
|
'is_required',
|
||||||
'needs_approval',
|
'needs_approval',
|
||||||
'needs_inspection',
|
'needs_inspection',
|
||||||
'document_template_id',
|
|
||||||
'is_active',
|
'is_active',
|
||||||
'sort_order',
|
'sort_order',
|
||||||
'connection_type',
|
'connection_type',
|
||||||
@@ -41,12 +39,4 @@ public function process(): BelongsTo
|
|||||||
{
|
{
|
||||||
return $this->belongsTo(Process::class);
|
return $this->belongsTo(Process::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 문서 양식 (검사 시 사용할 템플릿)
|
|
||||||
*/
|
|
||||||
public function documentTemplate(): BelongsTo
|
|
||||||
{
|
|
||||||
return $this->belongsTo(DocumentTemplate::class);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public function index(array $params)
|
|||||||
|
|
||||||
$query = Process::query()
|
$query = Process::query()
|
||||||
->where('tenant_id', $tenantId)
|
->where('tenant_id', $tenantId)
|
||||||
->with(['classificationRules', 'processItems.item:id,code,name', 'steps']);
|
->with(['classificationRules', 'processItems.item:id,code,name', 'steps', 'documentTemplate:id,name,category', 'workLogTemplateRelation:id,name,category']);
|
||||||
|
|
||||||
// 검색어
|
// 검색어
|
||||||
if ($q !== '') {
|
if ($q !== '') {
|
||||||
@@ -62,7 +62,7 @@ public function show(int $id)
|
|||||||
$tenantId = $this->tenantId();
|
$tenantId = $this->tenantId();
|
||||||
|
|
||||||
$process = Process::where('tenant_id', $tenantId)
|
$process = Process::where('tenant_id', $tenantId)
|
||||||
->with(['classificationRules', 'processItems.item:id,code,name', 'steps'])
|
->with(['classificationRules', 'processItems.item:id,code,name', 'steps', 'documentTemplate:id,name,category', 'workLogTemplateRelation:id,name,category'])
|
||||||
->find($id);
|
->find($id);
|
||||||
|
|
||||||
if (! $process) {
|
if (! $process) {
|
||||||
@@ -104,7 +104,7 @@ public function store(array $data)
|
|||||||
// 개별 품목 연결
|
// 개별 품목 연결
|
||||||
$this->syncProcessItems($process, $itemIds);
|
$this->syncProcessItems($process, $itemIds);
|
||||||
|
|
||||||
return $process->load(['classificationRules', 'processItems.item:id,code,name', 'steps']);
|
return $process->load(['classificationRules', 'processItems.item:id,code,name', 'steps', 'documentTemplate:id,name,category', 'workLogTemplateRelation:id,name,category']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ public function update(int $id, array $data)
|
|||||||
$this->syncProcessItems($process, $itemIds);
|
$this->syncProcessItems($process, $itemIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $process->fresh(['classificationRules', 'processItems.item:id,code,name', 'steps']);
|
return $process->fresh(['classificationRules', 'processItems.item:id,code,name', 'steps', 'documentTemplate:id,name,category', 'workLogTemplateRelation:id,name,category']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@ public function toggleActive(int $id)
|
|||||||
'updated_by' => $userId,
|
'updated_by' => $userId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $process->fresh(['classificationRules', 'processItems.item:id,code,name', 'steps']);
|
return $process->fresh(['classificationRules', 'processItems.item:id,code,name', 'steps', 'documentTemplate:id,name,category', 'workLogTemplateRelation:id,name,category']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public function index(int $processId)
|
|||||||
$process = $this->findProcess($processId);
|
$process = $this->findProcess($processId);
|
||||||
|
|
||||||
return $process->steps()
|
return $process->steps()
|
||||||
->with('documentTemplate:id,name,category')
|
->select('*')
|
||||||
->orderBy('sort_order')
|
->orderBy('sort_order')
|
||||||
->get();
|
->get();
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ public function show(int $processId, int $stepId)
|
|||||||
$this->findProcess($processId);
|
$this->findProcess($processId);
|
||||||
|
|
||||||
$step = ProcessStep::where('process_id', $processId)
|
$step = ProcessStep::where('process_id', $processId)
|
||||||
->with('documentTemplate:id,name,category')
|
->select('*')
|
||||||
->find($stepId);
|
->find($stepId);
|
||||||
if (! $step) {
|
if (! $step) {
|
||||||
throw new NotFoundHttpException(__('error.not_found'));
|
throw new NotFoundHttpException(__('error.not_found'));
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public function index(array $params)
|
|||||||
'salesOrder.client:id,name',
|
'salesOrder.client:id,name',
|
||||||
'process:id,process_name,process_code,department',
|
'process:id,process_name,process_code,department',
|
||||||
'items:id,work_order_id,item_id,item_name,specification,quantity,unit,status,options,sort_order,source_order_item_id',
|
'items:id,work_order_id,item_id,item_name,specification,quantity,unit,status,options,sort_order,source_order_item_id',
|
||||||
'items.sourceOrderItem:id,order_node_id',
|
'items.sourceOrderItem:id,order_node_id,floor_code,symbol_code',
|
||||||
'items.sourceOrderItem.node:id,name,code',
|
'items.sourceOrderItem.node:id,name,code',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -210,7 +210,7 @@ public function show(int $id)
|
|||||||
'salesOrder.writer:id,name',
|
'salesOrder.writer:id,name',
|
||||||
'process:id,process_name,process_code,work_steps,department',
|
'process:id,process_name,process_code,work_steps,department',
|
||||||
'process.steps' => fn ($q) => $q->where('is_active', true)->orderBy('sort_order'),
|
'process.steps' => fn ($q) => $q->where('is_active', true)->orderBy('sort_order'),
|
||||||
'items.sourceOrderItem:id,order_node_id',
|
'items.sourceOrderItem:id,order_node_id,floor_code,symbol_code',
|
||||||
'items.sourceOrderItem.node:id,name,code',
|
'items.sourceOrderItem.node:id,name,code',
|
||||||
'bendingDetail',
|
'bendingDetail',
|
||||||
'issues' => fn ($q) => $q->orderByDesc('created_at'),
|
'issues' => fn ($q) => $q->orderByDesc('created_at'),
|
||||||
@@ -257,9 +257,22 @@ public function store(array $data)
|
|||||||
|
|
||||||
// 품목 저장: 직접 전달된 품목이 없고 수주 ID가 있으면 수주에서 복사
|
// 품목 저장: 직접 전달된 품목이 없고 수주 ID가 있으면 수주에서 복사
|
||||||
if (empty($items) && $salesOrderId) {
|
if (empty($items) && $salesOrderId) {
|
||||||
$salesOrder = \App\Models\Orders\Order::with('items')->find($salesOrderId);
|
$salesOrder = \App\Models\Orders\Order::with('items.node')->find($salesOrderId);
|
||||||
if ($salesOrder && $salesOrder->items->isNotEmpty()) {
|
if ($salesOrder && $salesOrder->items->isNotEmpty()) {
|
||||||
foreach ($salesOrder->items as $index => $orderItem) {
|
foreach ($salesOrder->items as $index => $orderItem) {
|
||||||
|
// 수주 품목 + 노드에서 options 조합
|
||||||
|
$nodeOptions = $orderItem->node?->options ?? [];
|
||||||
|
$options = array_filter([
|
||||||
|
'floor' => $orderItem->floor_code,
|
||||||
|
'code' => $orderItem->symbol_code,
|
||||||
|
'width' => $nodeOptions['width'] ?? $nodeOptions['open_width'] ?? null,
|
||||||
|
'height' => $nodeOptions['height'] ?? $nodeOptions['open_height'] ?? null,
|
||||||
|
'cutting_info' => $nodeOptions['cutting_info'] ?? null,
|
||||||
|
'slat_info' => $nodeOptions['slat_info'] ?? null,
|
||||||
|
'bending_info' => $nodeOptions['bending_info'] ?? null,
|
||||||
|
'wip_info' => $nodeOptions['wip_info'] ?? null,
|
||||||
|
], fn ($v) => $v !== null);
|
||||||
|
|
||||||
$workOrder->items()->create([
|
$workOrder->items()->create([
|
||||||
'tenant_id' => $tenantId,
|
'tenant_id' => $tenantId,
|
||||||
'source_order_item_id' => $orderItem->id, // 원본 수주 품목 추적용
|
'source_order_item_id' => $orderItem->id, // 원본 수주 품목 추적용
|
||||||
@@ -269,6 +282,7 @@ public function store(array $data)
|
|||||||
'quantity' => $orderItem->quantity,
|
'quantity' => $orderItem->quantity,
|
||||||
'unit' => $orderItem->unit,
|
'unit' => $orderItem->unit,
|
||||||
'sort_order' => $index,
|
'sort_order' => $index,
|
||||||
|
'options' => ! empty($options) ? $options : null,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1639,11 +1653,7 @@ public function getInspectionTemplate(int $workOrderId): array
|
|||||||
|
|
||||||
$workOrder = WorkOrder::where('tenant_id', $tenantId)
|
$workOrder = WorkOrder::where('tenant_id', $tenantId)
|
||||||
->with([
|
->with([
|
||||||
'process.steps' => fn ($q) => $q->where('is_active', true)
|
'process.documentTemplate' => fn ($q) => $q->with([
|
||||||
->where('needs_inspection', true)
|
|
||||||
->whereNotNull('document_template_id')
|
|
||||||
->orderBy('sort_order'),
|
|
||||||
'process.steps.documentTemplate' => fn ($q) => $q->with([
|
|
||||||
'approvalLines',
|
'approvalLines',
|
||||||
'basicFields',
|
'basicFields',
|
||||||
'sections.items',
|
'sections.items',
|
||||||
@@ -1659,8 +1669,10 @@ public function getInspectionTemplate(int $workOrderId): array
|
|||||||
throw new NotFoundHttpException(__('error.not_found'));
|
throw new NotFoundHttpException(__('error.not_found'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$inspectionSteps = $workOrder->process?->steps ?? collect();
|
$process = $workOrder->process;
|
||||||
if ($inspectionSteps->isEmpty()) {
|
$docTemplate = $process?->documentTemplate;
|
||||||
|
|
||||||
|
if (! $docTemplate) {
|
||||||
return [
|
return [
|
||||||
'work_order_id' => $workOrderId,
|
'work_order_id' => $workOrderId,
|
||||||
'has_template' => false,
|
'has_template' => false,
|
||||||
@@ -1671,28 +1683,17 @@ public function getInspectionTemplate(int $workOrderId): array
|
|||||||
}
|
}
|
||||||
|
|
||||||
$documentService = app(DocumentService::class);
|
$documentService = app(DocumentService::class);
|
||||||
$templates = [];
|
$formattedTemplate = $documentService->formatTemplateForReact($docTemplate);
|
||||||
|
|
||||||
foreach ($inspectionSteps as $step) {
|
|
||||||
if (! $step->documentTemplate) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$templates[] = [
|
|
||||||
'step_id' => $step->id,
|
|
||||||
'step_name' => $step->step_name,
|
|
||||||
'step_code' => $step->step_code,
|
|
||||||
'sort_order' => $step->sort_order,
|
|
||||||
'template' => $documentService->formatTemplateForReact($step->documentTemplate),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'work_order_id' => $workOrderId,
|
'work_order_id' => $workOrderId,
|
||||||
'has_template' => ! empty($templates),
|
'has_template' => true,
|
||||||
'templates' => $templates,
|
'templates' => [[
|
||||||
// 하위호환: 첫 번째 템플릿
|
'template_id' => $docTemplate->id,
|
||||||
'template' => ! empty($templates) ? $templates[0]['template'] : null,
|
'template_name' => $docTemplate->name,
|
||||||
|
'template' => $formattedTemplate,
|
||||||
|
]],
|
||||||
|
'template' => $formattedTemplate,
|
||||||
'work_order_info' => $this->buildWorkOrderInfo($workOrder),
|
'work_order_info' => $this->buildWorkOrderInfo($workOrder),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -1709,11 +1710,7 @@ public function resolveInspectionDocument(int $workOrderId, array $params = []):
|
|||||||
|
|
||||||
$workOrder = WorkOrder::where('tenant_id', $tenantId)
|
$workOrder = WorkOrder::where('tenant_id', $tenantId)
|
||||||
->with([
|
->with([
|
||||||
'process.steps' => fn ($q) => $q->where('is_active', true)
|
'process.documentTemplate' => fn ($q) => $q->with([
|
||||||
->where('needs_inspection', true)
|
|
||||||
->whereNotNull('document_template_id')
|
|
||||||
->orderBy('sort_order'),
|
|
||||||
'process.steps.documentTemplate' => fn ($q) => $q->with([
|
|
||||||
'approvalLines',
|
'approvalLines',
|
||||||
'basicFields',
|
'basicFields',
|
||||||
'sections.items',
|
'sections.items',
|
||||||
@@ -1729,25 +1726,21 @@ public function resolveInspectionDocument(int $workOrderId, array $params = []):
|
|||||||
throw new NotFoundHttpException(__('error.not_found'));
|
throw new NotFoundHttpException(__('error.not_found'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// step_id가 지정되면 해당 단계 사용, 아니면 첫 번째 검사 단계
|
$process = $workOrder->process;
|
||||||
$inspectionSteps = $workOrder->process?->steps ?? collect();
|
$templateId = $process?->document_template_id;
|
||||||
$stepId = $params['step_id'] ?? null;
|
$docTemplate = $process?->documentTemplate;
|
||||||
|
|
||||||
$inspectionStep = $stepId
|
if (! $templateId || ! $docTemplate) {
|
||||||
? $inspectionSteps->firstWhere('id', (int) $stepId)
|
|
||||||
: $inspectionSteps->first();
|
|
||||||
|
|
||||||
if (! $inspectionStep || ! $inspectionStep->document_template_id) {
|
|
||||||
throw new BadRequestHttpException(__('error.work_order.no_inspection_template'));
|
throw new BadRequestHttpException(__('error.work_order.no_inspection_template'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$documentService = app(DocumentService::class);
|
$documentService = app(DocumentService::class);
|
||||||
$formattedTemplate = $documentService->formatTemplateForReact($inspectionStep->documentTemplate);
|
$formattedTemplate = $documentService->formatTemplateForReact($docTemplate);
|
||||||
|
|
||||||
// 기존 문서 조회 (work_order + template, 수정 가능한 상태)
|
// 기존 문서 조회 (work_order + template, 수정 가능한 상태)
|
||||||
$existingDocument = Document::query()
|
$existingDocument = Document::query()
|
||||||
->where('tenant_id', $tenantId)
|
->where('tenant_id', $tenantId)
|
||||||
->where('template_id', $inspectionStep->document_template_id)
|
->where('template_id', $templateId)
|
||||||
->where('linkable_type', 'work_order')
|
->where('linkable_type', 'work_order')
|
||||||
->where('linkable_id', $workOrderId)
|
->where('linkable_id', $workOrderId)
|
||||||
->whereIn('status', [Document::STATUS_DRAFT, Document::STATUS_REJECTED])
|
->whereIn('status', [Document::STATUS_DRAFT, Document::STATUS_REJECTED])
|
||||||
@@ -1757,8 +1750,7 @@ public function resolveInspectionDocument(int $workOrderId, array $params = []):
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
'work_order_id' => $workOrderId,
|
'work_order_id' => $workOrderId,
|
||||||
'step_id' => $inspectionStep->id,
|
'template_id' => $templateId,
|
||||||
'step_name' => $inspectionStep->step_name,
|
|
||||||
'template' => $formattedTemplate,
|
'template' => $formattedTemplate,
|
||||||
'existing_document' => $existingDocument,
|
'existing_document' => $existingDocument,
|
||||||
'work_order_info' => $this->buildWorkOrderInfo($workOrder),
|
'work_order_info' => $this->buildWorkOrderInfo($workOrder),
|
||||||
@@ -1768,7 +1760,7 @@ public function resolveInspectionDocument(int $workOrderId, array $params = []):
|
|||||||
/**
|
/**
|
||||||
* 검사 완료 시 Document + DocumentData 생성
|
* 검사 완료 시 Document + DocumentData 생성
|
||||||
*
|
*
|
||||||
* step_id 지정 시 해당 단계의 템플릿 사용, 미지정 시 첫 번째 검사 단계 사용.
|
* 공정(Process) 레벨의 document_template_id를 사용.
|
||||||
* 기존 DRAFT/REJECTED 문서가 있으면 update, 없으면 create.
|
* 기존 DRAFT/REJECTED 문서가 있으면 update, 없으면 create.
|
||||||
*/
|
*/
|
||||||
public function createInspectionDocument(int $workOrderId, array $inspectionData): array
|
public function createInspectionDocument(int $workOrderId, array $inspectionData): array
|
||||||
@@ -1777,27 +1769,18 @@ public function createInspectionDocument(int $workOrderId, array $inspectionData
|
|||||||
$userId = $this->apiUserId();
|
$userId = $this->apiUserId();
|
||||||
|
|
||||||
$workOrder = WorkOrder::where('tenant_id', $tenantId)
|
$workOrder = WorkOrder::where('tenant_id', $tenantId)
|
||||||
->with([
|
->with(['process'])
|
||||||
'process.steps' => fn ($q) => $q->where('is_active', true)
|
|
||||||
->where('needs_inspection', true)
|
|
||||||
->whereNotNull('document_template_id')
|
|
||||||
->orderBy('sort_order'),
|
|
||||||
])
|
|
||||||
->find($workOrderId);
|
->find($workOrderId);
|
||||||
|
|
||||||
if (! $workOrder) {
|
if (! $workOrder) {
|
||||||
throw new NotFoundHttpException(__('error.not_found'));
|
throw new NotFoundHttpException(__('error.not_found'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// step_id가 지정되면 해당 단계 사용, 아니면 첫 번째
|
// 공정 레벨의 중간검사 양식 사용
|
||||||
$inspectionSteps = $workOrder->process?->steps ?? collect();
|
$process = $workOrder->process;
|
||||||
$stepId = $inspectionData['step_id'] ?? null;
|
$templateId = $process?->document_template_id;
|
||||||
|
|
||||||
$inspectionStep = $stepId
|
if (! $templateId) {
|
||||||
? $inspectionSteps->firstWhere('id', (int) $stepId)
|
|
||||||
: $inspectionSteps->first();
|
|
||||||
|
|
||||||
if (! $inspectionStep || ! $inspectionStep->document_template_id) {
|
|
||||||
throw new BadRequestHttpException(__('error.work_order.no_inspection_template'));
|
throw new BadRequestHttpException(__('error.work_order.no_inspection_template'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1806,7 +1789,7 @@ public function createInspectionDocument(int $workOrderId, array $inspectionData
|
|||||||
// 기존 DRAFT/REJECTED 문서가 있으면 update
|
// 기존 DRAFT/REJECTED 문서가 있으면 update
|
||||||
$existingDocument = Document::query()
|
$existingDocument = Document::query()
|
||||||
->where('tenant_id', $tenantId)
|
->where('tenant_id', $tenantId)
|
||||||
->where('template_id', $inspectionStep->document_template_id)
|
->where('template_id', $templateId)
|
||||||
->where('linkable_type', 'work_order')
|
->where('linkable_type', 'work_order')
|
||||||
->where('linkable_id', $workOrderId)
|
->where('linkable_id', $workOrderId)
|
||||||
->whereIn('status', [Document::STATUS_DRAFT, Document::STATUS_REJECTED])
|
->whereIn('status', [Document::STATUS_DRAFT, Document::STATUS_REJECTED])
|
||||||
@@ -1822,7 +1805,7 @@ public function createInspectionDocument(int $workOrderId, array $inspectionData
|
|||||||
$action = 'inspection_document_updated';
|
$action = 'inspection_document_updated';
|
||||||
} else {
|
} else {
|
||||||
$documentData = [
|
$documentData = [
|
||||||
'template_id' => $inspectionStep->document_template_id,
|
'template_id' => $templateId,
|
||||||
'title' => $inspectionData['title'] ?? "중간검사성적서 - {$workOrder->work_order_no}",
|
'title' => $inspectionData['title'] ?? "중간검사성적서 - {$workOrder->work_order_no}",
|
||||||
'linkable_type' => 'work_order',
|
'linkable_type' => 'work_order',
|
||||||
'linkable_id' => $workOrderId,
|
'linkable_id' => $workOrderId,
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 검사양식/작업일지 설정을 ProcessStep → Process 레벨로 이동
|
||||||
|
* - processes: document_template_id, needs_work_log, work_log_template_id 추가
|
||||||
|
* - process_steps: document_template_id 제거
|
||||||
|
*/
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
// 1. Process에 검사양식/작업일지 필드 추가
|
||||||
|
Schema::table('processes', function (Blueprint $table) {
|
||||||
|
$table->foreignId('document_template_id')->nullable()->after('work_log_template')
|
||||||
|
->constrained('document_templates')->nullOnDelete()->comment('중간검사 양식 ID');
|
||||||
|
$table->boolean('needs_work_log')->default(false)->after('document_template_id')->comment('작업일지 여부');
|
||||||
|
$table->foreignId('work_log_template_id')->nullable()->after('needs_work_log')
|
||||||
|
->constrained('document_templates')->nullOnDelete()->comment('작업일지 양식 ID');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. ProcessStep에서 document_template_id 제거
|
||||||
|
Schema::table('process_steps', function (Blueprint $table) {
|
||||||
|
$table->dropForeign(['document_template_id']);
|
||||||
|
$table->dropColumn('document_template_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
// ProcessStep에 document_template_id 복원
|
||||||
|
Schema::table('process_steps', function (Blueprint $table) {
|
||||||
|
$table->foreignId('document_template_id')->nullable()->after('needs_inspection')
|
||||||
|
->constrained('document_templates')->nullOnDelete();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Process에서 필드 제거
|
||||||
|
Schema::table('processes', function (Blueprint $table) {
|
||||||
|
$table->dropForeign(['work_log_template_id']);
|
||||||
|
$table->dropForeign(['document_template_id']);
|
||||||
|
$table->dropColumn(['work_log_template_id', 'needs_work_log', 'document_template_id']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user