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('가이드레일용 연기차단재'); } // ========================================================================= // 모터/제어기 단가 // ========================================================================= /** * 모터 단가 */ public function getMotorPrice(string $motorCapacity): float { return $this->getEstimatePartPrice('motor', $motorCapacity); } /** * 제어기 단가 */ public function getControllerPrice(string $controllerType): float { return $this->getEstimatePartPrice('motor', $controllerType); } // ========================================================================= // 부자재 단가 // ========================================================================= /** * 샤프트 단가 */ public function getShaftPrice(string $size, float $length): float { $lengthStr = number_format($length, 1, '.', ''); $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); } /** * 앵글 단가 */ public function getAnglePrice(string $type, string $bracketSize, string $angleType): float { $cacheKey = "angle:{$type}:{$bracketSize}:{$angleType}"; 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') ->where('item_details.part_type', $type) ->where('item_details.specification', $bracketSize) ->where('items.attributes->angle_type', $angleType) ->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 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 = []; } }