AgentModel = new AgentModel(); $this->AgentIncentiveFileModel = new AgentIncentiveFileModel(); $this->PartnerGridDetailsModel = new PartnerGridDetailsModel(); } private function getAuthenticatedUserData(): ?object { $header = $this->request->getHeaderLine('Authorization'); if (!$header || !preg_match('/Bearer\s(\S+)/', $header, $matches)) { return null; } $decodedToken = validateJWT($matches[1]); if (!$decodedToken || !isset($decodedToken['data'])) { return null; } return $decodedToken['data']; } private function canManagePartnerGrid(?string $role): bool { return in_array((string) $role, ['1', '4'], true); } // List of all agents public function agentList() { try{ $id = $this->request->getGet('manager_id'); $data = $this->AgentModel->select('partner_agent.*,partner_sales_executive.name as sales_executive_name') ->join('partner_sales_executive', 'partner_sales_executive.id = partner_agent.sales_executive_id', 'left') ->where('partner_agent.manager_id' , $id ) ->findAll(); return $this->respond(['status' => 'success', 'code' => 200, 'data' => $data], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } public function agentListForEnquiryCreationDropdown() { try{ $id = $this->request->getGet('manager_id'); $data = $this->AgentModel->where('manager_id' , $id )->where('is_active',1)->findAll(); return $this->respond(['status' => 'success', 'code' => 200, 'data' => $data], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } // Find a agent public function findAgent() { try{ $id = $this->request->getGet('id'); $record = $this->AgentModel->find((int)$id); if (!$record) { return $this->respond(['status' => 'failed', 'code' => 404, 'data' => []], 200); } return $this->respond(['status' => 'success', 'code' => 200, 'data' => $record], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } // CREATE agent public function createAgent() { try{ $data = $this->request->getPost(); // handle file uploads $certificateFile = $this->request->getFile('certificate_file_name'); $certificateFileName = null; // certificate upload if ($certificateFile && $certificateFile->isValid()) { $uploadPath = WRITEPATH . 'uploads/agent/certificate_file/'; if (!is_dir($uploadPath)) { mkdir($uploadPath, 0777, true); } $certificateFileName = time() . '_' . $certificateFile->getRandomName(); $certificateFile->move($uploadPath, $certificateFileName); } $insertData = [ 'name' => $data['name'], 'email' => $data['email'], 'mobile' => $data['mobile'], 'agent_code' => $data['agent_code'], 'address' => $data['address'], 'manager_id' => $data['manager_id'], 'sales_executive_id' => $data['sales_executive_id'], 'certificate_file_name' => $certificateFileName, 'created_by' => $data['created_by'], 'retention_rate' => $data['retention_rate'] ?? 0 ]; $this->AgentModel->insert($insertData); return $this->respond(['status' => 'success', 'code' => 200, 'data' => []], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } // UPDATE agent public function updateAgent() { try{ $data = $this->request->getPost(); if (!isset($data['id'])) { return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'ID Required'], 200); } $id = $data['id']; // check if agent exists $agent = $this->AgentModel->find((int)$id); if (!$agent) { return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'Date Not Found'], 200); } // handle file uploads $certificateFile = $this->request->getFile('certificate_file_name'); $updateData = [ 'name' => $data['name'] ?? null, 'email' => $data['email'] ?? null, 'mobile' => $data['mobile'] ?? null, 'address' => $data['address'] ?? null, 'agent_code' => $data['agent_code'] ?? null, 'sales_executive_id' => $data['sales_executive_id'] ?? null, 'updated_by' => $data['updated_by'] ?? null, 'retention_rate' => $data['retention_rate'] ?? $agent['retention_rate'] ]; if ($certificateFile && $certificateFile->isValid()) { $uploadPath = WRITEPATH . 'uploads/agent/certificate_file/'; if (!is_dir($uploadPath)) { mkdir($uploadPath, 0777, true); } $certificateFileName = time() . '_' . $certificateFile->getRandomName(); $certificateFile->move($uploadPath, $certificateFileName); $updateData['certificate_file_name'] = $certificateFileName; } $this->AgentModel->update($id, $updateData); return $this->respond(['status' => 'success', 'code' => 200, 'data' => []], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } //update device token public function updateDeviceToken() { try{ $data = $this->request->getPost(); if (!isset($data['id'])) { return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'ID Required'], 200); } $id = $data['id']; // check if agent exists $agent = $this->AgentModel->find((int)$id); if (!$agent) { return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'Date Not Found'], 200); } $updateData = [ 'firebase_device_token' => $data['firebase_device_token'] ?? null, ]; $this->AgentModel->update($id, $updateData); return $this->respond(['status' => 'success', 'code' => 200, 'data' => []], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } // ACTIVATE / DEACTIVATE agent public function changeAgentStatus() { try { $data = $this->request->getJSON(true); // validate inputs if (!isset($data['id']) || !isset($data['is_active'])) { return $this->respond(['status' => 'failed', 'code' => 200,'data' => 'ID and Status are required'], 200); } $id = $data['id']; $is_active = (int) $data['is_active']; // 0 or 1 $status = ($is_active == 1) ? 0 : 1; // check if agent exists $agent = $this->AgentModel->find((int)$id); if (!$agent) { return $this->respond(['status' => 'failed','code' => 200, 'data' => 'Agent not found'], 200); } // update status $this->AgentModel->update($id, ['is_active' => $status]); return $this->respond([ 'status' => 'success', 'code' => 200,'data' => "Agent status updated"], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed','code' => 500,'data' => $e->getMessage()], 500); } } // Download agent certificate file public function downloadAgentCertificateFile() { try { $id = $this->request->getGet('agent_id'); if (!$id) { return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'ID is required'], 200); } // Fetch record from DB $fileRecord = $this->AgentModel->where('is_active',1)->find((int)$id); if (!$fileRecord) { return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File not found'], 200); } $filePath = WRITEPATH . 'uploads/agent/certificate_file/' . $fileRecord['certificate_file_name']; if (!file_exists($filePath)) { return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File missing on server'], 200); } // Force file download return $this->response->download($filePath, null); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'message' => $e->getMessage()], 500); } } // List of all agents incentive files public function agentIncentiveFileList() { try{ $authUser = $this->getAuthenticatedUserData(); if (!$authUser) { return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401); } $roleId = (string) ($authUser->role_id ?? 'agent'); $agentIdFromRequest = $this->request->getGet('agent_id'); $fileType = trim((string) ($this->request->getGet('file_type') ?? '')); // Partner can only view own files; manager/accounts can view all. $agentId = $this->canManagePartnerGrid($roleId) ? $agentIdFromRequest : ($authUser->id ?? null); if (empty($agentId)) { return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'agent_id is required'], 200); } $builder = $this->AgentIncentiveFileModel ->where('agent_id', (int) $agentId) ->where('is_active', 1); if ($fileType !== '') { $builder->where('file_type', $fileType); } $data = $builder->orderBy('id', 'DESC')->findAll(); return $this->respond(['status' => 'success', 'code' => 200, 'data' => $data], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } // Upload agent incentive file public function uploadAgentIncentiveFile() { try{ $data = $this->request->getPost(); $authUser = $this->getAuthenticatedUserData(); if (!$authUser) { return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401); } $roleId = (string) ($authUser->role_id ?? 'agent'); $isGridUpload = isset($data['file_type']) && $data['file_type'] === 'grid'; if ($isGridUpload && !$this->canManagePartnerGrid($roleId)) { return $this->respond([ 'status' => 'failed', 'code' => 403, 'data' => 'Only Manager and Accounts can upload/edit partner grid files' ], 403); } if ($isGridUpload) { $this->uploadGridFile($data); } else { $duplicateData = $this->AgentIncentiveFileModel ->where('agent_id', $data['agent_id']) ->where('incentive_month', $data['incentive_month']) ->first(); if (!empty($duplicateData)) { return $this->respond([ 'status' => 'failed', 'code' => 200, 'data' => 'Duplicate Entry.' ], 200); } } // handle file uploads $incentiveFile = $this->request->getFile('incentive_file_name'); $incentiveFileName = null; // incentive upload if ($incentiveFile && $incentiveFile->isValid()) { $uploadPath = WRITEPATH . 'uploads/agent/incentive_file/'; if (!is_dir($uploadPath)) { mkdir($uploadPath, 0777, true); } $incentiveFileName = time() . '_' . $incentiveFile->getRandomName(); $incentiveFile->move($uploadPath, $incentiveFileName); } $insertData = [ 'agent_id' => $data['agent_id'], 'incentive_month' => $data['incentive_month'], 'incentive_file_name' => $incentiveFileName, 'file_type' => $data['file_type'] ?? 'incentive', 'created_by' => $data['created_by'] ?? null ]; $this->AgentIncentiveFileModel->insert($insertData); return $this->respond(['status' => 'success', 'code' => 200, 'data' => []], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } // ───────────────────────────────────────── // Grid Functionality // ───────────────────────────────────────── public function uploadGridFile($data){ $gridFile = $this->request->getFile('incentive_file_name'); if (!$gridFile || !$gridFile->isValid()) { throw new \RuntimeException('Valid grid file is required'); } $extension = strtolower((string) $gridFile->getExtension()); if (!in_array($extension, ['xlsx', 'xls', 'csv'], true)) { throw new \RuntimeException('Only xlsx, xls, csv grid files are allowed'); } $spreadsheet = IOFactory::load($gridFile->getTempName()); $rows = $spreadsheet->getActiveSheet()->toArray(null, true, true, false); if (empty($rows)) { throw new \RuntimeException('Grid file is empty'); } $normalize = static function ($value): string { $value = strtolower(trim((string) $value)); return preg_replace('/[^a-z0-9]+/', '', $value) ?? ''; }; $toNumber = static function ($value): float { if (is_numeric($value)) { return (float) $value; } if (preg_match('/-?\d+(\.\d+)?/', (string) $value, $matches)) { return (float) $matches[0]; } return 0.0; }; $toNullable = static function ($value): ?string { $value = trim((string) $value); return $value === '' ? null : $value; }; $formatNumber = static function (float $value): string { return rtrim(rtrim(number_format($value, 2, '.', ''), '0'), '.'); }; $parsePremium = static function ($value) use ($formatNumber): ?string { $source = trim((string) $value); if ($source === '') { return null; } if (preg_match('/-?\d+(?:\.\d+)?/', $source, $matches) !== 1) { return null; } return $formatNumber((float) $matches[0]); }; // Fixed format: first row is header and data starts from row 2. $headerRowIndex = 0; $headerMap = []; $headerRow = $rows[$headerRowIndex] ?? []; foreach ($headerRow as $colIndex => $cell) { $normalizedHeader = $normalize($cell); if ($normalizedHeader === 'type') { $headerMap['vehicle_type_id'] = $colIndex; } elseif ($normalizedHeader === 'insurer') { $headerMap['insurer_id'] = $colIndex; } elseif ($normalizedHeader === 'rto') { $headerMap['rto_id'] = $colIndex; } elseif ($normalizedHeader === 'segment') { $headerMap['segment_id'] = $colIndex; } elseif ($normalizedHeader === 'comp') { $headerMap['comp'] = $colIndex; } elseif ($normalizedHeader === 'tp') { $headerMap['tp'] = $colIndex; } elseif ($normalizedHeader === 'fuel') { $headerMap['fuel'] = $colIndex; } elseif ($normalizedHeader === 'remarks') { $headerMap['remarks'] = $colIndex; } } if (!isset($headerMap['insurer_id'], $headerMap['rto_id'], $headerMap['segment_id'], $headerMap['vehicle_type_id'])) { throw new \RuntimeException('Invalid grid header. Required: TYPE, INSURER, RTO, SEGMENT'); } $defaultPartnerId = !empty($data['agent_id']) ? (int) $data['agent_id'] : null; $createdBy = $data['created_by'] ?? null; $db = \Config\Database::connect(); $vehicleTypeMaster = $db->table('vehicle_type')->select('id, vehicle_type')->where('is_active', 1)->get()->getResultArray(); $insurerMaster = $db->table('insurers')->select('id, name, short_name')->where('is_active', 1)->get()->getResultArray(); $rtoMaster = $db->table('rto_master')->select('id, rto_code, rto_name')->where('is_active', 1)->get()->getResultArray(); $partnerMaster = $db->table('partner_agent')->select('id, name, agent_code, retention_rate')->where('is_active', 1)->get()->getResultArray(); $insurerMap = []; foreach ($insurerMaster as $item) { $insurerMap[$normalize($item['name'])] = (int) $item['id']; $insurerMap[$normalize($item['short_name'])] = (int) $item['id']; $insurerMap[(string) $item['id']] = (int) $item['id']; } $rtoMap = []; foreach ($rtoMaster as $item) { $rtoMap[$normalize($item['rto_code'])] = (int) $item['id']; $rtoMap[$normalize($item['rto_name'])] = (int) $item['id']; $rtoMap[(string) $item['id']] = (int) $item['id']; } $partnerMap = []; $partnerRetentionMap = []; foreach ($partnerMaster as $item) { $id = (int) $item['id']; $partnerMap[$normalize($item['name'])] = $id; $partnerMap[$normalize($item['agent_code'])] = $id; $partnerMap[(string) $id] = $id; $partnerRetentionMap[$id] = $toNumber($item['retention_rate'] ?? 0); } $insertCount = 0; $updateCount = 0; $skipStats = [ 'empty_row' => 0, 'missing_required_columns' => 0, 'master_mapping_failed' => 0, 'partner_not_found' => 0, ]; $skipSamples = []; for ($i = $headerRowIndex + 1, $count = count($rows); $i < $count; $i++) { $row = $rows[$i]; $hasAnyData = false; foreach ($row as $cellValue) { if (trim((string) $cellValue) !== '') { $hasAnyData = true; break; } } if (!$hasAnyData) { $skipStats['empty_row']++; continue; } $insurerRaw = trim((string) ($row[$headerMap['insurer_id']] ?? '')); $rtoRaw = trim((string) ($row[$headerMap['rto_id']] ?? '')); $segmentRaw = trim((string) ($row[$headerMap['segment_id']] ?? '')); $vehicleTypeRaw = trim((string) ($row[$headerMap['vehicle_type_id']] ?? $segmentRaw)); if ($insurerRaw === '' || $rtoRaw === '' || $segmentRaw === '' || $vehicleTypeRaw === '') { $skipStats['missing_required_columns']++; if (count($skipSamples) < 15) { $skipSamples[] = [ 'row' => $i + 1, 'reason' => 'missing_required_columns', 'insurer' => $insurerRaw, 'rto' => $rtoRaw, 'segment' => $segmentRaw, 'vehicle_type' => $vehicleTypeRaw, ]; } continue; } $insurerId = $insurerMap[$normalize($insurerRaw)] ?? null; if ($insurerId === null && ctype_digit($insurerRaw) && isset($insurerMap[$insurerRaw])) { $insurerId = $insurerMap[$insurerRaw]; } $rtoId = $rtoMap[$normalize($rtoRaw)] ?? null; if ($rtoId === null && ctype_digit($rtoRaw) && isset($rtoMap[$rtoRaw])) { $rtoId = $rtoMap[$rtoRaw]; } if (empty($insurerId) || empty($rtoId)) { $skipStats['master_mapping_failed']++; if (count($skipSamples) < 15) { $skipSamples[] = [ 'row' => $i + 1, 'reason' => 'master_mapping_failed', 'insurer' => $insurerRaw, 'rto' => $rtoRaw, 'segment' => $segmentRaw, 'vehicle_type' => $vehicleTypeRaw, ]; } continue; } $comp = trim((string) ($row[$headerMap['comp']] ?? '')); $tp = trim((string) ($row[$headerMap['tp']] ?? '')); $remarks = trim((string) ($row[$headerMap['remarks']] ?? '')); $fuelRaw = trim((string) ($row[$headerMap['fuel']] ?? '')); $fuel = $toNullable($fuelRaw); $partnerRaw = trim((string) ($row[$headerMap['partner_id']] ?? '')); $partnerId = null; if ($partnerRaw !== '') { $partnerId = $partnerMap[$normalize($partnerRaw)] ?? null; if ($partnerId === null && ctype_digit($partnerRaw) && isset($partnerMap[$partnerRaw])) { $partnerId = $partnerMap[$partnerRaw]; } } if (empty($partnerId) && !empty($defaultPartnerId)) { $partnerId = $defaultPartnerId; } if (empty($partnerId)) { $skipStats['partner_not_found']++; if (count($skipSamples) < 15) { $skipSamples[] = [ 'row' => $i + 1, 'reason' => 'partner_not_found', 'partner' => $partnerRaw, ]; } continue; } $retentionRate = $partnerRetentionMap[(int) $partnerId] ?? 0.0; $compSanitized = $parsePremium($comp); $tpSanitized = $parsePremium($tp); $partnerComp = $compSanitized !== null ? $formatNumber($retentionRate + (float) $compSanitized) : null; $partnerTp = $tpSanitized !== null ? $formatNumber($retentionRate + (float) $tpSanitized) : null; $recordData = [ 'vehicle_type_id' => $vehicleTypeRaw, 'insurer_id' => (string) $insurerId, 'rto_id' => (string) $rtoId, 'segment_id' => $segmentRaw, 'comp' => $compSanitized, 'tp' => $tpSanitized, 'fuel' => $fuel, 'remarks' => $toNullable($remarks), 'partner_id' => (string) $partnerId, 'partner_comp' => $partnerComp, 'partner_tp' => $partnerTp, ]; $existing = $this->PartnerGridDetailsModel ->where('vehicle_type_id', $vehicleTypeRaw) ->where('insurer_id', (string) $insurerId) ->where('rto_id', (string) $rtoId) ->where('segment_id', $segmentRaw) ->where('partner_id', (string) $partnerId) ->first(); if ($existing) { $recordData['updated_by'] = $createdBy; $this->PartnerGridDetailsModel->update((int) $existing['id'], $recordData); $updateCount++; } else { $recordData['created_by'] = $createdBy; $this->PartnerGridDetailsModel->insert($recordData); $insertCount++; } } if ($insertCount === 0 && $updateCount === 0) { $debugData = [ 'message' => 'No valid rows found in grid file', 'header_row' => $headerRowIndex + 1, 'detected_headers' => array_keys($headerMap), 'skip_stats' => $skipStats, 'skip_samples' => $skipSamples, ]; throw new \RuntimeException(json_encode($debugData, JSON_UNESCAPED_SLASHES)); } return true; } // Delete agent incentive file public function deleteAgentIncentiveFile() { try { $authUser = $this->getAuthenticatedUserData(); if (!$authUser) { return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401); } $roleId = (string) ($authUser->role_id ?? 'agent'); $id = $this->request->getGet('id'); // check if agent exists $file = $this->AgentIncentiveFileModel->find((int)$id); if (!$file) { return $this->respond(['status' => 'failed','code' => 200, 'data' => 'File not found'], 200); } if (($file['file_type'] ?? '') === 'grid' && !$this->canManagePartnerGrid($roleId)) { return $this->respond([ 'status' => 'failed', 'code' => 403, 'data' => 'Only Manager and Accounts can edit/delete partner grid files' ], 403); } // update status $this->AgentIncentiveFileModel->update($id, ['is_active' => 0]); return $this->respond([ 'status' => 'success', 'code' => 200,'data' => "File status updated"], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed','code' => 500,'data' => $e->getMessage()], 500); } } // Download agent incentive file public function downloadAgentIncentiveFile() { try { $authUser = $this->getAuthenticatedUserData(); if (!$authUser) { return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401); } $roleId = (string) ($authUser->role_id ?? 'agent'); $id = $this->request->getGet('id'); if (!$id) { return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'ID is required'], 200); } // Fetch record from DB $fileRecord = $this->AgentIncentiveFileModel->where('is_active',1)->find((int)$id); if (!$fileRecord) { return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File not found'], 200); } if (!$this->canManagePartnerGrid($roleId) && (int) $fileRecord['agent_id'] !== (int) ($authUser->id ?? 0)) { return $this->respond(['status' => 'failed', 'code' => 403, 'data' => 'Access denied'], 403); } $filePath = WRITEPATH . 'uploads/agent/incentive_file/' . $fileRecord['incentive_file_name']; if (!file_exists($filePath)) { return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File missing on server'], 200); } // Force file download return $this->response->download($filePath, null); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'message' => $e->getMessage()], 500); } } // Download sample partner grid excel public function downloadSamplePartnerGridExcel() { try { $filePath = WRITEPATH . 'uploads/sample_partner_grid_file.xlsx'; if (!file_exists($filePath)) { return $this->respond([ 'status' => 'failed', 'code' => 404, 'data' => 'Sample partner grid file not found on server', ], 200); } return $this->response->download($filePath, null); } catch (\Exception $e) { return $this->respond([ 'status' => 'failed', 'code' => 500, 'data' => $e->getMessage(), ], 500); } } // Monthly commission grid filters public function monthlyCommissionGridFilters() { try { $authUser = $this->getAuthenticatedUserData(); if (!$authUser) { return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401); } $db = \Config\Database::connect(); $rtoMaster = $db->table('rto_master') ->select('id, rto_code, rto_name') ->where('is_active', 1) ->orderBy('rto_code', 'ASC') ->get() ->getResultArray(); $segmentMaster = $db->table('vehicle_type') ->select('id, vehicle_type') ->where('is_active', 1) ->orderBy('vehicle_type', 'ASC') ->get() ->getResultArray(); $planMaster = $db->table('partner_insurance_plan_type_master') ->select('id, insurance_plan_type') ->where('is_active', 1) ->orderBy('insurance_plan_type', 'ASC') ->get() ->getResultArray(); $partnerMaster = $db->table('partner_agent') ->select('id, name, agent_code, retention_rate') ->where('is_active', 1) ->orderBy('name', 'ASC') ->get() ->getResultArray(); $monthRows = $db->table('partner_grid_details') ->select("DATE_FORMAT(created_at, '%Y-%m') AS month_key") ->where('created_at IS NOT NULL', null, false) ->groupBy("DATE_FORMAT(created_at, '%Y-%m')") ->orderBy('month_key', 'DESC') ->get() ->getResultArray(); $months = array_values(array_filter(array_map(static function ($row) { return $row['month_key'] ?? null; }, $monthRows))); $latestMonth = !empty($months) ? $months[0] : date('Y-m'); return $this->respond([ 'status' => 'success', 'code' => 200, 'data' => [ 'rto_master' => $rtoMaster, 'segment_master' => $segmentMaster, 'plan_master' => $planMaster, 'partner_master' => $partnerMaster, 'months' => $months, 'default_month' => $latestMonth, ], ], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } // Monthly commission grid list public function monthlyCommissionGridList() { try { $authUser = $this->getAuthenticatedUserData(); if (!$authUser) { return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401); } $partnerId = (int) ($this->request->getGet('partner_id') ?? 0); $rtoId = (int) ($this->request->getGet('rto_id') ?? 0); $segmentId = (int) ($this->request->getGet('segment_id') ?? 0); $planType = trim((string) ($this->request->getGet('insurance_plan_type') ?? '')); $month = trim((string) ($this->request->getGet('month') ?? '')); if ($partnerId <= 0 || $rtoId <= 0 || $segmentId <= 0 || $planType === '') { return $this->respond([ 'status' => 'failed', 'code' => 200, 'data' => 'partner_id, rto_id, segment_id and insurance_plan_type are required', ], 200); } $normalizedPlan = strtolower($planType); $db = \Config\Database::connect(); $latestRow = $db->table('partner_grid_details') ->select("DATE_FORMAT(created_at, '%Y-%m') AS month_key") ->where('created_at IS NOT NULL', null, false) ->orderBy('created_at', 'DESC') ->get(1) ->getRowArray(); $effectiveMonth = $month !== '' ? $month : (($latestRow['month_key'] ?? '') ?: date('Y-m')); $rows = $db->table('partner_grid_details pgd') ->select(" pgd.id, pgd.partner_id, pa.name AS partner_name, pa.agent_code, COALESCE(pa.retention_rate, 0) AS retention_rate, pgd.rto_id, rm.rto_code, rm.rto_name, pgd.segment, pgd.vehicle_type_id, vt.vehicle_type AS vehicle_type_name, pgd.comp, pgd.tp, pgd.fuel, pgd.remarks, DATE_FORMAT(pgd.created_at, '%Y-%m') AS month_key ") ->join('partner_agent pa', 'pa.id = pgd.partner_id', 'left') ->join('rto_master rm', 'rm.id = pgd.rto_id', 'left') ->join('vehicle_type vt', 'vt.id = pgd.vehicle_type_id', 'left') ->where('pgd.partner_id', $partnerId) ->where('pgd.rto_id', $rtoId) ->where('pgd.segment_id', $segmentId) ->where("DATE_FORMAT(pgd.created_at, '%Y-%m') =", $effectiveMonth) ->orderBy('pgd.id', 'DESC') ->get() ->getResultArray(); $toPercent = static function ($value): float { if ($value === null) { return 0.0; } if (is_numeric($value)) { return (float) $value; } if (preg_match('/-?\d+(\.\d+)?/', (string) $value, $matches)) { return (float) $matches[0]; } return 0.0; }; $result = []; foreach ($rows as $row) { $retention = $toPercent($row['retention_rate'] ?? 0); $compRate = $toPercent($row['comp'] ?? 0); $tpRate = $toPercent($row['tp'] ?? 0); if (strpos($normalizedPlan, 'third') !== false || $normalizedPlan === 'tp') { $gridRate = $tpRate; } else { // Comprehensive + Own Damage use COMP column. $gridRate = $compRate; } $netRate = $gridRate - $retention; $row['insurance_plan_type'] = $planType; $row['month'] = $effectiveMonth; $row['grid_rate_percentage'] = round($gridRate, 2); $row['retention_rate_percentage'] = round($retention, 2); $row['final_commission_percentage'] = round($netRate, 2); $result[] = $row; } return $this->respond([ 'status' => 'success', 'code' => 200, 'data' => $result, 'meta' => [ 'month' => $effectiveMonth, 'insurance_plan_type' => $planType, ], ], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } // Monthly commission grid download public function downloadMonthlyCommissionGrid() { try { $authUser = $this->getAuthenticatedUserData(); if (!$authUser) { return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401); } $partnerId = (int) ($this->request->getGet('partner_id') ?? 0); $rtoId = (int) ($this->request->getGet('rto_id') ?? 0); $segmentId = (int) ($this->request->getGet('segment_id') ?? 0); $planType = trim((string) ($this->request->getGet('insurance_plan_type') ?? '')); $month = trim((string) ($this->request->getGet('month') ?? '')); if ($partnerId <= 0 || $rtoId <= 0 || $segmentId <= 0 || $planType === '') { return $this->respond([ 'status' => 'failed', 'code' => 200, 'data' => 'partner_id, rto_id, segment_id and insurance_plan_type are required', ], 200); } $normalizedPlan = strtolower($planType); $db = \Config\Database::connect(); $latestRow = $db->table('partner_grid_details') ->select("DATE_FORMAT(created_at, '%Y-%m') AS month_key") ->where('created_at IS NOT NULL', null, false) ->orderBy('created_at', 'DESC') ->get(1) ->getRowArray(); $effectiveMonth = $month !== '' ? $month : (($latestRow['month_key'] ?? '') ?: date('Y-m')); $sourceRows = $db->table('partner_grid_details pgd') ->select(" pa.name AS partner_name, pa.agent_code, COALESCE(pa.retention_rate, 0) AS retention_rate, rm.rto_code, rm.rto_name, vt.vehicle_type AS segment_name, pgd.comp, pgd.tp ") ->join('partner_agent pa', 'pa.id = pgd.partner_id', 'left') ->join('rto_master rm', 'rm.id = pgd.rto_id', 'left') ->join('vehicle_type vt', 'vt.id = pgd.segment_id', 'left') ->where('pgd.partner_id', $partnerId) ->where('pgd.rto_id', $rtoId) ->where('pgd.segment_id', $segmentId) ->where("DATE_FORMAT(pgd.created_at, '%Y-%m') =", $effectiveMonth) ->orderBy('pgd.id', 'DESC') ->get() ->getResultArray(); $toPercent = static function ($value): float { if ($value === null) { return 0.0; } if (is_numeric($value)) { return (float) $value; } if (preg_match('/-?\d+(\.\d+)?/', (string) $value, $matches)) { return (float) $matches[0]; } return 0.0; }; $rows = []; foreach ($sourceRows as $row) { $retention = $toPercent($row['retention_rate'] ?? 0); $compRate = $toPercent($row['comp'] ?? 0); $tpRate = $toPercent($row['tp'] ?? 0); $gridRate = (strpos($normalizedPlan, 'third') !== false || $normalizedPlan === 'tp') ? $tpRate : $compRate; $row['month'] = $effectiveMonth; $row['insurance_plan_type'] = $planType; $row['grid_rate_percentage'] = round($gridRate, 2); $row['retention_rate_percentage'] = round($retention, 2); $row['final_commission_percentage'] = round($gridRate - $retention, 2); $rows[] = $row; } if (empty($rows)) { return $this->respond([ 'status' => 'failed', 'code' => 404, 'data' => 'No data found for the selected filters', ], 200); } $safePlan = preg_replace('/[^a-zA-Z0-9]+/', '_', strtolower($planType)) ?: 'plan'; $fileName = "monthly_commission_{$effectiveMonth}_{$safePlan}.csv"; $tmpFile = WRITEPATH . 'uploads/temp/' . $fileName; if (!is_dir(dirname($tmpFile))) { mkdir(dirname($tmpFile), 0777, true); } $fp = fopen($tmpFile, 'w'); fputcsv($fp, [ 'Month', 'Partner', 'Partner Code', 'RTO', 'Segment', 'Insurance Plan', 'Grid %', 'Retention %', 'Final Commission %', ]); foreach ($rows as $row) { fputcsv($fp, [ $row['month'] ?? $effectiveMonth, $row['partner_name'] ?? '', $row['agent_code'] ?? '', trim((string) (($row['rto_code'] ?? '') . ' - ' . ($row['rto_name'] ?? '')), ' -'), $row['segment_name'] ?? '', $row['insurance_plan_type'] ?? $planType, $row['grid_rate_percentage'] ?? 0, $row['retention_rate_percentage'] ?? 0, $row['final_commission_percentage'] ?? 0, ]); } fclose($fp); return $this->response->download($tmpFile, null)->setFileName($fileName); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } }