selectRaw(' inventories.id, inventories.name, inventories.sku, categories.name as category_name, SUM(sale_details.quantity) as total_quantity_sold, SUM(sale_details.subtotal) as total_revenue, COUNT(DISTINCT sale_details.sale_id) as times_sold, MAX(sales.created_at) as last_sale_date, inventories.created_at as added_date ') ->join('inventories', 'sale_details.inventory_id', '=', 'inventories.id') ->join('categories', 'inventories.category_id', '=', 'categories.id') ->join('sales', 'sale_details.sale_id', '=', 'sales.id') ->where('sales.status', 'completed') ->whereNull('sales.deleted_at'); // Aplicar filtro de fechas si se proporcionan ambas if ($fromDate && $toDate) { $query->whereBetween(DB::raw('DATE(sales.created_at)'), [$fromDate, $toDate]); } $result = $query ->groupBy('inventories.id', 'inventories.name', 'inventories.sku', 'categories.name', 'inventories.created_at') ->orderByDesc('total_quantity_sold') ->first(); return $result ? $result->toArray() : null; } /** * Obtener productos sin movimiento * * @param int $daysThreshold Días mínimos sin movimiento (default: 30) * @param bool $includeStockValue Incluir valor del inventario (default: true) * @return array Lista de productos sin ventas */ public function getProductsWithoutMovement(int $daysThreshold = 30, bool $includeStockValue = true): array { // Obtener IDs de productos que SÍ tienen ventas $inventoriesWithSales = SaleDetail::query() ->join('sales', 'sale_details.sale_id', '=', 'sales.id') ->where('sales.status', 'completed') ->whereNull('sales.deleted_at') ->distinct() ->pluck('sale_details.inventory_id') ->toArray(); // Construir query para productos SIN ventas $query = Inventory::query() ->selectRaw(' inventories.id, inventories.name, inventories.sku, inventories.stock, categories.name as category_name, inventories.created_at as date_added, DATEDIFF(NOW(), inventories.created_at) as days_without_movement, prices.retail_price '); // Agregar valor del inventario si se solicita if ($includeStockValue) { $query->addSelect( DB::raw('(inventories.stock * COALESCE(prices.cost, 0)) as inventory_value') ); } return $query ->leftJoin('categories', 'inventories.category_id', '=', 'categories.id') ->leftJoin('prices', 'inventories.id', '=', 'prices.inventory_id') ->where('inventories.is_active', true) ->whereNull('inventories.deleted_at') ->whereNotIn('inventories.id', $inventoriesWithSales) ->havingRaw('DATEDIFF(NOW(), inventories.created_at) >= ?', [$daysThreshold]) ->orderBy('inventories.created_at') ->get() ->toArray(); } }