116 lines
3.0 KiB
PHP
116 lines
3.0 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Services\Quote;
|
||
|
|
|
||
|
|
use App\Models\Quote\Quote;
|
||
|
|
use App\Services\Service;
|
||
|
|
|
||
|
|
class QuoteNumberService extends Service
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* 견적번호 생성
|
||
|
|
*
|
||
|
|
* 형식: KD-{PREFIX}-{YYMMDD}-{SEQ}
|
||
|
|
* 예시: KD-SC-251204-01 (스크린), KD-ST-251204-01 (철재)
|
||
|
|
*/
|
||
|
|
public function generate(?string $productCategory = null): string
|
||
|
|
{
|
||
|
|
$tenantId = $this->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();
|
||
|
|
}
|
||
|
|
}
|