tenantId(); // 필터 파라미터 추출 $types = $filters['type'] ?? ['FG', 'PT', 'SM', 'RM', 'CS']; $search = trim($filters['search'] ?? $filters['q'] ?? ''); $categoryId = $filters['category_id'] ?? null; // 타입을 배열로 변환 (문자열인 경우 쉼표로 분리) if (is_string($types)) { $types = array_map('trim', explode(',', $types)); } // Product 타입 (FG, PT) $productTypes = array_intersect(['FG', 'PT'], $types); // Material 타입 (SM, RM, CS) $materialTypes = array_intersect(['SM', 'RM', 'CS'], $types); // Products 쿼리 (FG, PT 타입만) $productsQuery = null; if (! empty($productTypes)) { $productsQuery = Product::query() ->where('tenant_id', $tenantId) ->whereIn('product_type', $productTypes) ->where('is_active', 1) ->select([ 'id', DB::raw("'PRODUCT' as item_type"), 'code', 'name', 'unit', 'category_id', 'product_type as type_code', 'created_at', ]); // 검색 조건 if ($search !== '') { $productsQuery->where(function ($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('code', 'like', "%{$search}%"); }); } // 카테고리 필터 if ($categoryId) { $productsQuery->where('category_id', (int) $categoryId); } } // Materials 쿼리 (SM, RM, CS 타입) $materialsQuery = null; if (! empty($materialTypes)) { $materialsQuery = Material::query() ->where('tenant_id', $tenantId) ->whereIn('material_type', $materialTypes) ->select([ 'id', DB::raw("'MATERIAL' as item_type"), 'material_code as code', 'name', 'unit', 'category_id', 'material_type as type_code', 'created_at', ]); // 검색 조건 if ($search !== '') { $materialsQuery->where(function ($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('item_name', 'like', "%{$search}%") ->orWhere('material_code', 'like', "%{$search}%") ->orWhere('search_tag', 'like', "%{$search}%"); }); } // 카테고리 필터 if ($categoryId) { $materialsQuery->where('category_id', (int) $categoryId); } } // 각 쿼리 실행 후 merge (UNION 바인딩 문제 방지) $items = collect([]); if ($productsQuery) { $products = $productsQuery->get(); $items = $items->merge($products); } if ($materialsQuery) { $materials = $materialsQuery->get(); $items = $items->merge($materials); } // 정렬 (name 기준 오름차순) $items = $items->sortBy('name')->values(); // 페이지네이션 처리 $page = request()->input('page', 1); $offset = ($page - 1) * $perPage; $total = $items->count(); $paginatedItems = $items->slice($offset, $perPage)->values(); return new \Illuminate\Pagination\LengthAwarePaginator( $paginatedItems, $total, $perPage, $page, ['path' => request()->url(), 'query' => request()->query()] ); } /** * 단일 품목 조회 * * @param string $itemType 'PRODUCT' | 'MATERIAL' * @param int $id 품목 ID * @param bool $includePrice 가격 정보 포함 여부 * @param int|null $clientId 고객 ID (가격 조회 시) * @param string|null $priceDate 기준일 (가격 조회 시) * @return array 품목 데이터 (item_type, prices 포함) */ public function getItem( string $itemType, int $id, bool $includePrice = false, ?int $clientId = null, ?string $priceDate = null ): array { $tenantId = $this->tenantId(); $itemType = strtoupper($itemType); if ($itemType === 'PRODUCT') { $product = Product::query() ->with('category:id,name') ->where('tenant_id', $tenantId) ->find($id); if (! $product) { throw new NotFoundHttpException(__('error.not_found')); } $data = $product->toArray(); $data['item_type'] = 'PRODUCT'; $data['type_code'] = $product->product_type; // 가격 정보 추가 if ($includePrice) { $data['prices'] = $this->fetchPrices($itemType, $id, $clientId, $priceDate); } return $data; } elseif ($itemType === 'MATERIAL') { $material = Material::query() ->where('tenant_id', $tenantId) ->find($id); if (! $material) { throw new NotFoundHttpException(__('error.not_found')); } $data = $material->toArray(); $data['item_type'] = 'MATERIAL'; $data['code'] = $material->material_code; $data['type_code'] = $material->material_type; // 가격 정보 추가 if ($includePrice) { $data['prices'] = $this->fetchPrices($itemType, $id, $clientId, $priceDate); } return $data; } else { throw new \InvalidArgumentException(__('error.invalid_item_type')); } } /** * 품목의 판매가/매입가 조회 * * @param string $itemType 'PRODUCT' | 'MATERIAL' * @param int $itemId 품목 ID * @param int|null $clientId 고객 ID * @param string|null $priceDate 기준일 * @return array ['sale' => array, 'purchase' => array] */ private function fetchPrices(string $itemType, int $itemId, ?int $clientId, ?string $priceDate): array { // PricingService DI가 없으므로 직접 생성 $pricingService = app(\App\Services\Pricing\PricingService::class); $salePrice = $pricingService->getPriceByType($itemType, $itemId, 'SALE', $clientId, $priceDate); $purchasePrice = $pricingService->getPriceByType($itemType, $itemId, 'PURCHASE', $clientId, $priceDate); return [ 'sale' => $salePrice, 'purchase' => $purchasePrice, ]; } }