tenantId(); // 제품 카테고리에 따른 접두어 $prefix = match ($productCategory) { Quote::CATEGORY_SCREEN => 'SC', Quote::CATEGORY_STEEL => 'ST', default => 'SC', }; // 날짜 부분 (YYMMDD) $dateStr = now()->format('ymd'); // 오늘 날짜 기준으로 마지막 견적번호 조회 $pattern = "KD-{$prefix}-{$dateStr}-%"; $lastQuote = Quote::withTrashed() ->where('tenant_id', $tenantId) ->where('quote_number', 'like', $pattern) ->orderBy('quote_number', 'desc') ->first(); // 순번 계산 $sequence = 1; if ($lastQuote) { // KD-SC-251204-01 에서 마지막 숫자 추출 $parts = explode('-', $lastQuote->quote_number); if (count($parts) >= 4) { $lastSeq = (int) end($parts); $sequence = $lastSeq + 1; } } // 2자리 순번 (01, 02, ...) $seqStr = str_pad((string) $sequence, 2, '0', STR_PAD_LEFT); return "KD-{$prefix}-{$dateStr}-{$seqStr}"; } /** * 견적번호 미리보기 */ public function preview(?string $productCategory = null): array { $quoteNumber = $this->generate($productCategory); return [ 'quote_number' => $quoteNumber, 'product_category' => $productCategory ?? Quote::CATEGORY_SCREEN, 'generated_at' => now()->toDateTimeString(), ]; } /** * 견적번호 형식 검증 */ public function validate(string $quoteNumber): bool { // 형식: KD-XX-YYMMDD-NN return (bool) preg_match('/^KD-[A-Z]{2}-\d{6}-\d{2,}$/', $quoteNumber); } /** * 견적번호 파싱 */ public function parse(string $quoteNumber): ?array { if (! $this->validate($quoteNumber)) { return null; } $parts = explode('-', $quoteNumber); return [ 'prefix' => $parts[0], // KD 'category_code' => $parts[1], // SC or ST 'date' => $parts[2], // YYMMDD 'sequence' => (int) $parts[3], // 순번 ]; } /** * 견적번호 중복 체크 */ public function isUnique(string $quoteNumber, ?int $excludeId = null): bool { $tenantId = $this->tenantId(); $query = Quote::withTrashed() ->where('tenant_id', $tenantId) ->where('quote_number', $quoteNumber); if ($excludeId) { $query->where('id', '!=', $excludeId); } return ! $query->exists(); } }