AgentIncentiveFileModel = new AgentIncentiveFileModel(); $this->PayoutGridModel = new PartnerInsurancePayoutGridModel(); $this->PartnerAgentModel = new AgentModel(); } // GET /agent/gridFileList public function gridFileList() { try { $builder = $this->AgentIncentiveFileModel->builder(); // 1. Explicitly select and format dates in the SQL layer (Faster + Indian Format) $builder->select(" paif.id, paif.agent_id, paif.incentive_month, DATE_FORMAT(paif.incentive_month, '%d-%m-%Y') as vaild_from, paif.incentive_file_name, ps.name as created_by_name, paif.created_on, DATE_FORMAT(paif.created_on, '%d-%m-%Y %h:%i %p') as created_date "); $builder->from('partner_agent_incentive_file paif'); // 2. The Join $builder->join('partner_staff ps', 'ps.id = paif.created_by', 'left'); // 3. Filters $builder->where('paif.is_active', 1); $builder->where('paif.file_type', 'grid'); // 4. THE FIX: Group by the primary ID to stop the "5 rows" duplication $builder->groupBy('paif.id'); // 5. Order $builder->orderBy('paif.id', 'DESC'); $query = $builder->get(); $result = $query->getResult(); // Use $this->respond to maintain consistency with your other API methods return $this->respond([ 'status' => 'success', // Changed to 'success' to match your other methods 'code' => 200, 'data' => $result ], 200); } catch (\Exception $e) { return $this->respond([ 'status' => 'failed', 'code' => 500, 'data' => $e->getMessage() ], 500); } } // ------------------------------------------------------------------------- // Upload Grid File (file_type = 'grid') // Parses Excel and UPSERTs rows into partner_insurance_payout_grid // ------------------------------------------------------------------------- public function uploadGridFile() { try { /* ==================================================================== * STEP 1 — Validate POST input * ==================================================================== */ $data = $this->request->getPost(); $month = $data['incentive_month'] ?? null; $createdBy = $data['created_by'] ?? null; /* ==================================================================== * STEP 2 — Validation checks for month * ==================================================================== */ if (empty($month)) { return $this->respond([ 'status' => 'failed', 'code' => 400, 'data' => 'Date is required.', ], 200); } /* ==================================================================== * STEP 3 — Validate uploaded file * ==================================================================== */ $gridFile = $this->request->getFile('incentive_file_name'); if (!$gridFile || !$gridFile->isValid()) { return $this->respond([ 'status' => 'failed', 'code' => 400, 'data' => 'No valid file uploaded.', ], 200); } /* ==================================================================== * STEP 4 — Move file * ==================================================================== */ $uploadPath = WRITEPATH . 'uploads/agent/incentive_file/'; if (!is_dir($uploadPath)) mkdir($uploadPath, 0777, true); $gridFileName = time() . '_' . $gridFile->getRandomName(); $gridFile->move($uploadPath, $gridFileName); /* ==================================================================== * STEP 5 — Parse Excel FIRST (before saving file record) * ==================================================================== */ $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($uploadPath . $gridFileName); $rows = $spreadsheet->getActiveSheet()->toArray(null, true, true, false); if (empty($rows)) { if (file_exists($uploadPath . $gridFileName)) unlink($uploadPath . $gridFileName); return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'File is empty.'], 200); } /* ==================================================================== * STEP 6 — Clean Numeric Helper * ==================================================================== */ $extractNumeric = static function ($value): ?float { $str = strtolower(trim((string) $value)); if ($str === '' || $str === 'nan') return null; if (str_contains($str, '+')) { $sum = 0.0; $hasValid = false; foreach (explode('+', $str) as $part) { $cleanPart = preg_replace('/[^0-9.]/', '', $part); if (is_numeric($cleanPart)) { $sum += (float) $cleanPart; $hasValid = true; } } return $hasValid ? $sum : null; } $str = preg_replace('/[^0-9.]/', '', $str); return is_numeric($str) ? (float) $str : null; }; /* ==================================================================== * STEP 7 — Pre-validate ALL rows BEFORE inserting file record * ==================================================================== */ $currentVehicleType = null; $layoutHasComp = true; $errors = []; $validRows = []; foreach ($rows as $rowIndex => $row) { while (count($row) < 9) $row[] = null; $col0 = trim((string) $row[0]); $col1 = trim((string) $row[1]); // ── Section Header Detection (vehicle type row) ────────────────── if ( $col0 !== '' && ($col1 === '' || strtolower($col1) === 'nan') && strtoupper($col0) !== 'INSURER' ) { $currentVehicleType = strtoupper($col0); continue; } // ── Column Header Detection (Layout A vs B) ────────────────────── if (strtoupper($col0) === 'INSURER') { $col4Upper = strtoupper((string) $row[4]); // Layout B = col E header is FUEL/PETROL/DIESEL (Private Car layout) $layoutHasComp = !preg_match('/(FUEL|PETROL|DIESEL)/', $col4Upper); continue; } // ── Skip completely empty rows ─────────────────────────────────── if (empty($col0)) continue; // ── VALIDATION 1 : Vehicle type missing ────────────────────────── if ($currentVehicleType === null) { $errors[] = [ 'row' => $rowIndex + 1, 'insurer' => $col0, 'message' => "Row " . ($rowIndex + 1) . " (Insurer: {$col0}): Vehicle type header is missing. " . "Please add a vehicle type (e.g. 'TWO WHEELER NEW') before data rows.", ]; continue; } // ── VALIDATION 2 : Segment missing ─────────────────────────────── $segment = strtoupper(trim((string) $row[2])); if ($segment === '' || strtolower($segment) === 'nan') { $errors[] = [ 'row' => $rowIndex + 1, 'insurer' => $col0, 'vehicle_type' => $currentVehicleType, 'message' => "Row " . ($rowIndex + 1) . " (Insurer: {$col0}, Vehicle Type: {$currentVehicleType}): " . "Segment is empty. Please provide a valid segment value.", ]; continue; } // ── Map columns based on layout ─────────────────────────────────── if ($layoutHasComp) { // Layout A: INSURER | RTO | SEGMENT | COMP | TP | REMARKS | ... $insurer = strtoupper($col0); $rto = strtoupper((string) $row[1]); $comp = $extractNumeric($row[3]); $tp = $extractNumeric($row[4]); $od = null; $fuel = null; $brokerName = trim((string) $row[6]); $brokerExcelComp = $extractNumeric($row[7]); $brokerExcelTp = $extractNumeric($row[8]); $brokerExcelOd = null; } else { // Layout B: INSURER | RTO | SEGMENT | COMP | FUEL | REMARKS | ... $insurer = strtoupper($col0); $rto = strtoupper((string) $row[1]); $comp = $extractNumeric($row[3]); $tp = null; $od = null; $fuel = strtoupper(trim((string) $row[4])); $brokerName = trim((string) $row[6]); $brokerExcelComp = null; $brokerExcelTp = $extractNumeric($row[7]); $brokerExcelOd = null; } // ── OD logic: col D has "OD x.xX" prefix → move to od field ────── if ($comp !== null && stripos(trim((string) $row[3]), 'OD') !== false) { $od = $comp; $comp = null; $brokerExcelOd = $brokerExcelComp; $brokerExcelComp = null; } // ── OD logic: col E (TP) has "OD x.xX" prefix in Layout A ──────── if ($layoutHasComp && $tp !== null && stripos(trim((string) $row[4]), 'OD') !== false) { $od = $tp; $tp = null; $brokerExcelOd = $brokerExcelTp; $brokerExcelTp = null; } // ── Store valid parsed row ──────────────────────────────────────── $validRows[] = [ 'vehicle_type' => $currentVehicleType, 'fuel' => $fuel, 'insurer' => $insurer, 'rto' => $rto, 'broker_name' => $brokerName, 'segment' => $segment, 'comp' => $comp, 'tp' => $tp, 'od' => $od, 'remarks' => trim((string) ($row[5] ?? '')), 'broker_comp' => $brokerExcelComp, 'broker_tp' => $brokerExcelTp, 'broker_od' => $brokerExcelOd, 'created_by' => $createdBy, ]; } /* ==================================================================== * STEP 8 — If ANY validation errors, delete file & return errors * Do NOT insert file record at all * ==================================================================== */ if (!empty($errors)) { if (file_exists($uploadPath . $gridFileName)) unlink($uploadPath . $gridFileName); return $this->respond([ 'status' => 'failed', 'code' => 400, 'data' => [ 'inserted' => 0, 'errors' => $errors, 'message' => count($errors) . ' row(s) had validation issues. File was not saved.', ], ], 200); } /* ==================================================================== * STEP 9 — All rows valid: NOW insert the file record * ==================================================================== */ $fileData = [ 'incentive_month' => $month, 'incentive_file_name' => $gridFileName, 'file_type' => 'grid', 'is_active' => 1, 'created_by' => $createdBy, ]; $fileId = $this->AgentIncentiveFileModel->insert($fileData, true); if (empty($fileId) || (int)$fileId <= 0) { $modelErrors = $this->AgentIncentiveFileModel->errors(); return $this->respond([ 'status' => 'failed', 'code' => 400, 'data' => !empty($modelErrors) ? $modelErrors : 'Unable to create incentive file record.', ], 200); } /* ==================================================================== * STEP 10 — Insert all valid rows with the file ID * ==================================================================== */ $insertedCount = 0; foreach ($validRows as $gridRow) { $gridRow['partner_agent_incentive_file_id'] = $fileId; $this->PayoutGridModel->insert($gridRow); $insertedCount++; } /* ==================================================================== * STEP 11 — Full success response * ==================================================================== */ return $this->respond([ 'status' => 'success', 'code' => 200, 'data' => [ 'file_id' => $fileId, 'inserted' => $insertedCount, 'message' => "Processed: {$insertedCount} new", ], ], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } // public function uploadGridFile_old() // { // try { // /* ==================================================================== // * STEP 1 — Validate POST input (Agent ID removed) // * ==================================================================== */ // $data = $this->request->getPost(); // $month = $data['incentive_month'] ?? null; // $createdBy = $data['created_by'] ?? null; // /* ==================================================================== // * STEP 2 — Validation now only checks for month // * ==================================================================== */ // if (empty($month)) { // return $this->respond([ // 'status' => 'failed', // 'code' => 400, // 'data' => 'Incentive month is required.', // ], 200); // } // /* ==================================================================== // * STEP 3 — Validate uploaded file // * ==================================================================== */ // $gridFile = $this->request->getFile('incentive_file_name'); // if (!$gridFile || !$gridFile->isValid()) { // return $this->respond([ // 'status' => 'failed', // 'code' => 400, // 'data' => 'No valid file uploaded.', // ], 200); // } // /* ==================================================================== // * STEP 4 — Move file // * ==================================================================== */ // $uploadPath = WRITEPATH . 'uploads/agent/incentive_file/'; // if (!is_dir($uploadPath)) mkdir($uploadPath, 0777, true); // $gridFileName = time() . '_' . $gridFile->getRandomName(); // $gridFile->move($uploadPath, $gridFileName); // /* ==================================================================== // * STEP 5 — Save File record & GET THE ID // * ==================================================================== */ // $fileData = [ // 'incentive_month' => $month, // 'incentive_file_name' => $gridFileName, // 'file_type' => 'grid', // 'is_active' => 1, // 'created_by' => $createdBy, // ]; // // insert() with returnID enabled returns the inserted primary key in CI4. // $fileId = $this->AgentIncentiveFileModel->insert($fileData, true); // if (empty($fileId) || (int)$fileId <= 0) { // $modelErrors = $this->AgentIncentiveFileModel->errors(); // return $this->respond([ // 'status' => 'failed', // 'code' => 400, // 'data' => !empty($modelErrors) ? $modelErrors : 'Unable to create incentive file record.', // ], 200); // } // /* ==================================================================== // * STEP 6 — Parse Excel // * ==================================================================== */ // $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($uploadPath . $gridFileName); // $rows = $spreadsheet->getActiveSheet()->toArray(null, true, true, false); // if (empty($rows)) { // return $this->respond(['status' => 'success', 'code' => 200, 'data' => 'File empty.'], 200); // } // /* ==================================================================== // * STEP 7 — Clean Numeric Helper (Modified to store only numbers) // * ==================================================================== */ // $extractNumeric = static function ($value): ?float { // $str = strtolower(trim((string) $value)); // if ($str === '' || $str === 'nan') return null; // if (str_contains($str, '+')) { // $sum = 0.0; $hasValid = false; // foreach (explode('+', $str) as $part) { // $cleanPart = preg_replace('/[^0-9.]/', '', $part); // if (is_numeric($cleanPart)) { $sum += (float) $cleanPart; $hasValid = true; } // } // return $hasValid ? $sum : null; // } // // Strip everything except numbers and decimals // $str = preg_replace('/[^0-9.]/', '', $str); // return is_numeric($str) ? (float) $str : null; // }; // /* ==================================================================== // * STEP 8 — Process Rows // * ==================================================================== */ // $currentVehicleType = null; // $layoutHasComp = true; // $insertedCount = 0; // foreach ($rows as $row) { // while (count($row) < 9) $row[] = null; // $col0 = trim((string) $row[0]); // $col1 = trim((string) $row[1]); // // Section Header Detection // if ($col0 !== '' && ($col1 === '' || strtolower($col1) === 'nan') && strtoupper($col0) !== 'INSURER') { // $currentVehicleType = strtoupper($col0); // continue; // } // // Column Header Detection (Layout A vs B) // if (strtoupper($col0) === 'INSURER') { // $col4Upper = strtoupper((string)$row[4]); // $layoutHasComp = !preg_match('/(FUEL|PETROL|DIESEL|TP)/', $col4Upper); // continue; // } // if (empty($col0) || $currentVehicleType === null) continue; // // Mapping with Clean Numbers // if ($layoutHasComp) { // $insurer = strtoupper($col0); // $rto = strtoupper((string)$row[1]); // $segment = strtoupper((string)$row[2]); // // We extract numeric values only for the storage // $comp = $extractNumeric($row[3]); // $tp = $extractNumeric($row[4]); // $od = null; // $fuel = null; // $brokerName = trim((string)$row[6]); // $brokerExcelComp = $extractNumeric($row[7]); // $brokerExcelTp = $extractNumeric($row[8]); // $brokerExcelOd = null; // } else { // $insurer = strtoupper($col0); // $rto = strtoupper((string)$row[1]); // $segment = strtoupper((string)$row[2]); // $comp = $extractNumeric($row[3]); // $tp = null; // $od = null; // $fuel = strtoupper((string)$row[4]); // $brokerName = trim((string)$row[6]); // $brokerExcelComp = null; // $brokerExcelTp = $extractNumeric($row[7]); // $brokerExcelOd = null; // } // // OD logic (if COMP contains an OD value) // if ($comp !== null && stripos((string)$row[3], 'OD') !== false && $tp === null) { // $od = $comp; $comp = null; // $brokerExcelOd = $brokerExcelComp; $brokerExcelComp = null; // } // $gridRow = [ // 'partner_agent_incentive_file_id' => $fileId, // STORE THE FILE ID HERE // 'vehicle_type' => $currentVehicleType, // 'fuel' => $fuel, // 'insurer' => $insurer, // 'rto' => $rto, // 'broker_name' => $brokerName, // 'segment' => $segment, // 'comp' => $comp, // 'tp' => $tp, // 'od' => $od, // 'remarks' => trim((string)($row[5] ?? '')), // 'broker_comp' => $brokerExcelComp, // 'broker_tp' => $brokerExcelTp, // 'broker_od' => $brokerExcelOd, // ]; // $gridRow['created_by'] = $createdBy; // $this->PayoutGridModel->insert($gridRow); // $insertedCount++; // } // return $this->respond([ // 'status' => 'success', // 'code' => 200, // 'data' => [ // 'file_id' => $fileId, // 'inserted' => $insertedCount, // 'message' => "Processed: {$insertedCount} new", // ], // ], 200); // } catch (\Exception $e) { // return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); // } // } // ------------------------------------------------------------------------- // List all grid records (optional utility endpoint) // ------------------------------------------------------------------------- // GET /grid?role=Manager means all list (optional insurer, rto, segment, vehicle_type) public function getGridData() { try { $request = $this->request; $role = $request->getGet('role'); $file_id = $request->getGet('file_id'); // 1. Role is required if (!$role) { return $this->respond([ 'status' => 'failed', 'code' => 400, 'data' => 'Role is required to fetch payout data.' ], 400); } // 2. Extract Filters $insurer = trim((string) ($request->getGet('insurer') ?? '')); $rto = trim((string) ($request->getGet('rto') ?? '')); $segment = trim((string) ($request->getGet('segment') ?? '')); $vehicle_type = trim((string) ($request->getGet('vehicle_type') ?? '')); $search = trim((string) ($request->getGet('search') ?? '')); // ── 3. Resolve file_id based on role ───────────────────────────────── $db = \Config\Database::connect(); if (in_array($role, ['Manager', 'Accounts'], true)) { // Manager / Accounts: file_id must be supplied in query param if (empty($file_id)) { return $this->respond([ 'status' => 'failed', 'code' => 400, 'data' => 'file_id is required for Manager / Accounts role.' ], 400); } $resolvedFileId = (int) $file_id; } else { // Agent: find the latest id from partner_agent_incentive_file (no agent filter needed) $fileRow = $db->table('partner_agent_incentive_file') ->select('id') ->orderBy('id', 'DESC') ->limit(1) ->get() ->getRowArray(); if (empty($fileRow)) { return $this->respond([ 'status' => 'failed', 'code' => 404, 'data' => 'No incentive file found.' ], 404); } $resolvedFileId = (int) $fileRow['id']; } // ── 4. Build Grid Query ─────────────────────────────────────────────── $builder = $this->PayoutGridModel->builder(); $builder->where('partner_agent_incentive_file_id', $resolvedFileId); $builder->orderBy('vehicle_type', 'ASC')->orderBy('insurer', 'ASC'); // Apply filters for every role on the same resolved file. if ($insurer !== '') { $builder->where('insurer', $insurer); } if ($rto !== '') { $builder->where('rto', $rto); } if ($segment !== '') { $builder->where('segment', $segment); } if ($vehicle_type !== '') { $builder->where('vehicle_type', $vehicle_type); } if ($search !== '') { $builder->groupStart() ->like('insurer', $search) ->orLike('vehicle_type', $search) ->orLike('segment', $search) ->orLike('rto', $search) ->orLike('remarks', $search) ->groupEnd(); } if (in_array($role, ['Manager', 'Accounts'], true)) { $builder->select('id, insurer, vehicle_type, segment, rto, comp, tp, od, fuel, broker_name, remarks, broker_comp, broker_tp, broker_od, created_by, created_at, updated_by, updated_at'); } else { // Agent $builder->select('id, insurer, vehicle_type, segment, rto, comp, tp, od, fuel, broker_name, remarks, broker_comp, broker_tp, broker_od, partner_agent_incentive_file_id, created_at, updated_at', false); } // ── 5. Execute (once) ───────────────────────────────────────────────── $gridResults = $builder->get()->getResultArray(); if (empty($gridResults)) { return $this->respond([ 'status' => 'failed', 'code' => 404, 'data' => 'No data found for the given file_id.' ], 404); } // ── 6. Role-based post-processing ───────────────────────────────────── if (in_array($role, ['Manager', 'Accounts'], true)) { $gridResults = PartnerPayoutGridRetention::applyManagerAccountsDisplayToRows($gridResults); } if (strtolower(trim((string) $role)) === 'agent') { $agentId = (int) trim((string) ($request->getGet('logged_id') ?? $request->getGet('agent_id') ?? 0)); if ($agentId > 0) { $gridResults = PartnerPayoutGridRetention::applyToRows($gridResults, $agentId, $db); } else { foreach ($gridResults as &$r) { $r['oa'] = $r['comp'] ?? null; $r['ob'] = $r['tp'] ?? null; $r['oc'] = $r['od'] ?? null; $r['partner_VT'] = '-'; $r['partner_RR'] = '-'; $r['comp'] = '-'; $r['tp'] = '-'; $r['od'] = '-'; } unset($r); } } // ── 7. Respond ──────────────────────────────────────────────────────── return $this->respond([ 'status' => 'success', 'code' => 200, 'data' => [ 'id' => $resolvedFileId, 'grid' => $gridResults, 'rtos' => $this->getUniqueColumnValues('rto', $resolvedFileId), 'segments' => $this->getUniqueColumnValues('segment', $resolvedFileId), 'vehicle_types' => $this->getUniqueColumnValues('vehicle_type', $resolvedFileId), 'insurers' => $this->getUniqueColumnValues('insurer', $resolvedFileId), ] ], 200); } catch (\Exception $e) { return $this->respond([ 'status' => 'failed', 'code' => 500, 'data' => $e->getMessage() ], 500); } } /** * Helper function to fetch unique non-empty values for a column */ private function getUniqueColumnValues($column, ?int $fileId = null) { $builder = $this->PayoutGridModel->select($column) ->distinct() ->where("$column IS NOT NULL") ->where("$column !=", '') ->orderBy($column, 'ASC'); if (!empty($fileId)) { $builder->where('partner_agent_incentive_file_id', (int) $fileId); } $results = $builder->findAll(); return array_column($results, $column); } // ------------------------------------------------------------------------- // Update grid row by id // Route: POST /agent/updateGrid // ------------------------------------------------------------------------- public function updateGrid() { try { $jsonData = (array) ($this->request->getJSON(true) ?? []); $postData = $this->request->getPost() ?? []; $data = !empty($jsonData) ? $jsonData : $postData; $id = isset($data['id']) ? (int) $data['id'] : 0; if ($id <= 0) { return $this->respond([ 'status' => 'failed', 'code' => 400, 'data' => 'id is required.', ], 200); } $existing = $this->PayoutGridModel->find($id); if (empty($existing)) { return $this->respond([ 'status' => 'failed', 'code' => 404, 'data' => 'Grid record not found.', ], 200); } $updateData = [ 'vehicle_type' => array_key_exists('vehicle_type', $data) ? $data['vehicle_type'] : null, 'fuel' => array_key_exists('fuel', $data) ? $data['fuel'] : null, 'rto' => array_key_exists('rto', $data) ? $data['rto'] : null, 'segment' => array_key_exists('segment', $data) ? $data['segment'] : null, 'broker_name' => array_key_exists('broker_name', $data) ? $data['broker_name'] : null, 'comp' => array_key_exists('comp', $data) ? $data['comp'] : null, 'tp' => array_key_exists('tp', $data) ? $data['tp'] : null, 'od' => array_key_exists('od', $data) ? $data['od'] : null, 'broker_comp' => array_key_exists('broker_comp', $data) ? $data['broker_comp'] : null, 'broker_tp' => array_key_exists('broker_tp', $data) ? $data['broker_tp'] : null, 'broker_od' => array_key_exists('broker_od', $data) ? $data['broker_od'] : null, 'remarks' => array_key_exists('remarks', $data) ? $data['remarks'] : null, ]; $hasAnyField = false; foreach (array_keys($updateData) as $field) { if (array_key_exists($field, $data)) { $hasAnyField = true; break; } } if (!$hasAnyField) { return $this->respond([ 'status' => 'failed', 'code' => 400, 'data' => 'No update fields provided.', ], 200); } if (array_key_exists('updated_by', $data)) { $updateData['updated_by'] = $data['updated_by']; } $this->PayoutGridModel->updateGridById($id, $updateData); $updated = $this->PayoutGridModel->find($id); return $this->respond([ 'status' => 'success', 'code' => 200, 'data' => $updated, ], 200); } catch (\Exception $e) { return $this->respond([ 'status' => 'failed', 'code' => 500, 'data' => $e->getMessage(), ], 500); } } // ------------------------------------------------------------------------- // 2. Download Filtered Grid in Excel // Route: $routes->get('agent/downloadGrid', 'AgentIncentiveController::downloadGridInExcel'); // Purpose: Applies the exact same filters as getGridData/(:num)(), but instead // of returning JSON, it generates and downloads an Excel file. // ------------------------------------------------------------------------- public function downloadGridInExcel() { try { $request = $this->request; // Get GET parameters for filtering $role = $request->getGet('role'); $insurer = $request->getGet('insurer'); $rto = $request->getGet('rto'); $segment = $request->getGet('segment'); $vehicle_type = $request->getGet('vehicle_type'); $builder = $this->PayoutGridModel->builder(); $builder->orderBy('vehicle_type', 'ASC')->orderBy('insurer', 'ASC'); // Apply filters if the user is a Manager or Accounts role if (in_array($role, ['Manager', 'Accounts'])) { if (!empty($insurer)) $builder->where('insurer', $insurer); if (!empty($rto)) $builder->where('rto', $rto); if (!empty($segment)) $builder->where('segment', $segment); if (!empty($vehicle_type)) $builder->where('vehicle_type', $vehicle_type); } // Fetch the filtered records $records = $builder->get()->getResultArray(); if (empty($records)) { return $this->respond([ 'status' => 'failed', 'code' => 404, 'data' => 'No records found to export based on your filters.' ], 404); } // --- Start Excel Generation --- $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); // Setup Header Row $sheet->setCellValue('A1', 'Vehicle Type'); $sheet->setCellValue('B1', 'Insurer'); $sheet->setCellValue('C1', 'RTO'); $sheet->setCellValue('D1', 'Segment'); $sheet->setCellValue('E1', 'Broker Name'); // Changed from 'ID' for clarity $sheet->setCellValue('F1', 'Comp'); $sheet->setCellValue('G1', 'TP'); $sheet->setCellValue('H1', 'OD'); // Populate Excel Rows $rowNumber = 2; // Start on row 2 (row 1 is headers) foreach ($records as $row) { $sheet->setCellValue('A' . $rowNumber, $row['vehicle_type']); $sheet->setCellValue('B' . $rowNumber, $row['insurer']); $sheet->setCellValue('C' . $rowNumber, $row['rto']); $sheet->setCellValue('D' . $rowNumber, $row['segment']); $sheet->setCellValue('E' . $rowNumber, $row['broker_name']); $sheet->setCellValue('F' . $rowNumber, $row['comp']); $sheet->setCellValue('G' . $rowNumber, $row['tp']); $sheet->setCellValue('H' . $rowNumber, $row['od']); $rowNumber++; } // Auto-size columns for better readability foreach (range('A', $sheet->getHighestColumn()) as $columnID) { $sheet->getColumnDimension($columnID)->setAutoSize(true); } // Write the file to a temporary location $writer = new Xlsx($spreadsheet); $fileName = 'Filtered_Payout_Grid_' . date('Y-m-d_H-i') . '.xlsx'; $tempFile = tempnam(sys_get_temp_dir(), 'grid_export'); $writer->save($tempFile); // Return the file as a direct download response return $this->response->download($tempFile, null)->setFileName($fileName); } catch (\Exception $e) { return $this->respond([ 'status' => 'failed', 'code' => 500, 'data' => 'Failed to generate Excel file: ' . $e->getMessage() ], 500); } } }