context->schoolYear())); $items = InventoryItem::query() ->where('type', $type) ->orderBy('name') ->get() ->toArray(); $itemIds = array_map(static fn ($i) => (int) ($i['id'] ?? 0), $items); $agg = []; if (! empty($itemIds)) { $qb = DB::table('inventory_movements') ->selectRaw('item_id, SUM(CASE WHEN qty_change>0 THEN qty_change ELSE 0 END) AS received, SUM(CASE WHEN qty_change<0 THEN -qty_change ELSE 0 END) AS issued') ->whereIn('item_id', $itemIds) ->groupBy('item_id'); if (strtolower($selectedYear) !== 'all' && $selectedYear !== '') { $qb->where('school_year', $selectedYear); } foreach ($qb->get() as $row) { $agg[(int) $row->item_id] = [ 'received' => (int) ($row->received ?? 0), 'issued' => (int) ($row->issued ?? 0), ]; } } return [ 'items' => $items, 'agg' => $agg, 'selectedYear' => $selectedYear, 'currentYear' => $this->context->schoolYear(), 'schoolYears' => $this->getSchoolYearsForType($type), ]; } public function summaryAll(?string $schoolYear, ?string $type): array { $selectedYear = trim((string) ($schoolYear ?? $this->context->schoolYear())); $selectedType = strtolower((string) ($type ?? 'all')); $isAllYears = strtolower($selectedYear) === 'all'; $qbItems = DB::table('inventory_items as i') ->select('i.*', 'c.name AS category_name', DB::raw("CONCAT(u.firstname, ' ', u.lastname) AS updated_by_name")) ->leftJoin('inventory_categories as c', 'c.id', '=', 'i.category_id') ->leftJoin('users as u', 'u.id', '=', 'i.updated_by') ->orderBy('i.name'); if (! $isAllYears) { $qbItems->where('i.school_year', $selectedYear); } if (in_array($selectedType, ['book', 'classroom', 'office', 'kitchen'], true)) { $qbItems->where('i.type', $selectedType); } $items = $qbItems->get()->map(fn ($r) => (array) $r)->all(); $rows = []; $totals = [ 'opening' => 0, 'received' => 0, 'distributed' => 0, 'other_out' => 0, 'adjust_net' => 0, 'ending' => 0, 'onhand' => 0, 'variance' => 0, ]; if (! empty($items)) { $itemIds = array_map('intval', array_column($items, 'id')); $aggById = []; $qb = DB::table('inventory_movements as m') ->selectRaw(" m.item_id, SUM(CASE WHEN m.movement_type = 'initial' THEN m.qty_change ELSE 0 END) AS opening, SUM(CASE WHEN m.qty_change > 0 AND m.movement_type IN ('in','adjust') THEN m.qty_change ELSE 0 END) AS received, SUM(CASE WHEN m.qty_change < 0 AND m.movement_type = 'distribution' THEN -m.qty_change ELSE 0 END) AS distributed, SUM(CASE WHEN m.qty_change < 0 AND m.movement_type = 'out' THEN -m.qty_change ELSE 0 END) AS other_out, SUM(CASE WHEN m.movement_type = 'adjust' THEN m.qty_change ELSE 0 END) AS adjust_net ") ->whereIn('m.item_id', $itemIds) ->groupBy('m.item_id'); if (! $isAllYears) { $qb->where('m.school_year', $selectedYear); } foreach ($qb->get()->map(fn ($r) => (array) $r)->all() as $r) { $aggById[(int) $r['item_id']] = [ 'opening' => (int) ($r['opening'] ?? 0), 'received' => (int) ($r['received'] ?? 0), 'distributed' => (int) ($r['distributed'] ?? 0), 'other_out' => (int) ($r['other_out'] ?? 0), 'adjust_net' => (int) ($r['adjust_net'] ?? 0), ]; } foreach ($items as $it) { $id = (int) $it['id']; $a = $aggById[$id] ?? ['opening' => 0, 'received' => 0, 'distributed' => 0, 'other_out' => 0, 'adjust_net' => 0]; $opening = (int) $a['opening']; $received = (int) $a['received']; $distributed = (int) $a['distributed']; $otherOut = (int) $a['other_out']; $adjustNet = (int) $a['adjust_net']; $ending = $opening + $received - $distributed - $otherOut + $adjustNet; $onhand = (int) ($it['quantity'] ?? 0); $variance = $onhand - $ending; $rows[] = [ 'id' => $id, 'name' => (string) ($it['name'] ?? ''), 'type' => (string) ($it['type'] ?? ''), 'category' => (string) ($it['category_name'] ?? ''), 'opening' => $opening, 'received' => $received, 'distributed' => $distributed, 'other_out' => $otherOut, 'adjust_net' => $adjustNet, 'ending' => $ending, 'onhand' => $onhand, 'variance' => $variance, ]; $totals['opening'] += $opening; $totals['received'] += $received; $totals['distributed'] += $distributed; $totals['other_out'] += $otherOut; $totals['adjust_net'] += $adjustNet; $totals['ending'] += $ending; $totals['onhand'] += $onhand; $totals['variance'] += $variance; } } return [ 'selectedYear' => $selectedYear, 'currentYear' => $this->context->schoolYear(), 'schoolYears' => $this->getSchoolYearsForType('book') ?: [$this->context->schoolYear(), 'All'], 'rows' => $rows, 'totals' => $totals, 'selectedType' => $selectedType, ]; } private function getSchoolYearsForType(string $type): array { $years = InventoryItem::query() ->select('school_year') ->where('type', $type) ->whereNotNull('school_year') ->groupBy('school_year') ->orderByDesc('school_year') ->pluck('school_year') ->map(fn ($v) => trim((string) $v)) ->filter() ->unique() ->values() ->all(); $currentYear = $this->context->schoolYear(); if ($currentYear && ! in_array($currentYear, $years, true)) { array_unshift($years, $currentYear); } return $years; } }