feat: 견적 산출 서비스 prices 테이블 연동

- Price 모델에 getCurrentPrice(), getSalesPriceByItemCode() 메서드 추가
- Price 모델에 STATUS_*, ITEM_TYPE_* 상수 추가
- QuoteCalculationService에 setTenantId(), getUnitPrice() 메서드 추가
- 스크린 품목 단가: 원단, 케이스, 브라켓, 인건비 prices 조회로 변경
- 철재 품목 단가: 철판, 용접, 표면처리, 가공비 prices 조회로 변경
- 모터 용량별 단가: 50W~300W prices 조회로 변경
- 모든 단가는 prices 조회 실패 시 기존 하드코딩 값을 fallback으로 사용
This commit is contained in:
2025-12-19 16:20:38 +09:00
parent 8f1292f7c4
commit 4d3085e705
2 changed files with 171 additions and 20 deletions

View File

@@ -16,6 +16,24 @@ class Price extends Model
protected $table = 'prices';
// ─────────────────────────────────────────────────────────────
// Constants
// ─────────────────────────────────────────────────────────────
// 상태
public const STATUS_DRAFT = 'draft';
public const STATUS_ACTIVE = 'active';
public const STATUS_INACTIVE = 'inactive';
public const STATUS_FINALIZED = 'finalized';
// 품목 유형
public const ITEM_TYPE_PRODUCT = 'PRODUCT';
public const ITEM_TYPE_MATERIAL = 'MATERIAL';
protected $fillable = [
'tenant_id',
'item_type_code',
@@ -218,4 +236,93 @@ public function toSnapshot(): array
'is_final' => $this->is_final,
];
}
// ─────────────────────────────────────────────────────────────
// Static Query Methods (견적 산출용)
// ─────────────────────────────────────────────────────────────
/**
* 특정 품목의 현재 유효 단가 조회
*
* @param int $tenantId 테넌트 ID
* @param string $itemTypeCode 품목 유형 (PRODUCT/MATERIAL)
* @param int $itemId 품목 ID
* @param int|null $clientGroupId 고객 그룹 ID (NULL = 기본가)
*/
public static function getCurrentPrice(
int $tenantId,
string $itemTypeCode,
int $itemId,
?int $clientGroupId = null
): ?self {
$today = now()->toDateString();
$query = static::query()
->where('tenant_id', $tenantId)
->where('item_type_code', $itemTypeCode)
->where('item_id', $itemId)
->whereIn('status', [self::STATUS_ACTIVE, self::STATUS_FINALIZED])
->where('effective_from', '<=', $today)
->where(function ($q) use ($today) {
$q->whereNull('effective_to')
->orWhere('effective_to', '>=', $today);
});
// 고객그룹 지정된 가격 우선, 없으면 기본가
if ($clientGroupId) {
$groupPrice = (clone $query)
->where('client_group_id', $clientGroupId)
->orderByDesc('effective_from')
->first();
if ($groupPrice) {
return $groupPrice;
}
}
// 기본가 (client_group_id = NULL)
return $query
->whereNull('client_group_id')
->orderByDesc('effective_from')
->first();
}
/**
* 품목 코드로 현재 유효 판매단가 조회
* (quote_formula_items.item_code와 연동용)
*
* @param int $tenantId 테넌트 ID
* @param string $itemCode 품목 코드
* @return float 판매단가 (없으면 0)
*/
public static function getSalesPriceByItemCode(int $tenantId, string $itemCode): float
{
// products 테이블에서 품목 코드로 검색
$product = \Illuminate\Support\Facades\DB::table('products')
->where('tenant_id', $tenantId)
->where('code', $itemCode)
->whereNull('deleted_at')
->first();
if ($product) {
$price = static::getCurrentPrice($tenantId, self::ITEM_TYPE_PRODUCT, $product->id);
return (float) ($price?->sales_price ?? 0);
}
// materials 테이블에서도 검색
$material = \Illuminate\Support\Facades\DB::table('materials')
->where('tenant_id', $tenantId)
->where('code', $itemCode)
->whereNull('deleted_at')
->first();
if ($material) {
$price = static::getCurrentPrice($tenantId, self::ITEM_TYPE_MATERIAL, $material->id);
return (float) ($price?->sales_price ?? 0);
}
return 0;
}
}