'datetime', 'establishment_date' => 'date', 'nts_closure_date' => 'date', 'raw_summary' => 'array', 'raw_short_term_overdue' => 'array', 'raw_negative_info_kci' => 'array', 'raw_negative_info_cb' => 'array', 'raw_suspension_info' => 'array', 'raw_workout_info' => 'array', 'raw_company_info' => 'array', 'raw_nts_status' => 'array', 'short_term_overdue_cnt' => 'integer', 'negative_info_kci_cnt' => 'integer', 'negative_info_pb_cnt' => 'integer', 'negative_info_cb_cnt' => 'integer', 'suspension_info_cnt' => 'integer', 'workout_cnt' => 'integer', ]; /** * 모델 부팅 시 inquiry_key 자동 생성 */ protected static function boot() { parent::boot(); static::creating(function ($model) { if (empty($model->inquiry_key)) { $model->inquiry_key = self::generateInquiryKey(); } if (empty($model->inquired_at)) { $model->inquired_at = now(); } }); } /** * 고유 조회 키 생성 */ public static function generateInquiryKey(): string { return date('Ymd').Str::upper(Str::random(24)); } /** * 조회자 관계 */ public function user(): BelongsTo { return $this->belongsTo(\App\Models\User::class); } /** * 총 이슈 건수 계산 */ public function getTotalIssueCountAttribute(): int { return $this->short_term_overdue_cnt + $this->negative_info_kci_cnt + $this->negative_info_pb_cnt + $this->negative_info_cb_cnt + $this->suspension_info_cnt + $this->workout_cnt; } /** * 이슈 여부 확인 */ public function getHasIssueAttribute(): bool { return $this->total_issue_count > 0; } /** * 포맷된 사업자번호 */ public function getFormattedCompanyKeyAttribute(): string { $key = $this->company_key; if (strlen($key) === 10) { return substr($key, 0, 3).'-'.substr($key, 3, 2).'-'.substr($key, 5); } return $key; } /** * 전체 원본 데이터 조합 */ public function getAllRawData(): array { return [ 'companyInfo' => $this->raw_company_info, 'ntsStatus' => $this->raw_nts_status, 'summary' => $this->raw_summary, 'shortTermOverdue' => $this->raw_short_term_overdue, 'negativeInfoKCI' => $this->raw_negative_info_kci, 'negativeInfoCB' => $this->raw_negative_info_cb, 'suspensionInfo' => $this->raw_suspension_info, 'workoutInfo' => $this->raw_workout_info, ]; } /** * 국세청 상태가 정상(영업중)인지 확인 */ public function isNtsActive(): bool { return $this->nts_status_code === '01'; } /** * 국세청 상태 라벨 */ public function getNtsStatusLabelAttribute(): string { return match ($this->nts_status_code) { '01' => '계속사업자', '02' => '휴업자', '03' => '폐업자', default => $this->nts_status ?: '미확인', }; } /** * API 응답으로부터 모델 생성 * * @param string $companyKey 사업자번호 * @param array $apiResult 쿠콘 API 결과 * @param array|null $ntsResult 국세청 API 결과 * @param int|null $userId 조회자 ID * @param int|null $tenantId 테넌트 ID */ public static function createFromApiResponse( string $companyKey, array $apiResult, ?array $ntsResult = null, ?int $userId = null, ?int $tenantId = null ): self { // 요약 정보에서 건수 추출 $summaryData = $apiResult['summary']['data'] ?? []; $creditSummaryList = $summaryData['data']['creditSummaryList'][0] ?? $summaryData['creditSummaryList'][0] ?? []; // 기업 기본정보 추출 (OA08) $companyInfoData = $apiResult['companyInfo']['data']['data'] ?? $apiResult['companyInfo']['data'] ?? []; $companyName = $companyInfoData['korentrnm'] ?? $companyInfoData['entrpNm'] ?? null; $ceoName = $companyInfoData['korreprnm'] ?? $companyInfoData['reprNm'] ?? null; $companyAddress = $companyInfoData['addr'] ?? $companyInfoData['address'] ?? null; $businessType = $companyInfoData['bizcnd'] ?? $companyInfoData['indutyNm'] ?? null; $businessItem = $companyInfoData['bizitm'] ?? $companyInfoData['indutyDetailNm'] ?? null; $establishmentDate = null; if (! empty($companyInfoData['estbDt']) || ! empty($companyInfoData['estbDate'])) { $estbDt = $companyInfoData['estbDt'] ?? $companyInfoData['estbDate']; if (strlen($estbDt) === 8) { $establishmentDate = substr($estbDt, 0, 4).'-'.substr($estbDt, 4, 2).'-'.substr($estbDt, 6, 2); } } // 국세청 상태 추출 $ntsStatus = null; $ntsStatusCode = null; $ntsTaxType = null; $ntsClosureDate = null; if ($ntsResult && ($ntsResult['success'] ?? false)) { $ntsData = $ntsResult['data'] ?? []; $ntsStatus = $ntsData['b_stt'] ?? null; $ntsStatusCode = $ntsData['b_stt_cd'] ?? null; $ntsTaxType = $ntsData['tax_type'] ?? null; if (! empty($ntsData['end_dt']) && strlen($ntsData['end_dt']) === 8) { $ntsClosureDate = substr($ntsData['end_dt'], 0, 4).'-'.substr($ntsData['end_dt'], 4, 2).'-'.substr($ntsData['end_dt'], 6, 2); } } // 성공/실패 상태 판단 $successCount = 0; $totalCount = 7; // companyInfo 추가 $errors = []; foreach (['companyInfo', 'summary', 'shortTermOverdue', 'negativeInfoKCI', 'negativeInfoCB', 'suspensionInfo', 'workoutInfo'] as $key) { if (isset($apiResult[$key]['success']) && $apiResult[$key]['success']) { $successCount++; } else { $errors[] = $key.': '.($apiResult[$key]['error'] ?? 'Unknown error'); } } $status = match (true) { $successCount === $totalCount => 'success', $successCount > 0 => 'partial', default => 'failed', }; return self::create([ 'tenant_id' => $tenantId, 'company_key' => $companyKey, 'user_id' => $userId, 'inquired_at' => now(), // 기업 기본정보 'company_name' => $companyName, 'ceo_name' => $ceoName, 'company_address' => $companyAddress, 'business_type' => $businessType, 'business_item' => $businessItem, 'establishment_date' => $establishmentDate, // 국세청 상태 'nts_status' => $ntsStatus, 'nts_status_code' => $ntsStatusCode, 'nts_tax_type' => $ntsTaxType, 'nts_closure_date' => $ntsClosureDate, // 요약 건수 'short_term_overdue_cnt' => $creditSummaryList['shorttermOverdueCnt'] ?? 0, 'negative_info_kci_cnt' => $creditSummaryList['negativeInfoBbCnt'] ?? 0, 'negative_info_pb_cnt' => $creditSummaryList['negativeInfoPbCnt'] ?? 0, 'negative_info_cb_cnt' => $creditSummaryList['negativeInfoCbCnt'] ?? 0, 'suspension_info_cnt' => $creditSummaryList['suspensionInfoCnt'] ?? 0, 'workout_cnt' => $creditSummaryList['workoutCnt'] ?? 0, // 원본 데이터 'raw_company_info' => $apiResult['companyInfo'] ?? null, 'raw_nts_status' => $ntsResult, 'raw_summary' => $apiResult['summary'] ?? null, 'raw_short_term_overdue' => $apiResult['shortTermOverdue'] ?? null, 'raw_negative_info_kci' => $apiResult['negativeInfoKCI'] ?? null, 'raw_negative_info_cb' => $apiResult['negativeInfoCB'] ?? null, 'raw_suspension_info' => $apiResult['suspensionInfo'] ?? null, 'raw_workout_info' => $apiResult['workoutInfo'] ?? null, // 상태 'status' => $status, 'error_message' => $status !== 'success' ? implode('; ', $errors) : null, ]); } /** * 스코프: 사업자번호로 검색 */ public function scopeByCompanyKey($query, string $companyKey) { return $query->where('company_key', $companyKey); } /** * 스코프: 기간으로 검색 */ public function scopeBetweenDates($query, $startDate, $endDate) { return $query->whereBetween('inquired_at', [$startDate, $endDate]); } /** * 스코프: 이슈 있는 것만 */ public function scopeWithIssues($query) { return $query->where(function ($q) { $q->where('short_term_overdue_cnt', '>', 0) ->orWhere('negative_info_kci_cnt', '>', 0) ->orWhere('negative_info_pb_cnt', '>', 0) ->orWhere('negative_info_cb_cnt', '>', 0) ->orWhere('suspension_info_cnt', '>', 0) ->orWhere('workout_cnt', '>', 0); }); } }