From 3fc5f511bc08bf26c933c9affad983efaab98ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Mon, 9 Mar 2026 17:43:15 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[quality]=20=EA=B2=80=EC=82=AC=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=9E=90=EB=8F=99=20=EC=9E=AC=EA=B3=84?= =?UTF-8?q?=EC=82=B0=20+=20=EC=88=98=EC=A3=BC=EC=B2=98=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 개소별 inspection_status를 검사 데이터 내용 기반으로 자동 판정 (15개 판정필드 + 사진 유무 → pending/in_progress/completed) - 문서 status를 개소 상태 집계로 자동 재계산 - inspectLocation, updateLocations 모두 적용 - QualityDocumentLocation에 STATUS_IN_PROGRESS 상수 추가 - transformToFrontend에 client_id 매핑 추가 --- .../Qualitys/QualityDocumentLocation.php | 2 + app/Services/QualityDocumentService.php | 96 +++++++++++++++++-- 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/app/Models/Qualitys/QualityDocumentLocation.php b/app/Models/Qualitys/QualityDocumentLocation.php index 311ed9d..fd362bc 100644 --- a/app/Models/Qualitys/QualityDocumentLocation.php +++ b/app/Models/Qualitys/QualityDocumentLocation.php @@ -12,6 +12,8 @@ class QualityDocumentLocation extends Model const STATUS_PENDING = 'pending'; + const STATUS_IN_PROGRESS = 'in_progress'; + const STATUS_COMPLETED = 'completed'; protected $fillable = [ diff --git a/app/Services/QualityDocumentService.php b/app/Services/QualityDocumentService.php index e3142cd..2391b2f 100644 --- a/app/Services/QualityDocumentService.php +++ b/app/Services/QualityDocumentService.php @@ -257,6 +257,9 @@ public function update(int $id, array $data) $this->updateLocations($doc->id, $locations); } + // 개소 상태 기반 문서 상태 재계산 + $this->recalculateDocumentStatus($doc); + $this->auditLogger->log( $doc->tenant_id, self::AUDIT_TARGET, @@ -476,9 +479,87 @@ private function updateLocations(int $docId, array $locations): void if (! empty($updateData)) { $location->update($updateData); } + + // 검사 데이터 내용 기반 inspection_status 재계산 + $location->refresh(); + $newStatus = $this->determineLocationStatus($location->inspection_data); + + if ($location->inspection_status !== $newStatus) { + $location->update(['inspection_status' => $newStatus]); + } } } + /** + * 개소 상태 기반 문서 상태 재계산 + */ + private function recalculateDocumentStatus(QualityDocument $doc): void + { + $doc->load('locations'); + $total = $doc->locations->count(); + + if ($total === 0) { + $doc->update(['status' => QualityDocument::STATUS_RECEIVED]); + + return; + } + + $completedCount = $doc->locations + ->where('inspection_status', QualityDocumentLocation::STATUS_COMPLETED) + ->count(); + $inProgressCount = $doc->locations + ->where('inspection_status', QualityDocumentLocation::STATUS_IN_PROGRESS) + ->count(); + + if ($completedCount === $total) { + $doc->update(['status' => QualityDocument::STATUS_COMPLETED]); + } elseif ($completedCount > 0 || $inProgressCount > 0) { + $doc->update(['status' => QualityDocument::STATUS_IN_PROGRESS]); + } else { + $doc->update(['status' => QualityDocument::STATUS_RECEIVED]); + } + } + + /** + * 검사 데이터 내용 기반 개소 상태 판정 + * + * - 데이터 없음 or 검사항목 0개+사진 없음 → pending + * - 검사항목 일부 or 사진 없음 → in_progress + * - 15개 검사항목 전부 + 사진 있음 → completed + */ + private function determineLocationStatus(?array $inspectionData): string + { + if (empty($inspectionData)) { + return QualityDocumentLocation::STATUS_PENDING; + } + + $judgmentFields = [ + 'appearanceProcessing', 'appearanceSewing', 'appearanceAssembly', + 'appearanceSmokeBarrier', 'appearanceBottomFinish', 'motor', 'material', + 'lengthJudgment', 'heightJudgment', 'guideRailGap', 'bottomFinishGap', + 'fireResistanceTest', 'smokeLeakageTest', 'openCloseTest', 'impactTest', + ]; + + $inspected = 0; + foreach ($judgmentFields as $field) { + if (isset($inspectionData[$field]) && $inspectionData[$field] !== null && $inspectionData[$field] !== '') { + $inspected++; + } + } + + $hasPhotos = ! empty($inspectionData['productImages']) && is_array($inspectionData['productImages']) && count($inspectionData['productImages']) > 0; + + if ($inspected === 0 && ! $hasPhotos) { + return QualityDocumentLocation::STATUS_PENDING; + } + + if ($inspected < count($judgmentFields) || ! $hasPhotos) { + return QualityDocumentLocation::STATUS_IN_PROGRESS; + } + + return QualityDocumentLocation::STATUS_COMPLETED; + } + /** * 수주 동기화 (update 시 사용) */ @@ -668,6 +749,7 @@ private function transformToFrontend(QualityDocument $doc, bool $detail = false) 'id' => $doc->id, 'quality_doc_number' => $doc->quality_doc_number, 'site_name' => $doc->site_name, + 'client_id' => $doc->client_id, 'client' => $doc->client?->name ?? '', 'location_count' => $doc->locations?->count() ?? 0, 'required_info' => $this->calculateRequiredInfo($doc), @@ -784,9 +866,6 @@ public function inspectLocation(int $docId, int $locId, array $data) if (isset($data['change_reason'])) { $updateData['change_reason'] = $data['change_reason']; } - if (isset($data['inspection_status'])) { - $updateData['inspection_status'] = $data['inspection_status']; - } if (array_key_exists('inspection_data', $data)) { $updateData['inspection_data'] = $data['inspection_data']; } @@ -795,11 +874,16 @@ public function inspectLocation(int $docId, int $locId, array $data) $location->update($updateData); } - // 상태를 진행중으로 변경 (접수 상태일 때) - if ($doc->isReceived()) { - $doc->update(['status' => QualityDocument::STATUS_IN_PROGRESS]); + // 검사 데이터 기반 개소 상태 자동 판정 + $location->refresh(); + $newLocStatus = $this->determineLocationStatus($location->inspection_data); + if ($location->inspection_status !== $newLocStatus) { + $location->update(['inspection_status' => $newLocStatus]); } + // 문서 상태 재계산 + $this->recalculateDocumentStatus($doc); + return $location->fresh()->toArray(); }); }