db = db_connect(); $this->AgentModel = new AgentModel(); $this->AgentIncentiveFileModel = new AgentIncentiveFileModel(); $this->PartnerRetentionRateModel = new PartnerRetentionRateModel(); } /** * @return list */ protected function parseRetentionRatesFromRequest(): array { $raw = $this->request->getPost('retention_rates'); if ($raw === null || $raw === '') { return []; } if (is_string($raw)) { $decoded = json_decode($raw, true); } else { $decoded = $raw; } if (!is_array($decoded)) { return []; } $out = []; foreach ($decoded as $row) { if (!is_array($row)) { continue; } $vtId = $row['vehicle_type_id'] ?? $row['vehicleTypeId'] ?? null; if ($vtId === null || $vtId === '') { continue; } $segmentId = $row['segment_id'] ?? $row['segmentId'] ?? null; if ($segmentId === null || $segmentId === '') { continue; } $rateRaw = $row['retention_rate'] ?? $row['retentionRate'] ?? null; if ($rateRaw === null || $rateRaw === '') { continue; } $rate = (float) $rateRaw; if ($rate < 0 || $rate > 100) { continue; } $out[] = [ 'vehicle_type_id' => (int) $vtId, 'segment_id' => (int) $segmentId, 'retention_rate' => round($rate, 2), ]; } return $out; } /** * Replace retention rows for an agent from the parsed list (main Save). */ protected function syncRetentionRatesForAgent(int $agentId, array $rates, ?int $userId): void { $this->PartnerRetentionRateModel->where('agent_id', $agentId)->delete(); $now = date('Y-m-d H:i:s'); foreach ($rates as $r) { $this->PartnerRetentionRateModel->insert([ 'agent_id' => $agentId, 'vehicle_type_id' => $r['vehicle_type_id'], 'segment_id' => $r['segment_id'], 'retention_rate' => $r['retention_rate'], 'is_active' => 1, 'created_by' => $userId, 'created_on' => $now, 'updated_by' => null, 'updated_on' => null, ]); } } /** Active vehicle types for partner retention UI */ public function partnerVehicleTypeList() { try { $data = $this->db->table('partner_segment ps') ->select('vt.id as vehicle_type_id, vt.vehicle_type, ps.id as segment_id, ps.segment') ->join('vehicle_type vt', 'vt.id = ps.vehicle_type_id', 'inner') ->where('ps.is_active', 1) ->where('vt.is_active', 1) ->orderBy('vt.vehicle_type', 'ASC') ->orderBy('ps.segment', 'ASC') ->get() ->getResultArray(); return $this->respond(['status' => 'success', 'code' => 200, 'data' => $data], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } /** * Upsert one vehicle-type retention row (inline edit on agent details). */ public function updateAgentVehicleRetention() { try { $data = $this->request->getJSON(true); if (!is_array($data) || $data === []) { $raw = $this->request->getRawInput(); if (is_array($raw) && $raw !== []) { $data = $raw; } elseif (is_string($raw) && $raw !== '') { $decoded = json_decode($raw, true); if (is_array($decoded)) { $data = $decoded; } } } if (!is_array($data) || $data === []) { $data = $this->request->getPost(); } if (!is_array($data)) { $data = []; } if (!isset($data['agent_id'], $data['vehicle_type_id'], $data['segment_id'], $data['retention_rate'])) { return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'agent_id, vehicle_type_id, segment_id and retention_rate are required'], 200); } $agentId = (int) $data['agent_id']; $vtId = (int) $data['vehicle_type_id']; $segmentId = (int) $data['segment_id']; $rate = (float) $data['retention_rate']; if ($rate < 0 || $rate > 100) { return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'retention_rate must be between 0 and 100'], 200); } $agent = $this->AgentModel->find($agentId); if (!$agent) { return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'Agent not found'], 200); } $updatedBy = isset($data['updated_by']) ? (int) $data['updated_by'] : null; $now = date('Y-m-d H:i:s'); $existing = $this->PartnerRetentionRateModel ->where('agent_id', $agentId) ->where('vehicle_type_id', $vtId) ->where('segment_id', $segmentId) ->first(); if ($existing) { $this->PartnerRetentionRateModel->update((int) $existing['id'], [ 'retention_rate' => round($rate, 2), 'is_active' => 1, 'updated_by' => $updatedBy, 'updated_on' => $now, ]); } else { $this->PartnerRetentionRateModel->insert([ 'agent_id' => $agentId, 'vehicle_type_id' => $vtId, 'segment_id' => $segmentId, 'retention_rate' => round($rate, 2), 'is_active' => 1, 'created_by' => $updatedBy, 'created_on' => $now, ]); } return $this->respond(['status' => 'success', 'code' => 200, 'data' => []], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } /** * Bulk upsert retention rows from the retention table (Save all). * POST JSON or form: agent_id, updated_by, retention_rates (array of {vehicle_type_id, retention_rate}). * Skips invalid entries; empty retention_rates array returns an error. */ public function saveAgentRetentionRatesBulk() { try { $data = $this->request->getJSON(true); if (!is_array($data) || $data === []) { $raw = $this->request->getRawInput(); if (is_array($raw) && $raw !== []) { $data = $raw; } elseif (is_string($raw) && $raw !== '') { $decoded = json_decode($raw, true); if (is_array($decoded)) { $data = $decoded; } } } if (!is_array($data) || $data === []) { $data = $this->request->getPost(); } if (!is_array($data)) { $data = []; } if (!isset($data['agent_id'])) { return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'agent_id is required'], 200); } $agentId = (int) $data['agent_id']; $agent = $this->AgentModel->find($agentId); if (!$agent) { return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'Agent not found'], 200); } $ratesRaw = $data['retention_rates'] ?? null; if ($ratesRaw === null || $ratesRaw === '') { return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'retention_rates is required'], 200); } if (is_string($ratesRaw)) { $rates = json_decode($ratesRaw, true); } else { $rates = $ratesRaw; } if (!is_array($rates) || $rates === []) { return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'retention_rates must be a non-empty array'], 200); } $updatedBy = isset($data['updated_by']) ? (int) $data['updated_by'] : null; $now = date('Y-m-d H:i:s'); $saved = 0; foreach ($rates as $row) { if (!is_array($row)) { continue; } $vtId = $row['vehicle_type_id'] ?? $row['vehicleTypeId'] ?? null; if ($vtId === null || $vtId === '') { continue; } $segmentId = $row['segment_id'] ?? $row['segmentId'] ?? null; if ($segmentId === null || $segmentId === '') { continue; } $rateRaw = $row['retention_rate'] ?? $row['retentionRate'] ?? null; if ($rateRaw === null || $rateRaw === '') { continue; } $rate = (float) $rateRaw; if ($rate < 0 || $rate > 100) { continue; } $vtId = (int) $vtId; $segmentId = (int) $segmentId; $existing = $this->PartnerRetentionRateModel ->where('agent_id', $agentId) ->where('vehicle_type_id', $vtId) ->where('segment_id', $segmentId) ->first(); if ($existing) { $this->PartnerRetentionRateModel->update((int) $existing['id'], [ 'retention_rate' => round($rate, 2), 'is_active' => 1, 'updated_by' => $updatedBy, 'updated_on' => $now, ]); } else { $this->PartnerRetentionRateModel->insert([ 'agent_id' => $agentId, 'vehicle_type_id' => $vtId, 'segment_id' => $segmentId, 'retention_rate' => round($rate, 2), 'is_active' => 1, 'created_by' => $updatedBy, 'created_on' => $now, ]); } $saved++; } if ($saved === 0) { return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'No valid retention rows to save'], 200); } return $this->respond([ 'status' => 'success', 'code' => 200, 'data' => ['saved_count' => $saved], ], 200); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500); } } // 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(); if ($data !== []) { $ids = array_column($data, 'id'); $db = db_connect(); $avgs = $db->table('partner_retention_rate') ->select('agent_id, ROUND(AVG(retention_rate), 2) AS avg_retention', false) ->where('is_active', 1) ->whereIn('agent_id', $ids) ->groupBy('agent_id') ->get() ->getResultArray(); $avgMap = []; foreach ($avgs as $a) { $avgMap[(int) $a['agent_id']] = $a['avg_retention']; } foreach ($data as &$row) { $aid = (int) $row['id']; if (isset($avgMap[$aid])) { $row['retention_rate'] = $avgMap[$aid]; } } unset($row); } 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); } $rates = $this->PartnerRetentionRateModel ->select('partner_retention_rate.id as retention_row_id, partner_retention_rate.vehicle_type_id, partner_retention_rate.segment_id, partner_retention_rate.retention_rate, vehicle_type.vehicle_type, partner_segment.segment') ->join('vehicle_type', 'vehicle_type.id = partner_retention_rate.vehicle_type_id', 'left') ->join('partner_segment', 'partner_segment.id = partner_retention_rate.segment_id', 'left') ->where('partner_retention_rate.agent_id', (int) $id) ->where('partner_retention_rate.is_active', 1) ->orderBy('vehicle_type.vehicle_type', 'ASC') ->orderBy('partner_segment.segment', 'ASC') ->findAll(); $record['retention_by_vehicle'] = $rates; 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 $certificateFileName = storage_upload_if_valid( $this->request->getFile('certificate_file_name'), 'agent', 'certificate_file' ); $rates = $this->parseRetentionRatesFromRequest(); $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' => null, ]; $this->AgentModel->insert($insertData); $newId = (int) $this->AgentModel->getInsertID(); if ($rates !== [] && $newId > 0) { $createdBy = isset($data['created_by']) ? (int) $data['created_by'] : null; $this->syncRetentionRatesForAgent($newId, $rates, $createdBy); } return $this->respond(['status' => 'success', 'code' => 200, 'data' => ['agent_id' => $newId]], 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); } $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' => null, ]; // handle file uploads $certificateFileName = storage_upload_if_valid( $this->request->getFile('certificate_file_name'), 'agent', 'certificate_file' ); if ($certificateFileName !== null) { $updateData['certificate_file_name'] = $certificateFileName; } if (array_key_exists('retention_rates', $data)) { $rates = $this->parseRetentionRatesFromRequest(); $updatedBy = isset($data['updated_by']) ? (int) $data['updated_by'] : null; $this->syncRetentionRatesForAgent((int) $id, $rates, $updatedBy); } $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 || empty($fileRecord['certificate_file_name'])) { return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File not found'], 200); } $fileName = $fileRecord['certificate_file_name']; if (!storage_exists('agent', 'certificate_file', $fileName)) { return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File missing on server'], 200); } return storage_download('agent', 'certificate_file', $fileName); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'message' => $e->getMessage()], 500); } } // List of all agents incentive files public function agentIncentiveFileList() { try{ $agent_id = $this->request->getGet('agent_id'); $type = $this->request->getGet('type'); $fileType = (!empty($type) && $type === 'grid') ? 'grid' : 'incentive'; $data = $this->AgentIncentiveFileModel->where('agent_id',$agent_id)->where('is_active',1)->where('file_type', $fileType)->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(); //duplicate check (ignore soft-deleted rows) $duplicateData = $this->AgentIncentiveFileModel ->where('agent_id', $data['agent_id']) ->where('incentive_month', $data['incentive_month']) ->where('file_type', 'incentive') ->where('is_active', 1) ->first(); if(!empty($duplicateData)){ return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'Duplicate Entry.'], 200); } // handle file uploads $incentiveFileName = storage_upload_if_valid( $this->request->getFile('incentive_file_name'), 'agent', 'incentive_file' ); $insertData = [ 'agent_id' => $data['agent_id'], 'incentive_month' => $data['incentive_month'], 'incentive_file_name' => $incentiveFileName, '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); } } // Delete agent incentive file public function deleteAgentIncentiveFile() { try { $id = $this->request->getGet('id'); // check if agent exists $file = $this->AgentIncentiveFileModel->where('file_type','incentive')->find((int)$id); if (!$file) { return $this->respond(['status' => 'failed','code' => 200, 'data' => 'File not found'], 200); } // 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 { $id = $this->request->getGet('id'); $type = $this->request->getGet('type'); // 'incentive' or 'grid' if (!$id) { return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'ID is required'], 200); } $fileType = (!empty($type) && $type === 'grid') ? 'grid' : 'incentive'; // Fetch record from DB $fileRecord = $this->AgentIncentiveFileModel->where('is_active',1)->where('file_type',$fileType)->find((int)$id); if (!$fileRecord || empty($fileRecord['incentive_file_name'])) { return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File not found'], 200); } $fileName = $fileRecord['incentive_file_name']; if (!storage_exists('agent', 'incentive_file', $fileName)) { return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File missing on server'], 200); } return storage_download('agent', 'incentive_file', $fileName); } catch (\Exception $e) { return $this->respond(['status' => 'failed', 'code' => 500, 'message' => $e->getMessage()], 500); } } //Empty Commit }