select('expenses.*', 'u.firstname as purchaser_firstname', 'u.lastname as purchaser_lastname') ->leftJoin('users as u', 'u.id', '=', 'expenses.purchased_by') ->where('expenses.status', 'approved') ->whereNull('expenses.reimbursement_id') ->where('expenses.category', '!=', 'Donation') ->orderByDesc('expenses.created_at'); $records = $builder->get(); $grouped = []; foreach ($records as $row) { $personId = (int) ($row->purchased_by ?? 0); $name = trim(($row->purchaser_firstname ?? '').' '.($row->purchaser_lastname ?? '')); if ($name === '') { $name = $row->purchaser_firstname ?? $row->purchaser_lastname ?? 'Unknown'; } $key = $personId > 0 ? 'id_'.$personId : 'name_'.strtolower($name); if (! isset($grouped[$key])) { $grouped[$key] = [ 'name' => $name ?: 'Unknown', 'total' => 0.0, 'vendors' => [], 'descriptions' => [], ]; } $amount = (float) ($row->amount ?? 0); $grouped[$key]['total'] += $amount; $vendor = trim((string) ($row->retailor ?? '')); if ($vendor !== '' && ! in_array($vendor, $grouped[$key]['vendors'], true)) { $grouped[$key]['vendors'][] = $vendor; } $desc = trim((string) ($row->description ?? '')); if ($desc !== '' && ! in_array($desc, $grouped[$key]['descriptions'], true)) { $grouped[$key]['descriptions'][] = $desc; } } $rows = []; $rows[] = ['Person', 'Total Amount', 'Vendors', 'Descriptions']; foreach ($grouped as $entry) { $rows[] = [ $entry['name'], number_format($entry['total'], 2, '.', ''), implode('; ', $entry['vendors']), implode('; ', $entry['descriptions']), ]; } return [ 'filename' => 'under_processing_expenses.csv', 'rows' => $rows, ]; } public function buildProcessedCsv(array $filters): array { $builder = DB::table('reimbursements') ->select('reimbursements.*', 'u.firstname as reimb_firstname', 'u.lastname as reimb_lastname') ->leftJoin('users as u', 'u.id', '=', 'reimbursements.reimbursed_to'); if (! empty($filters['semester'])) { $builder->where('reimbursements.semester', $filters['semester']); } if (! empty($filters['status'])) { $builder->where('reimbursements.status', $filters['status']); } if (! empty($filters['user_id'])) { $builder->where('reimbursements.reimbursed_to', $filters['user_id']); } if (! empty($filters['school_year'])) { $builder->where('reimbursements.school_year', $filters['school_year']); } $records = $builder->orderByDesc('reimbursements.created_at')->get(); $rows = []; $rows[] = ['Amount', 'Reimbursed To', 'Status', 'Method', 'Check #', 'School Year', 'Semester', 'Date']; foreach ($records as $row) { $hasName = trim(($row->reimb_firstname ?? '').' '.($row->reimb_lastname ?? '')) !== ''; if (! $hasName) { $label = $this->recipients->specialRecipientLabel($row->reimbursed_to ?? null); if ($label !== null) { $row->reimb_firstname = $label; $row->reimb_lastname = ''; } } $rows[] = [ $row->amount, trim(($row->reimb_firstname ?? '').' '.($row->reimb_lastname ?? '')), $row->status, $row->reimbursement_method ?? '-', $row->check_number ?? '-', $row->school_year ?? '-', $row->semester ?? '-', $row->created_at, ]; } return [ 'filename' => 'reimbursements_export.csv', 'rows' => $rows, ]; } public function buildBatchCsv(int $batchId): array { $batch = ReimbursementBatch::query()->find($batchId); if (! $batch) { throw new RuntimeException('Batch not found.'); } $batchLabel = trim((string) ($batch->title ?? '')) ?: ('Batch #'.$batchId); $receiptRows = $this->fetchBatchReceiptRows($batchId); $rows = []; $this->writeBatchCsvMetadata($rows, $batchLabel, $batch->toArray()); $this->writeBatchCsvSummary($rows, $receiptRows, $this->formatCsvDate($batch->closed_at ?? null)); return [ 'filename' => sprintf('batch_%d_items.csv', $batchId), 'rows' => $rows, ]; } private function fetchBatchReceiptRows(int $batchId, array $receiptIds = []): array { $builder = DB::table('reimbursement_batch_items as bi') ->select('e.id as expense_id', 'e.retailor', 'e.amount', 'e.description', 'e.receipt_path as receipt_filename', 'e.date_of_purchase as purchase_date', 'u.firstname as purchaser_firstname', 'u.lastname as purchaser_lastname') ->join('expenses as e', 'e.id', '=', 'bi.expense_id') ->leftJoin('users as u', 'u.id', '=', 'e.purchased_by') ->where('bi.batch_id', $batchId); if (! empty($receiptIds)) { $builder->whereIn('e.id', $receiptIds); } $rows = $builder->get()->toArray(); return array_values(array_filter($rows, static function ($row) { return ! empty($row->receipt_filename); })); } private function writeBatchCsvMetadata(array &$rows, string $batchLabel, array $batch): void { $closedAtRaw = $batch['closed_at'] ?? null; $rows[] = ['Batch Title', $batchLabel]; $rows[] = ['School Year', $batch['school_year'] ?? '', 'Semester', $batch['semester'] ?? '', 'Closed At', $this->formatCsvDate($closedAtRaw)]; $rows[] = []; $rows[] = ['Amount', 'Credit', 'Paid', 'Balance', 'Date', 'By Who', 'Vendor', 'Description']; } private function writeBatchCsvSummary(array &$rows, array $receiptRows, string $totalsDate = ''): void { $receiptTotalsByRecipient = []; foreach ($receiptRows as $row) { $recipientName = trim(($row->purchaser_firstname ?? '').' '.($row->purchaser_lastname ?? '')); if ($recipientName === '') { $recipientName = 'Unknown'; } $vendor = trim((string) ($row->retailor ?? '')); if ($vendor === '') { $vendor = 'Vendor N/A'; } $dateText = $this->formatCsvDate($row->purchase_date ?? null); $descriptionValue = trim((string) ($row->description ?? '')); $descriptionParts = $descriptionValue !== '' ? [$descriptionValue] : ['Receipt']; $amountValue = (float) ($row->amount ?? 0); $receiptTotalsByRecipient[$recipientName] = ($receiptTotalsByRecipient[$recipientName] ?? 0.0) + $amountValue; $rows[] = [ number_format($amountValue, 2, '.', ''), '', number_format($amountValue, 2, '.', ''), '0.00', $dateText, $recipientName, $vendor, implode(' | ', $descriptionParts), ]; } if (! empty($receiptTotalsByRecipient)) { $rows[] = []; $rows[] = ['Totals by Recipient', '', '', '', '', '', '', '']; foreach ($receiptTotalsByRecipient as $recipient => $total) { $formatted = number_format($total, 2, '.', ''); $rows[] = [ $formatted, '', $formatted, '0.00', $totalsDate, $recipient, '', '', ]; } } } private function formatCsvDate(?string $value): string { if (! $value) { return ''; } $timestamp = strtotime($value); if ($timestamp === false) { return ''; } return date('m-d-Y', $timestamp); } }