feat: 품질관리·대시보드·결재 양식 개선

- 품질관리 수주선택 필터링 + 검사 상태 자동 재계산
- 제품검사 요청서 Document(EAV) 자동생성 및 동기화
- 현황판 결재 카드 approvalOnly 스코프 + sub_label 추가
- 캘린더 어음 만기일 일정 연동
- QuoteStatService codebridge DB 커넥션 연결
- 테넌트 부트스트랩 기본 결재 양식 자동 시딩
This commit is contained in:
2026-03-10 11:29:56 +09:00
parent c46b950fde
commit bd500a87bd
7 changed files with 316 additions and 42 deletions

View File

@@ -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();
});
}