tenantId = $tenantId; } // ========================================================================= // BDmodels 단가 조회 (절곡품) // ========================================================================= /** * BDmodels 단가 조회 * * item_details 컬럼 매핑: * product_category = 'bdmodels' * part_type = seconditem (가이드레일, 케이스, 마구리, L-BAR, 하단마감재, 보강평철, 연기차단재) * specification = spec (120*70, 500*380 등) * items.attributes: * model_name = KSS01, KSS02 등 * finishing_type = SUS, EGI */ public function getBDModelPrice( string $secondItem, ?string $modelName = null, ?string $finishingType = null, ?string $spec = null ): float { $cacheKey = "bdmodel:{$secondItem}:{$modelName}:{$finishingType}:{$spec}"; if (isset($this->cache[$cacheKey])) { return $this->cache[$cacheKey]; } $query = DB::table('items') ->join('item_details', 'item_details.item_id', '=', 'items.id') ->join('prices', 'prices.item_id', '=', 'items.id') ->where('items.tenant_id', $this->tenantId) ->where('items.is_active', true) ->whereNull('items.deleted_at') ->where('item_details.product_category', 'bdmodels') ->where('item_details.part_type', $secondItem); if ($modelName) { $query->where('items.attributes->model_name', $modelName); } else { $query->where(function ($q) { $q->whereNull('items.attributes->model_name') ->orWhere('items.attributes->model_name', ''); }); } if ($finishingType) { $query->where('items.attributes->finishing_type', $finishingType); } if ($spec) { $query->where('item_details.specification', $spec); } // 현재 유효한 단가 $today = now()->toDateString(); $query->where('prices.effective_from', '<=', $today) ->where(function ($q) use ($today) { $q->whereNull('prices.effective_to') ->orWhere('prices.effective_to', '>=', $today); }) ->whereNull('prices.deleted_at'); $price = (float) ($query->value('prices.sales_price') ?? 0); $this->cache[$cacheKey] = $price; return $price; } /** * 케이스 단가 */ public function getCasePrice(string $spec): float { return $this->getBDModelPrice('케이스', null, null, $spec); } /** * 가이드레일 단가 */ public function getGuideRailPrice(string $modelName, string $finishingType, string $spec): float { return $this->getBDModelPrice('가이드레일', $modelName, $finishingType, $spec); } /** * 하단마감재(하장바) 단가 */ public function getBottomBarPrice(string $modelName, string $finishingType): float { return $this->getBDModelPrice('하단마감재', $modelName, $finishingType); } /** * L-BAR 단가 */ public function getLBarPrice(string $modelName): float { return $this->getBDModelPrice('L-BAR', $modelName); } /** * 보강평철 단가 */ public function getFlatBarPrice(): float { return $this->getBDModelPrice('보강평철'); } /** * 케이스 마구리 단가 */ public function getCaseCapPrice(string $spec): float { return $this->getBDModelPrice('마구리', null, null, $spec); } /** * 케이스용 연기차단재 단가 */ public function getCaseSmokeBlockPrice(): float { return $this->getBDModelPrice('케이스용 연기차단재'); } /** * 가이드레일용 연기차단재 단가 */ public function getRailSmokeBlockPrice(): float { return $this->getBDModelPrice('가이드레일용 연기차단재'); } // ========================================================================= // 모터/제어기 단가 // ========================================================================= /** * 모터 단가 * * chandj col2는 '150K(S)', '300K(S)', '300K' 등 다양한 형식 * handler는 '150K', '300K' 등 단순 용량으로 호출 * LIKE 매칭 + 380V 기본 전압 필터 적용 */ public function getMotorPrice(string $motorCapacity, string $voltage = '380'): float { $cacheKey = "motor:{$motorCapacity}:{$voltage}"; if (isset($this->cache[$cacheKey])) { return $this->cache[$cacheKey]; } $today = now()->toDateString(); $price = (float) (DB::table('items') ->join('item_details', 'item_details.item_id', '=', 'items.id') ->join('prices', 'prices.item_id', '=', 'items.id') ->where('items.tenant_id', $this->tenantId) ->where('items.is_active', true) ->whereNull('items.deleted_at') ->where('item_details.product_category', 'motor') ->where('item_details.part_type', 'LIKE', "{$motorCapacity}%") ->where('items.attributes->voltage', $voltage) ->where('prices.effective_from', '<=', $today) ->where(function ($q) use ($today) { $q->whereNull('prices.effective_to') ->orWhere('prices.effective_to', '>=', $today); }) ->whereNull('prices.deleted_at') ->value('prices.sales_price') ?? 0); $this->cache[$cacheKey] = $price; return $price; } /** * 제어기 단가 */ public function getControllerPrice(string $controllerType): float { return $this->getEstimatePartPrice('controller', $controllerType); } // ========================================================================= // 부자재 단가 // ========================================================================= /** * 샤프트 단가 * * chandj col10은 '0.3', '3', '6' 등 혼재 포맷 * 정수면 '6', 소수면 '0.3' 그대로 저장됨 */ public function getShaftPrice(string $size, float $length): float { // chandj 원본 포맷에 맞게 변환: 정수면 정수형, 소수면 소수형 $lengthStr = ($length == (int) $length) ? (string) (int) $length : (string) $length; $cacheKey = "shaft:{$size}:{$lengthStr}"; if (isset($this->cache[$cacheKey])) { return $this->cache[$cacheKey]; } $price = $this->getEstimatePartPriceBySpec('shaft', $size, $lengthStr); $this->cache[$cacheKey] = $price; return $price; } /** * 파이프 단가 */ public function getPipePrice(string $thickness, int $length): float { return $this->getEstimatePartPriceBySpec('pipe', $thickness, (string) $length); } /** * 모터 받침용 앵글 단가 (bracket angle) * * 5130: calculateAngle(qty, itemList, '스크린용') → col2 검색 * chandj col2 값: '스크린용', '철제300K', '철제400K', '철제800K' */ public function getAnglePrice(string $searchOption): float { $cacheKey = "angle_bracket:{$searchOption}"; if (isset($this->cache[$cacheKey])) { return $this->cache[$cacheKey]; } $today = now()->toDateString(); $price = (float) (DB::table('items') ->join('item_details', 'item_details.item_id', '=', 'items.id') ->join('prices', 'prices.item_id', '=', 'items.id') ->where('items.tenant_id', $this->tenantId) ->where('items.is_active', true) ->whereNull('items.deleted_at') ->where('item_details.product_category', 'angle_bracket') ->where('item_details.part_type', $searchOption) ->where('prices.effective_from', '<=', $today) ->where(function ($q) use ($today) { $q->whereNull('prices.effective_to') ->orWhere('prices.effective_to', '>=', $today); }) ->whereNull('prices.deleted_at') ->value('prices.sales_price') ?? 0); $this->cache[$cacheKey] = $price; return $price; } /** * 부자재용 앵글 단가 (main angle) * * 5130: calculateMainAngle(1, itemList, '앵글3T', '2.5') → col4+col10 검색 */ public function getMainAnglePrice(string $angleType, string $size): float { $cacheKey = "angle_main:{$angleType}:{$size}"; if (isset($this->cache[$cacheKey])) { return $this->cache[$cacheKey]; } $price = $this->getEstimatePartPriceBySpec('angle_main', $angleType, $size); $this->cache[$cacheKey] = $price; return $price; } /** * 원자재 단가 */ public function getRawMaterialPrice(string $materialName): float { return $this->getEstimatePartPrice('raw_material', $materialName); } // ========================================================================= // 내부 헬퍼 // ========================================================================= /** * product_category + part_type 기반 단가 조회 */ private function getEstimatePartPrice(string $productCategory, string $partType): float { $cacheKey = "{$productCategory}:{$partType}"; if (isset($this->cache[$cacheKey])) { return $this->cache[$cacheKey]; } $today = now()->toDateString(); $price = (float) (DB::table('items') ->join('item_details', 'item_details.item_id', '=', 'items.id') ->join('prices', 'prices.item_id', '=', 'items.id') ->where('items.tenant_id', $this->tenantId) ->where('items.is_active', true) ->whereNull('items.deleted_at') ->where('item_details.product_category', $productCategory) ->where('item_details.part_type', $partType) ->where('prices.effective_from', '<=', $today) ->where(function ($q) use ($today) { $q->whereNull('prices.effective_to') ->orWhere('prices.effective_to', '>=', $today); }) ->whereNull('prices.deleted_at') ->value('prices.sales_price') ?? 0); $this->cache[$cacheKey] = $price; return $price; } /** * product_category + spec1 + spec2 기반 단가 조회 */ private function getEstimatePartPriceBySpec(string $productCategory, string $spec1, string $spec2): float { $cacheKey = "{$productCategory}:{$spec1}:{$spec2}"; if (isset($this->cache[$cacheKey])) { return $this->cache[$cacheKey]; } $today = now()->toDateString(); $price = (float) (DB::table('items') ->join('item_details', 'item_details.item_id', '=', 'items.id') ->join('prices', 'prices.item_id', '=', 'items.id') ->where('items.tenant_id', $this->tenantId) ->where('items.is_active', true) ->whereNull('items.deleted_at') ->where('item_details.product_category', $productCategory) ->where('item_details.part_type', $spec1) ->where('item_details.specification', $spec2) ->where('prices.effective_from', '<=', $today) ->where(function ($q) use ($today) { $q->whereNull('prices.effective_to') ->orWhere('prices.effective_to', '>=', $today); }) ->whereNull('prices.deleted_at') ->value('prices.sales_price') ?? 0); $this->cache[$cacheKey] = $price; return $price; } /** * 캐시 초기화 */ public function clearCache(): void { $this->cache = []; } }