diff --git a/app/Config/Routes.php b/app/Config/Routes.php index b0aded13..4764e7e1 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -55,7 +55,6 @@ $routes->post("add_advertise_image", "AppContentManagementController::add_advert $routes->post('remove_advertise_image', 'AppContentManagementController::remove_advertise_image'); $routes->get("add_image_index", "AppContentManagementController::add_image_index"); $routes->get("getAdvertiseImage/(:any)", "AppContentManagementController::getAdvertiseImage/$1"); -$routes->get("frontend_content", "AppContentManagementController::frontend_content"); $routes->get('showAdvertiseImage/(:any)', 'AppContentManagementController::showAdvertiseImage/$1'); @@ -169,6 +168,7 @@ $routes->group("/client", ["filter" => "authMVC"], function ($routes) { $routes->post("create_2", "ClientController::createClientKYCInfo_2"); $routes->post("edit_2", "ClientController::editClientKYCInfo_2"); $routes->post("delete_2", "ClientController::deleteClientKycDocs_2/$1"); + $routes->get("download_2/(:any)", "ClientController::downloadClientKycDocs_2/$1"); }); @@ -421,6 +421,7 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) { $routes->match(['get', 'post', 'delete'], 'nhanceBranchMaster', 'MasterController::nhanceBranchMaster'); $routes->match(['get', 'post', 'delete'], 'vehicleTypeMaster', 'MasterController::vehicleTypeMaster'); $routes->match(['get', 'post', 'delete'], 'rtoMaster', 'MasterController::rtoMaster'); + $routes->match(['get', 'post', 'delete'], 'partnerPOS', 'MasterController::partnerPOS'); $routes->get('checkDuplicateCdAccount', 'MasterController::checkDuplicateCdAccount'); $routes->get('proceedExcelFileDataValidation', 'EmployeeController::proceedExcelFileDataValidation'); $routes->get('checkTpaApiEnable', 'EmployeeRestController::checkTpaApiEnable'); @@ -647,7 +648,13 @@ $routes->group("employeeRest", ["filter" => ['appSignature' , 'authJWT']], funct $routes->get("ticketHistoryList", "ThzController::ticketHistoryList"); $routes->match(['get','post','put'], 'ticketType', 'ThzController::ticketType'); - + //this route for nhance website contact form (sales/service) + $routes->group("employeeRest", ['filter' => ['appSignature'] ], function ($routes) { + $routes->post("ticketSave", "ThzController::ticketSave"); + }); + + //API FAQ + $routes->match(['get','post','delete'], 'FAQ', 'AppContentManagementController::FAQ'); //claims $routes->post('initiateClaim',"EmployeeRestController::initiateClaim"); @@ -766,6 +773,7 @@ $routes->get('IRSubmission','MediAssistApiController::IRSubmission'); // API service $routes->post("ecardRequest", "ApiServiceController::ecardRequest"); $routes->post("getTPAID", "ApiServiceController::getTPAID"); +// $routes->get("getTPAID", "ApiServiceController::getTPAID"); $routes->get('fileDownload','MediAssistApiController::fileDownload'); @@ -776,7 +784,7 @@ $routes->get('testTracelog','TestBusinessController::a'); $routes->get("claimView", "EmployeeRestController::claimView"); // General Tickets -$routes->post("ticketSave", "ThzController::ticketSave"); +$routes->post("ticketSave", "ThzController::ticketSave"); // $routes->get("ticketList", "ThzController::ticketList"); $routes->post("ticketConversationSave", "ThzController::ticketConversationSave"); $routes->get("ticketConversationList", "ThzController::ticketConversationList"); @@ -784,6 +792,10 @@ $routes->post('ticketAutoFetchDetails',"ThzController::ticketAutoFetchDetails"); $routes->match(['get','post','put'], 'ticketType', 'ThzController::ticketType'); $routes->get("ticketHistoryList", "ThzController::ticketHistoryList"); +// General FAQ +$routes->match(['get','post','delete'], 'FAQ', 'AppContentManagementController::FAQ'); +$routes->match(['get','post','delete'], 'frontend_content', 'AppContentManagementController::frontend_content'); +$routes->get("getFEContent", "EmployeeRestController::getFEContent"); //for testing $routes->group('test', function($routes) { @@ -852,3 +864,89 @@ $routes->group('logs', function($routes) { $routes->get('delete/(:segment)', 'LogController::delete/$1'); $routes->get('clearAll', 'LogController::clearAll'); }); + +// ----------------------------------------------------------------------------------------------------------------- +$routes->group('sales', function($routes) { + + // ==================== LEAD ROUTES ==================== + + // Get all leads with filters + $routes->get('leads', 'SalesController::getLeads'); + + // Get lead statistics + $routes->get('leads/stats', 'SalesController::getLeadStats'); + + // Get single lead with complete details + $routes->get('leads/(:num)', 'SalesController::getLead/$1'); + + // Create new lead + $routes->post('leads', 'SalesController::createLead'); + + // Update lead + $routes->put('leads/(:num)', 'SalesController::updateLead/$1'); + + // Delete lead + $routes->delete('leads/(:num)', 'SalesController::deleteLead/$1'); + + // Get contacts for a lead + $routes->get('leads/(:num)/contacts', 'SalesController::getContacts/$1'); + + // Get activities for a lead + $routes->get('leads/(:num)/activities', 'SalesController::getLeadActivities/$1'); + + // Get activity timeline for a lead + $routes->get('leads/(:num)/timeline', 'SalesController::getActivityTimeline/$1'); + + // Get notes for a lead + $routes->get('leads/(:num)/notes', 'SalesController::getLeadNotes/$1'); + + // ==================== CONTACT PERSON ROUTES ==================== + + // Create contact person + $routes->post('contacts', 'SalesController::createContact'); + + // Update contact person + $routes->put('contacts/(:num)', 'SalesController::updateContact/$1'); + + // Delete contact person + $routes->delete('contacts/(:num)', 'SalesController::deleteContact/$1'); + + // Set primary contact + $routes->put('contacts/(:num)/set-primary', 'SalesController::setPrimaryContact/$1'); + + // ==================== ACTIVITY ROUTES ==================== + + // Get all activities with filters + $routes->get('activities', 'SalesController::getActivities'); + + // Get upcoming activities + $routes->get('activities/upcoming', 'SalesController::getUpcomingActivities'); + + // Get pending activities count + $routes->get('activities/pending-count', 'SalesController::getPendingCount'); + + // Create activity + $routes->post('activities', 'SalesController::createActivity'); + + // Update activity + $routes->put('activities/(:num)', 'SalesController::updateActivity/$1'); + + // Complete activity + $routes->post('activities/(:num)/complete', 'SalesController::completeActivity/$1'); + + // Delete activity + $routes->delete('activities/(:num)', 'SalesController::deleteActivity/$1'); + + // ==================== NOTE ROUTES ==================== + + // Create note + $routes->post('notes', 'SalesController::createNote'); + + // Update note + $routes->put('notes/(:num)', 'SalesController::updateNote/$1'); + + // Delete note + $routes->delete('notes/(:num)', 'SalesController::deleteNote/$1'); +}); + + diff --git a/app/Controllers/ApiServiceController.php b/app/Controllers/ApiServiceController.php index b22a1470..5f8f17e4 100644 --- a/app/Controllers/ApiServiceController.php +++ b/app/Controllers/ApiServiceController.php @@ -14,6 +14,8 @@ use App\Controllers\ICICILombardController; use App\Controllers\MediAssistApiController; use App\Models\BatchFileModel; use App\Models\FileModel; +use App\Helpers\TPADataCompareHelper; +use App\Helpers\TPADataCompareHelper2; class ApiServiceController extends BaseController { @@ -154,6 +156,19 @@ class ApiServiceController extends BaseController public function getTPAID() { + // helper('TPADataCompareHelper'); + // print_rr(json_decode(file_get_contents(WRITEPATH.'/tmp/medi.json'),true));die(); + // $report = TPADataCompareHelper::compareDbVsMediassist( + // $report = TPADataCompareHelper2::compareDbVsMediassistGrouped( + // json_decode(file_get_contents(WRITEPATH.'/tmp/db.json'),true), + // json_decode(file_get_contents(WRITEPATH.'/tmp/medi.json'),true) + // ); + // print_rr($report); + // die(); + + + + //START OF THE PROGRAM log_message('error', "getTPAID payloads :" . json_encode($this->request->getPost() ?? [])); $tpa_id = $this->request->getPost('tpa_id') ?? null; $policy_no = $this->request->getPost('policy_no') ?? null; diff --git a/app/Controllers/AppContentManagementController.php b/app/Controllers/AppContentManagementController.php index da65a7b1..33d592e9 100755 --- a/app/Controllers/AppContentManagementController.php +++ b/app/Controllers/AppContentManagementController.php @@ -11,6 +11,7 @@ use CodeIgniter\API\ResponseTrait; use App\Models\AddImgModel; use App\Models\FEContentModel; use App\Models\ClientModel; +use App\Models\FAQModel; class AppContentManagementController extends AdminController { @@ -19,6 +20,7 @@ class AppContentManagementController extends AdminController protected $addImgModel; protected $feContentModel; protected $clientModel; + protected $faqModel; public function __construct() @@ -27,6 +29,7 @@ class AppContentManagementController extends AdminController $this->addImgModel = new AddImgModel(); $this->feContentModel = new FEContentModel(); $this->clientModel = new ClientModel(); + $this->faqModel = new FAQModel(); } //listing public function add_image_index() @@ -133,27 +136,236 @@ class AppContentManagementController extends AdminController - // public function getAdvertiseImage($image_id){ - // $image_data = $this->addImgModel->select('name')->where(['id' => $image_id])->first(); - // if($image_data){ - // return $image_data['name']; - // }else{ - // return ; - // } - // } + public function frontend_content() + { + + $method = $this->request->getMethod(); + + if ($this->request->getMethod() === 'post') { + $id = $this->request->getPost('fe_id'); + $data = $this->request->getPost(); + unset($data['fe_id']); - // Front End Content Area - public function frontend_content(){ + foreach ($data as $k => $v) { + if ($v === '' || $v === null) {unset($data[$k]);} + } + + // INSERT / UPDATE + if (empty($id)) { + $status = $this->feContentModel->insert($data); + $text = "Created"; + } else { + $status = $this->feContentModel->update($id, $data); + $text = "Updated"; + } - $data['test'] = $this->feContentModel->findAll(); - $headerData['tab_name'] = 'Front-End Content'; - $headerData['page_name'] = 'Front-End Content'; + return $this->respond([ + 'status' => $status ? true : false, + 'message' => "Frontend Content $text " . ($status ? 'successfully' : 'failed'), + 'code' => $status ? 200 : 400, + 'data' => $data, + ] , $status ? 200 : 400 ); + } + elseif ($method === 'get') { - echo view('layout/header', $headerData); - echo view('frontend_content_list', $data); - echo view('layout/footer'); + $id = $this->request->getGet('fe_id') ?? null; + + if (!empty($id)) { + + $data['fe_list'] = $this->feContentModel->where('id', $id)->orderBy('id', 'DESC')->findAll(); + + if (!empty($data)) { + return $this->respond(['status' => true, 'code' => 200, 'data' => $data], 200); + } else { + return $this->respond(['status' => false, 'code' => 400, 'message' => 'No data found'], 200); + } + } + + $data['fe_list'] = $this->feContentModel->orderBy('id', 'DESC')->findAll(); + + return $this->loadLayout('frontend_content_list', ['data' => $data,'tab_name' => 'Front-End Content','page_name' => 'Front-End Content']); + + } elseif ($method === 'delete') { + + // $input = $this->request->getRawInput(); + $id = $this->request->getGet('fe_id'); // ✅ THIS + $id = $id ?? null; + + if (empty($id)) { + return $this->respond([ + 'status' => false, + 'code' => 404, + 'message' => 'No ID provided for deletion' + ], 200); + } + + $update_status = $this->feContentModel->where('id', $id)->set(['is_active' => 0])->update(); + + if ($update_status) { + return $this->respond([ + 'status' => true, + 'code' => 200, + 'message' => 'Data removed successfully', + 'fe_id' => $id + ], 200); + } else { + return $this->respond([ + 'status' => false, + 'code' => 400, + 'message' => 'Failed to remove data', + 'fe_id' => $id + ], 200); + } + } + } + + + public function FAQ() + { + $method = strtolower($this->request->getMethod()); + $returnType = strtolower($this->request->getGet('return_type') ?? 'api'); + $ref = ['timestamp' => date('Y-m-d H:i:s')]; + + try { + // --- 1. POST: CREATE OR UPDATE --- + if ($method === 'post') { + $id = $this->request->getPost('faq_id'); + $data = array_filter($this->request->getPost(), fn($v) => $v !== '' && $v !== null); + + if (empty($id)) { + $status = $this->faqModel->insert($data); + $msg = "Created"; + } else { + $status = $this->faqModel->update($id, $data); + $msg = "Updated"; + } + + // if ($returnType === 'web') { + // return redirect()->back()->with($status ? 'success' : 'error', "FAQ $msg " . ($status ? 'successfully' : 'failed')); + // } + + // return $this->response->setJSON([ + // ])->setStatusCode($result ? 200 : 400); + return $this->response->setJSON([ + 'status' => $status ? 'success' : 'error', + 'message' => "FAQ $msg " . ($status ? 'successfully' : 'failed'), + 'code' => $status ? 200 : 400, + 'data' => $data, + 'ref' => $ref + ])->setStatusCode($status ? 200 : 400); + } + + // --- 2. GET: FETCH LIST OR SINGLE --- + elseif ($method === 'get') { + + $id = $this->request->getGet('faq_id'); + + if (!empty($id)) { + if($returnType === 'web'){ + $row = $this->faqModel->find($id); + }else{ + $row = $this->faqModel->where('is_active', 1)->find($id); + } + + $data['faq_list'] = $row ? [$row] : []; + } else { + if($returnType === 'web'){ + $data['faq_list'] = $this->faqModel->orderBy('id', 'desc')->findAll(); + }else{ + $data['faq_list'] = $this->faqModel->where('is_active', 1)->orderBy('id', 'desc')->findAll(); + } + } + + if ($returnType === 'web') { + $data['tab_name'] = "FAQ"; + $data['page_name'] = "FAQ"; + // print_r($data);die; + return $this->loadLayout('faq_list', $data); + } + + // API Response Logic + if (empty($data['faq_list'])) { + return $this->response->setJSON([ + 'status' => $returnType === 'web' ? false : 'error', + 'message' => 'No data found', + 'code' => 404, + 'data' => [], + 'ref' => $ref + ])->setStatusCode(200); // Using 200 with error status is common for mobile apps to prevent crashes + } + + return $this->response->setJSON([ + 'status' => $returnType === 'web' ? true : 'success', + 'message' => 'Data retrieved', + 'code' => 200, + 'data' => $data, + 'ref' => $ref + ])->setStatusCode(200); + } + + // --- 3. DELETE: SOFT DELETE --- + elseif ($method === 'delete') { + $id = $this->request->getGet('faq_id'); + $status = (!empty($id)) ? $this->faqModel->update($id, ['is_active' => 0]) : false; + + return $this->response->setJSON([ + 'status' => $status ? ($returnType === 'web' ? true : 'success') : ($returnType === 'web' ? false : 'error'), + 'message' => $status ? 'Data removed successfully' : 'Failed to remove or ID missing', + 'code' => $status ? 200 : 400, + 'data' => [], + 'ref' => $ref + ])->setStatusCode( 200 ); + } + + } catch (\Throwable $e) { + $msg = $e->getMessage(); + return $this->response->setJSON([ + 'status' => $returnType === 'web' ? false : 'error', + 'code' => 500, + 'message' => $msg, + 'ref' => ['file' => $e->getFile(), 'line' => $e->getLine()] + ])->setStatusCode(500); + } } + // public function frontend_content_files() + // { + + // // 1. Define your physical system path + // define('UPLOAD_PATH', ROOTPATH . 'public/uploads/add_image_upload/'); + + // // 2. Define the public URL so the editor can display the image later + // // This is what the browser uses to see the image (e.g., https://site.com/uploads/...) + // // $publicUrl = "https://yourdomain.com/public/uploads/add_image_upload/"; + // $publicUrl = ROOTPATH . 'public/uploads/add_image_upload/'; + // // if (!is_dir($uploadPath)) mkdir($uploadPath, 0755, true); + + // header('Content-Type: application/json'); + + // if ($_FILES['files']) { + // // Ensure directory exists + // if (!is_dir(UPLOAD_PATH)) { + // mkdir(UPLOAD_PATH, 0775, true); + // } + + // $fileName = time() . '_' . basename($_FILES['files']['name'][0]); + // $fullPath = UPLOAD_PATH . $fileName; + + // if (move_uploaded_file($_FILES['files']['tmp_name'][0], $fullPath)) { + // echo json_encode([ + // "success" => true, + // "data" => [ + // "baseurl" => $publicUrl, + // "files" => [$fileName] + // ] + // ]); + // } else { + // echo json_encode(["success" => false, "error" => "Failed to move file to " . UPLOAD_PATH]); + // } + // } + + // } + } \ No newline at end of file diff --git a/app/Controllers/ClientController.php b/app/Controllers/ClientController.php index d5370b02..dea5763b 100755 --- a/app/Controllers/ClientController.php +++ b/app/Controllers/ClientController.php @@ -726,7 +726,7 @@ class ClientController extends AdminController if ($requestFrom == 'rest') { return $data; } - + // print_rr($data);die(); // Load the view for the new list page; echo view('layout/header', $headerData); echo view('view_deposit', $data); @@ -741,12 +741,14 @@ class ClientController extends AdminController $client_id = $this->request->getPost('client_id'); $insurer_id = $this->request->getPost('insurer_id'); $record_date = $this->request->getPost('record_date'); + $cd_ac_pk = $this->request->getPost('cd_ac_pk'); + $cd_ac_no = $this->request->getPost('cd_ac_no'); - $CD_Account_Number = $this->CDMasterModel - ->where('client_id', $client_id) - ->where('insurer_id', $insurer_id) - ->where('is_active', 1) - ->first(); + // $CD_Account_Number = $this->CDMasterModel + // ->where('client_id', $client_id) + // ->where('insurer_id', $insurer_id) + // ->where('is_active', 1) + // ->first(); if (!empty($record_date)) { @@ -762,8 +764,8 @@ class ClientController extends AdminController 'sub_type_id' => $this->request->getPost('sub_type_id'), 'client_id' => $this->request->getPost('client_id'), 'client_policy_id' => null, - 'cd_ac_no' => $CD_Account_Number['cd_ac_no'] ?? null, - 'cd_ac_pk' => $CD_Account_Number['id'] ?? null, + 'cd_ac_no' => $cd_ac_no ?? null, + 'cd_ac_pk' => $cd_ac_pk ?? null, 'endorsement_no' => null, 'insurer_id' => $this->request->getPost('insurer_id'), 'description' => $this->request->getPost('description'), @@ -1099,6 +1101,8 @@ class ClientController extends AdminController $kyc_id = $this->request->getPost('id'); $client_id = $this->request->getPost('client_id'); + // echo "KID".$kyc_id; + // echo "CID".$client_id; if (empty($kyc_id)) { return $this->respond(['status' => false, 'code' => 400, 'message' => 'Missing document ID.'], 200); @@ -1107,7 +1111,7 @@ class ClientController extends AdminController $updateData['is_active'] = $this->request->getPost('is_active'); $delete = $this->clientKYCDocsModel->update($kyc_id, $updateData); - + // $delete = 1; if ($delete) { $html = $this->generateKycSingleTable($client_id); $dropdown = $this->fetch_dropdown($client_id); @@ -1117,37 +1121,43 @@ class ClientController extends AdminController } } - public function fetch_dropdown($client_id){ - - $db = db_connect(); + public function fetch_dropdown($client_id) + { + $db = db_connect(); - - $submitted_ids_subquery = $db->table('client_kyc_documents ckd') - ->select('ckd.kyc_doc_type_id') - ->where('ckd.client_id', $client_id) - ->where('ckd.is_active', 1) - ->getCompiledSelect(); - - - $result = $db->table('kyc_docs kd') - ->select('kd.*') - ->join('clients c', 'kd.kyc_type_id = c.entity_type_id') - ->where('c.id', $client_id) - ->where("kd.kyc_type_id NOT IN ({$submitted_ids_subquery})") - ->groupBy('kd.id') - ->get() - ->getResultArray(); + // Already uploaded docs + $uploadedData = $db->table('client_kyc_documents') + ->select('kyc_doc_type_id') + ->where('client_id', $client_id) + ->where('is_active', 1) + ->get() + ->getResultArray(); - - $dropdown = ''; - $dropdown .= ''; + $excludedIds = array_column($uploadedData, 'kyc_doc_type_id'); + + // Available docs + $builder = $db->table('kyc_docs kd'); + $builder->select('kd.id, kd.file_name'); + $builder->join('clients c', 'kd.kyc_type_id = c.entity_type_id'); + $builder->where('c.id', $client_id); + $builder->where('kd.is_active', 1); + + if (!empty($excludedIds)) { + $builder->whereNotIn('kd.id', $excludedIds); + } + + $result = $builder->get()->getResultArray(); // ✅ ARRAY + + // Build dropdown + $dropdown = ''; + $dropdown .= ''; + + foreach ($result as $row) { + $dropdown .= ''; + } - foreach ($result as $row) { - $dropdown .= ''; - } - return $dropdown; } @@ -2388,13 +2398,27 @@ class ClientController extends AdminController - public function uploadVehicleFile() + public function uploadVehicleFile($params = null) { $this->myLogger->logme('error', 'uploadVehicleFile function called'); - $data = $this->request->getPost(); - $files = $this->request->getFiles(); - // $data['client_id'] = 12; - // $data['vehicle_id'] = 1; + + $client_id = null; + $vehicle_id = null; + $files = null; + $docs_name = null; + + if(empty($params) && $this->request){ + $data = $this->request->getPost(); + $files = $this->request->getFiles(); + $client_id = $data['client_id'] ?? null; + $vehicle_id = $data['vehicle_id'] ?? null; + $docs_name = $data['other_docs_name'] ?? null; + }else { + $client_id = $params['client_id'] ?? null; + $vehicle_id = $params['vehicle_id'] ?? null; + $docs_name = $params['other_docs_name'] ?? null; + $files = $params['file_data'] ?? null; + } // upload path $uploadFilePath = WRITEPATH . 'uploads/client_kyc_documents'; @@ -2402,20 +2426,21 @@ class ClientController extends AdminController $insertedDocs = []; // Check document names and files - if (isset($data['other_docs_name']) && isset($files['file_name'])) { - foreach ($data['other_docs_name'] as $key => $docName) { + if (!empty($docs_name) && !empty($files) && !empty($client_id) && !empty($vehicle_id)) { + + foreach ($docs_name as $key => $docName) { // Get the corresponding file for this document name $file = $files['file_name'][$key]; if (!empty($docName) && $file->isValid() && !$file->hasMoved()) { // Upload the file - $uploadedFileName = file_Upload($file, $uploadFilePath); + $uploadedFileName = file_Upload_for_lead($file, $uploadFilePath); if ($uploadedFileName) { // Prepare data for each document upload $docData = [ - 'client_id' => $data['client_id'], - 'vehicle_id' => $data['vehicle_id'], + 'client_id' => $client_id, + 'vehicle_id' => $vehicle_id, 'other_docs_name' => $docName, 'file_name' => $uploadedFileName, 'created_by' => get_session_userid(), @@ -2431,16 +2456,21 @@ class ClientController extends AdminController } } + if(!empty($params) && !$this->request){ + return true; + } + if (!empty($insertedDocs)) { // Fetch all documents for the client $vehicleDocs = $this->clientKYCDocsModel ->where('client_id', $data['client_id']) ->where('vehicle_id', $data['vehicle_id']) ->findAll(); - return $this->respond(['status' => true, 'code' => 200, 'vehicle_docs' => $vehicleDocs, 'inserted_docs' => $insertedDocs], 200); + return $this->respond(['status' => true, 'code' => 200, 'vehicle_docs' => $vehicleDocs, 'inserted_docs' => $insertedDocs, 'message' => 'File uploaded successfully'], 200); } else { return $this->respond(['status' => false, 'code' => 400, 'message' => 'No documents uploaded or inserted'], 200); } + } else { return $this->respond(['status' => false, 'code' => 400, 'message' => 'Invalid data submission'], 200); } @@ -2513,22 +2543,57 @@ class ClientController extends AdminController { - $result['ckdlist'] = db_connect()->table('client_kyc_documents ckd') - ->select("ckd.id,ckd.client_id,ckd.kyc_doc_type_id,ckd.file_name,kd.file_name AS kd_docs_name,ckd.other_docs_name,ckd.vehicle_id,ckd.is_active, - CASE - WHEN ckd.kyc_doc_type_id IS NULL - OR ckd.kyc_doc_type_id = 0 - OR ckd.kyc_doc_type_id = '' - THEN ckd.other_docs_name - ELSE kd.file_name - END AS ui_docs_name") - ->join('kyc_docs kd','ckd.kyc_doc_type_id = kd.kyc_type_id','left') - ->where('ckd.client_id',$client_id) - ->where('ckd.is_active',1) - ->groupBy('ckd.id') - ->get() - ->getResultArray(); + // $result['ckdlist'] = db_connect()->table('client_kyc_documents ckd') + // ->select("ckd.id,ckd.client_id,ckd.kyc_doc_type_id,ckd.file_name,kd.file_name AS kd_docs_name,ckd.other_docs_name,ckd.vehicle_id,ckd.is_active, + // CASE + // WHEN ckd.kyc_doc_type_id IS NULL + // OR ckd.kyc_doc_type_id = 0 + // OR ckd.kyc_doc_type_id = '' + // THEN ckd.other_docs_name + // ELSE kd.file_name + // END AS ui_docs_name") + // ->join('kyc_docs kd','ckd.kyc_doc_type_id = kd.id','left') + // ->where('ckd.client_id',$client_id) + // ->where('ckd.is_active',1) + // ->groupBy('ckd.id') + // ->get() + // ->getResultArray(); + + $builder = db_connect()->table('client_kyc_documents AS ckd'); + + // Select the standard fields + $builder->select(' + ckd.id, + ckd.client_id, + ckd.kyc_doc_type_id, + ckd.file_name, + ckd.other_docs_name, + ckd.vehicle_id, + ckd.is_active + '); + + // Add the CASE statement for ui_docs_name + // Using false as the second parameter tells CI4 not to escape the string + $builder->select(" + CASE + WHEN ckd.kyc_doc_type_id IS NULL OR ckd.kyc_doc_type_id = 0 OR ckd.kyc_doc_type_id = '' + THEN ckd.other_docs_name + ELSE kd.file_name + END AS ui_docs_name + ", false); + + // Join with kyc_docs + $builder->join('kyc_docs AS kd', 'ckd.kyc_doc_type_id = kd.id', 'left'); + + // Where clauses + $builder->where('ckd.client_id', $client_id); + $builder->where('ckd.is_active', 1); + + // Group By + $builder->groupBy('ckd.id'); + + $result['ckdlist'] = $builder->get()->getResultArray(); $result['client_id'] = $client_id; $table = view('client_kyc_single_table', $result); @@ -8334,6 +8399,32 @@ class ClientController extends AdminController return $default_templates_inserted ? true : false; } + // public function downloadClientKycDocs_2() + // { + + // $file = WRITEPATH . 'uploads/client_kyc_documents/' . $file_name; // Example file path + // if (file_exists($file)) { + // return $this->response->download($file, null)->setFileName($file_name); + // } else { + // return "File not found."; + // } + // } + public function downloadClientKycDocs_2($file_name = null) + { + if (!$file_name) { + return "No file specified."; + } + + // Use absolute path to your upload folder + $file = WRITEPATH . 'uploads/client_kyc_documents/' . $file_name; + + if (file_exists($file)) { + // download() takes the path as first param and null (or data) as second + return $this->response->download($file, null); + } else { + return "File not found at: " . $file; + } + } } diff --git a/app/Controllers/EmployeeRestController.php b/app/Controllers/EmployeeRestController.php index 2dad01de..bb64f69e 100755 --- a/app/Controllers/EmployeeRestController.php +++ b/app/Controllers/EmployeeRestController.php @@ -2844,7 +2844,7 @@ class EmployeeRestController extends AdminController $emp_reatail_policy_data = $this->getEmpRetailPolicy($retailUserData); $wellness_data = ['status' => 'failed','message' => 'Coming soon........!']; - return $this->respond(['status' => 'success', 'code' => 200, 'data' => [], 'emp_name' => $emp_reatail_policy_data[0]['insurerd_name'] ?? "", 'pre_policy_count' => 0, 'retail_policy_data' => $emp_reatail_policy_data, 'wellness_data' => $wellness_data], 200); + return $this->respond(['status' => 'success', 'code' => 200, 'data' => [], 'emp_name' => $emp_reatail_policy_data[0]['insurerd_name'] ?? "", 'pre_policy_count' => 0, 'emp_not_enrolled_count' => 0, 'retail_policy_data' => $emp_reatail_policy_data, 'wellness_data' => $wellness_data], 200); } } @@ -3164,7 +3164,7 @@ class EmployeeRestController extends AdminController { try { - $feContentData = $this->feContentModel->findAll(); + $feContentData = $this->feContentModel->where('is_active',1)->findAll(); if (count($feContentData) > 0) { return $this->respond(['status' => 'success', 'code' => 200, 'data' => $feContentData], 200); @@ -3828,14 +3828,19 @@ class EmployeeRestController extends AdminController } } - if ($pdf_exist_in_the_file && $tpa_claim_push == true) { + $count = $this->ticketMaster->where('id', $ticket_id)->where('is_active', 1)->where('tpa_claim_push_reference_no IS NULL')->countAllResults(); + log_message('error', 'Ticket validation | Ticket ID: ' . $ticket_id . ' | Matching active tickets with NULL TPA reference: ' . $count); + + if ($count > 0 && $pdf_exist_in_the_file && $tpa_claim_push == true) { // this call for TPA integration $apiServiceController = new ApiServiceController(); $apiServiceController->pushClaims($ticket_id); log_message('error', "pushClaims function called with Ticket ID: {$ticket_id}, In Employee Rest Controller"); } else { if($tpa_claim_push == false){ - log_message('error', 'Skip the TPA claim push'); + log_message('error', 'Skip the TPA claim push for IR DOCUMENTS'); + } else if($count == 0){ + log_message('error', 'TPA claim push skipped as the claim reference number is already generated.'); }else{ log_message('error', "Failed to call the pushClaims function in EmployeeRestController for Ticket ID: {$ticket_id}, because the .pdf file does not exist."); } @@ -4408,7 +4413,8 @@ class EmployeeRestController extends AdminController { $client = \Config\Services::curlrequest(); $url = env('PRE_ENROLLMENT_BASEURL') . $endPoint; - $response = $client->post($url, ['json' => $postData, 'http_errors' => false]); + $headers = [ 'App-Signature' => getenv('APP_SIGNATURE') ]; + $response = $client->post($url, ['json' => $postData,'headers' => $headers,'http_errors' => false]); // return json_decode($response->getBody(), true); return $response->getBody(); } diff --git a/app/Controllers/JobWorker.php b/app/Controllers/JobWorker.php index 43935749..c32da5e6 100755 --- a/app/Controllers/JobWorker.php +++ b/app/Controllers/JobWorker.php @@ -190,6 +190,9 @@ class JobWorker extends AdminController 'initiateWellnessOnboardJob' => [ 'type' => 'CC', // Handler Category 'handler' => 'App\Controllers\EmployeeController', + ], 'saveMediAssitAPIData' => [ + 'type' => 'CC', // Handler Category + 'handler' => 'App\Controllers\MediAssistApiController', ], ]; diff --git a/app/Controllers/MasterController.php b/app/Controllers/MasterController.php index fec5dc4e..2c243fee 100755 --- a/app/Controllers/MasterController.php +++ b/app/Controllers/MasterController.php @@ -38,6 +38,7 @@ use App\Models\SettingsModel; use App\Models\VehicleModel; use App\Models\VehicleTypeModel; use App\Models\RTOModel; +use App\Models\PartnerPosModel; use CodeIgniter\CLI\CLI; @@ -2320,4 +2321,148 @@ class MasterController extends AdminController } } -} \ No newline at end of file + public function partnerPOS() + { + $posModel = new PartnerPosModel(); + $method = $this->request->getMethod(); + + if ($this->request->getMethod() === 'post') { + + $id = $this->request->getPost('pk'); + $data = $this->request->getPost(); + + unset($data['pk']); + + try { + // Certificate + $certificate = $this->uploadPOSFile('certificate_file_name', 'pos_certificate_files'); + if ($certificate !== null) { $data['certificate_file_name'] = $certificate; } else { unset($data['certificate_file_name']); } + + // PAN file + $panFile = $this->uploadPOSFile('pan_file_name', 'pos_certificate_files'); + if ($panFile !== null) { $data['pan_file_name'] = $panFile; } else { unset($data['pan_file_name']);} + + // Aadhaar file + $aadharFile = $this->uploadPOSFile('aadhar_file_name', 'pos_certificate_files'); + if ($aadharFile !== null) { $data['aadhar_file_name'] = $aadharFile; } else { unset($data['aadhar_file_name']);} + + } catch (\RuntimeException $e) { + return $this->respond([ 'status' => false, 'code' => 400, 'message' => $e->getMessage(), 'data' => $data ], 400); + } + + foreach ($data as $k => $v) { + if ($v === '' || $v === null) { + unset($data[$k]); + } + } + + + // INSERT / UPDATE + if (empty($id)) { + $status = $posModel->insert($data); + } else { + $status = $posModel->update($id, $data); + } + + if ($status) { + return $this->respond([ 'status' => true, 'code' => 200, 'message' => 'POS updated successfully', 'data' => $data ], 200); + } + + return $this->respond([ 'status' => false, 'code' => 400, 'message' => 'Failed to update', 'data' => $data ], 400); + } + elseif ($method === 'get') { + + $id = $this->request->getGet('pk') ?? null; + + $db = db_connect(); + + $builder = $db->table('partner_staff'); + + $data['manager_list'] = $builder->select('partner_staff.id, partner_staff.name') + ->where('partner_staff.is_active', 1) + ->where('partner_staff.role_id', 1) + ->get() + ->getResultArray(); + + + if (!empty($id)) { + + $data['pos_list'] = $posModel + ->select('partner_pos.*, partner_staff.name AS manager_name') + ->join('partner_staff', 'partner_staff.id = partner_pos.manager_id', 'left') + ->where('partner_pos.id', $id) + ->orderBy('partner_pos.id', 'DESC') + ->findAll(); + + if (!empty($data)) { + return $this->respond(['status' => true, 'code' => 200, 'data' => $data], 200); + } else { + return $this->respond(['status' => false, 'code' => 400, 'message' => 'No data found'], 200); + } + } + + $data['pos_list'] = $posModel + ->select('partner_pos.*, ps.name AS manager_name') + ->join('partner_staff ps', 'ps.id = partner_pos.manager_id', 'left') + ->orderBy('partner_pos.id', 'DESC') + ->findAll(); + + + return $this->loadLayout('pos_list', ['data' => $data,'tab_name' => 'POS details','page_name' => 'POS details']); + + } elseif ($method === 'delete') { + + // $input = $this->request->getRawInput(); + $id = $this->request->getGet('pk'); // ✅ THIS + $id = $id ?? null; + + if (empty($id)) { + return $this->respond([ + 'status' => false, + 'code' => 404, + 'message' => 'No ID provided for deletion' + ], 200); + } + + $update_status = $posModel->where('id', $id)->set(['is_active' => 0])->update(); + + if ($update_status) { + return $this->respond([ + 'status' => true, + 'code' => 200, + 'message' => 'Data removed successfully', + 'pk' => $id + ], 200); + } else { + return $this->respond([ + 'status' => false, + 'code' => 400, + 'message' => 'Failed to remove data', + 'pk' => $id + ], 200); + } + } + } + + private function uploadPOSFile(string $fieldName, string $uploadDir) + { + $file = $this->request->getFile($fieldName); + + if (!$file || !$file->isValid()) { return null; } + + $allowedMime = [ 'image/jpg', 'image/jpeg', 'image/png', 'image/webp', 'application/pdf']; + + if (!in_array($file->getMimeType(), $allowedMime)) { throw new \RuntimeException('Invalid file format'); } + + $path = ROOTPATH . 'public/uploads/' . $uploadDir . '/'; + + if (!is_dir($path)) { mkdir($path, 0755, true); } + + $newName = $file->getRandomName(); + $file->move($path, $newName); + + return $newName; + } + + + } \ No newline at end of file diff --git a/app/Controllers/MediAssistApiController.php b/app/Controllers/MediAssistApiController.php index e293c57f..ba21f694 100644 --- a/app/Controllers/MediAssistApiController.php +++ b/app/Controllers/MediAssistApiController.php @@ -5,10 +5,12 @@ namespace App\Controllers; use App\Controllers\BaseController; use App\Models\BatchFileModel; use App\Models\EmployeePolicyModel; +use App\Models\TpaApiDataModel; use CodeIgniter\HTTP\ResponseInterface; use CodeIgniter\HTTP\IncomingRequest; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\API\ResponseTrait; +use App\Controllers\Jobs; class MediAssistApiController extends BaseController { @@ -203,8 +205,18 @@ class MediAssistApiController extends BaseController public function GetBenefDetails($requestData) { - helper('api'); + // helper('api'); + // helper('TPADataCompareHelper'); + // $report = EmployeeCompareHelper::compareDbVsMediassist( + // $dbRows, + // $mediassistAPIdata + // ); + // print_rr($report); + // die(); + // print_r($requestData);die(); + + //START OF THE PROGRAM $function_calling_type = $requestData['return_type'] ?? 'job'; try { @@ -266,7 +278,6 @@ class MediAssistApiController extends BaseController return $this->respond(['status' => false, 'message' => 'employeePolicyData not found']); } } - $allBenef = []; $startIndex = 0; $range = 100; @@ -326,6 +337,19 @@ class MediAssistApiController extends BaseController $startIndex += $range; } while ($startIndex < $totalCount); + //save API data as JSON for analysis + $json = json_encode($allBenef, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + // $filename = time() . '.json'; + $filePath = WRITEPATH . 'tmp/'.time().'_'.$requestData['file_id'].'.json'; + file_put_contents($filePath, $json); + + //call a job for dump JSON data to DB + $job_details = new Jobs(); + $r = Jobs::addJob(['job_name' => 'saveMediAssitAPIData', 'payload' => [ + 'file_id' => $requestData['file_id'], + 'json_file_path' => $filePath ]]); + + // now update DB $updated = 0; $employee_policy_ids = []; @@ -334,11 +358,10 @@ class MediAssistApiController extends BaseController $hasMatchForThisPolicy = false; foreach ($allBenef as $row) { - if ( strtolower(trim($policy_data['name'] ?? '')) == strtolower(trim($row['benefName'] ?? '')) && ($policy_data['emp_code'] ?? '') == ($row['priBenefEmpCode'] ?? '') && - strtolower(trim($policy_data['relationship'] ?? '')) == strtolower(trim($row['relName'] ?? '')) && + strtolower(trim($policy_data['relationship'] ?? '')) == strtolower(trim(str_replace('-', ' ', $row['relName'] ?? ''))) && ($policy_data['gender'] ?? '') == ($row['benefSex'] ?? '') && ($policy_data['dob'] ?? '') == (change_date_format($row['benefDOB'], 'd/m/Y H:i:s') ?? '') ) { @@ -721,6 +744,54 @@ class MediAssistApiController extends BaseController ]; } + public function saveMediAssitAPIData($array) + { + $file_id = $array['file_id']; + $json = file_get_contents($array['json_file_path']); + $records = json_decode($json, true); + // log_message('error','saveMediAssitAPIData' . json_encode($array));//die(); + $file_model = new BatchFileModel(); + $file_info = $file_model->where('id', $file_id)->find(); + // dd($file_info); + $tpaApiDataModel = new TpaApiDataModel(); + + // echo $file_id;die(); + //deactivate old data + $tpaApiDataModel->set('is_active', 0)->where('file_id', $file_id)->update(); + + //covert tpa data to our model data + $mappedRows = []; + + foreach ($records as $row) { + + $mappedRows[] = [ + 'file_id' => $file_id, // ← pass from controller + 'emp_code' => trim($row['priBenefEmpCode'] ?? ''), + + 'name' => trim($row['benefName'] ?? ''), + 'dob' => !empty($row['benefDOB']) + ? date('Y-m-d', strtotime(str_replace('/', '-', $row['benefDOB']))) + : null, + + 'relation' => trim($row['relName'] ?? null), + 'gender' => strtoupper($row['benefSex'] ?? null), + 'self' => strtolower($row['relName'] ?? '') === 'self' ? 1 : 0, + + 'tpa_id' => trim($row['benefMediAssistID'] ?? null), + 'age' => is_numeric($row['benefAge'] ?? null) + ? (int) $row['benefAge'] + : null, + + 'is_active' => 1, + 'created_by' => $file_info[0]['created_by'] ?? null, + ]; + } + // log_message('error','COUNT' . count($mappedRows)); + // print_rr($mappedRows);//die(); + $result = $tpaApiDataModel->insertBatchWithChunkLog($mappedRows,500,('FILE_ID_'.$file_id)); + // unlink($file_array['json_file_path']); // delete temp json file + } + diff --git a/app/Controllers/PayoutController.php b/app/Controllers/PayoutController.php index 01cd7251..90d501dc 100644 --- a/app/Controllers/PayoutController.php +++ b/app/Controllers/PayoutController.php @@ -51,12 +51,14 @@ class PayoutController extends BaseController $data = $this->request->getPost(); // print_r($data); die; - $agent_id = $data['agent_id'] ?? null; + // $agent_id = $data['agent_id'] ?? null; because dropdown hided + $pos_id = $data['pos_id'] ?? null; $status_id = $data['status_id'] ?? null; $start_date = $data['start_date'] ?? null; $end_date = $data['end_date'] ?? null; - $payout_data = $this->invoiceModel->invoiceList($agent_id, $status_id, $start_date, $end_date); + // $payout_data = $this->invoiceModel->invoiceList($agent_id, $status_id, $start_date, $end_date); // because dropdown hided + $payout_data = $this->invoiceModel->invoiceList($pos_id, $status_id, $start_date, $end_date); $payout_data['payout_list_data'] = $payout_data; $payout_data = view('payout_list', $payout_data); @@ -86,7 +88,8 @@ class PayoutController extends BaseController // for list $data['payout_status'] = $this->payout_status; - $data['agent_list'] = $this->invoiceModel->agentList(); + // $data['agent_list'] = $this->invoiceModel->agentList(); // because dropdown hided + $data['pos_list'] = $this->invoiceModel->posList(); $data['page_name'] = "Invoices"; $payout_data['payout_list_data'] = $this->invoiceModel->invoiceList(); $data['payout_list'] = view('payout_list', $payout_data); @@ -204,8 +207,9 @@ class PayoutController extends BaseController $title = $type === 'add' ? 'Add Payouts' : ($type === 'edit' ? 'Edit Payouts' + : ($type === 'view' ? 'View Payouts' : ($type === 'adjustment' ? 'Payouts Adjustment' - : 'Payouts')); + : 'Payouts'))); $data['tab_name'] = $title; $data['page_name'] = $title; @@ -215,10 +219,12 @@ class PayoutController extends BaseController if($type == 'add') { $data['agents'] = $this->invoiceModel->agentList(['is_active' => 1]); // common both add and edit + $data['pos_list'] = $this->invoiceModel->posList(['is_active' => 1]); // common both add and edit } else { $data['agents'] = $this->invoiceModel->agentList(); // common both add and edit + $data['pos_list'] = $this->invoiceModel->posList(); // common both add and edit } // $data['checked_policy_numbers'] = []; // $data['invoice'] = []; @@ -236,16 +242,21 @@ class PayoutController extends BaseController return $this->loadLayout('invoice_policy_mapping_add', $data); } - if (($type === 'edit' || $type === 'adjustment') && !empty($id)) { + if (($type === 'edit' || $type === 'view' || $type === 'adjustment') && !empty($id)) { $invoice = $this->invoiceModel->where('id', $id)->first(); + // print_r($invoice);die; $data['freeze_edit'] = $this->auditHistory->where('table_name', 'partner_invoice')->where('pk', $id)->countAllResults(); - + if($type === 'view'){ $data['freeze_edit'] = 1; } - $agentId = $invoice['agent_id'] ?? null; + // $agentId = $invoice['agent_id'] ?? null; + $posId = $invoice['pos_id'] ?? null; - $data['payouts'] = $this->invoiceModel->payoutList(2 ,$agentId,$id); - $data['extra_payouts'] = $agentId ? $this->invoiceModel->payoutList(3, $agentId) : []; + // $data['payouts'] = $this->invoiceModel->payoutList(2 ,$agentId,$id); + // $data['extra_payouts'] = $agentId ? $this->invoiceModel->payoutList(3, $agentId) : []; + $data['payouts'] = $this->invoiceModel->payoutList(2 ,$posId,$id); + // print_r($data['payouts']);die; + $data['extra_payouts'] = $posId ? $this->invoiceModel->payoutList(3, $posId) : []; $invoice_items = $this->invoiceItemModel->where('invoice_id', $id)->findAll(); @@ -439,7 +450,7 @@ class PayoutController extends BaseController $agentCode = $agent["agent_code"]; $today = date("Ymd"); - $likePattern = "INV/$agentCode/$today/%"; + $likePattern = "NIIB/$agentCode/$today/%"; $count = $this->invoiceModel ->like("invoice_no", $likePattern) @@ -447,7 +458,7 @@ class PayoutController extends BaseController $nextNumber = $count + 1; - return "INV/$agentCode/$today/$nextNumber"; + return "NIIB/$agentCode/$today/$nextNumber"; } // private function generateInvoiceNumber() @@ -518,6 +529,7 @@ class PayoutController extends BaseController // Get invoice data from database $invoiceData = $this->getInvoiceData($invoiceId); + // print_r($invoiceData);die; if (empty($invoiceData)) { return $this->respond([ @@ -591,6 +603,27 @@ class PayoutController extends BaseController } + // private function getInvoiceData($invoiceId) + // { + // $invoice_data = $this->invoiceModel + // ->select(' + // partner_invoice.*, + // pa.name as agent_name, + // pa.email as agent_email, + // pa.mobile as agent_mobile, + // pa.address as agent_address, + // pa.agent_code, + // pa.certificate_file_name, + // pa.commission_retain + // ') + // ->join('partner_agent pa', 'partner_invoice.agent_id = pa.id') + // ->where('partner_invoice.is_active', 1) + // ->where('partner_invoice.id', $invoiceId) + // ->first(); + + // return $invoice_data; + // } + private function getInvoiceData($invoiceId) { $invoice_data = $this->invoiceModel @@ -604,9 +637,20 @@ class PayoutController extends BaseController pa.address as agent_address, pa.agent_code, pa.certificate_file_name, - pa.commission_retain + pa.commission_retain, + pos.name as pos_name, + pos.email as pos_email, + pos.mobile as pos_mobile, + pos.address as pos_address, + pos.city as pos_city, + pos.pincode as pos_pincode, + pos.state as pos_state, + pos.pos_code, + pos.certificate_file_name as pos_certificate_file_name, + pos.bank_name,pos.account_holder_name,pos.account_number,pos.ifsc_code, ') - ->join('partner_agent pa', 'partner_invoice.agent_id = pa.id') + ->join('partner_agent pa', 'partner_invoice.agent_id = pa.id','left') + ->join('partner_pos pos', 'partner_invoice.pos_id = pos.id') ->where('partner_invoice.is_active', 1) ->where('partner_invoice.id', $invoiceId) ->first(); diff --git a/app/Controllers/PolicyTransactionController.php b/app/Controllers/PolicyTransactionController.php index 66798ee5..4f2fcb49 100644 --- a/app/Controllers/PolicyTransactionController.php +++ b/app/Controllers/PolicyTransactionController.php @@ -669,14 +669,43 @@ ->where('is_active', 1) ->findAll(); + // // Fetch Sales Team + // $data['salse_team'] = $this->userModel + // ->select('user_profiles.*,nhance_branch.id as nhance_branch_id, nhance_branch.branch_name as nhance_branch_name') + // ->join('user_teams', 'user_profiles.id = user_teams.user_id') + // ->join('nhance_branch', 'user_profiles.nhance_branch_id = nhance_branch.id','left') + // ->where('user_teams.team_id', 5) + // ->where('user_teams.is_active', 1) + // ->where('user_profiles.is_active', 1) + // ->findAll(); + + // // Fetch Partner Agent + // $data['partner_agent'] = db_connect()->table('partner_agent') + // ->where('is_active', 1) + // ->get() + // ->getResultArray(); + + // // Fetch ACM + // $data['ACM'] = $this->userModel + // ->where('role', 3) // Assuming `role` is in `user_profiles` + // ->where('is_active', 1) + // ->findAll(); + // Fetch Sales Team $data['salse_team'] = $this->userModel - ->select('user_profiles.*,nhance_branch.id as nhance_branch_id, nhance_branch.branch_name as nhance_branch_name') + ->select(' + user_profiles.*, + nhance_branch.id as nhance_branch_id, + nhance_branch.branch_name as nhance_branch_name, + rm.first_name AS rm_name + ') ->join('user_teams', 'user_profiles.id = user_teams.user_id') ->join('nhance_branch', 'user_profiles.nhance_branch_id = nhance_branch.id','left') + ->join('user_profiles AS rm', 'user_profiles.rm_id = rm.id', 'left') ->where('user_teams.team_id', 5) ->where('user_teams.is_active', 1) ->where('user_profiles.is_active', 1) + ->groupBy('user_profiles.id', 'asc') ->findAll(); // Fetch Partner Agent @@ -687,12 +716,20 @@ // Fetch ACM $data['ACM'] = $this->userModel - ->where('role', 3) // Assuming `role` is in `user_profiles` - ->where('is_active', 1) - ->findAll(); - // echo '
';
+ ->select('
+ user_profiles.*,
+ nhance_branch.id AS nhance_branch_id,
+ nhance_branch.branch_name AS nhance_branch_name,
+ rm.first_name AS rm_name
+ ')
+ ->join('user_teams', 'user_profiles.id = user_teams.user_id')
+ ->join('nhance_branch', 'user_profiles.nhance_branch_id = nhance_branch.id', 'left')
+ ->join('user_profiles AS rm', 'user_profiles.rm_id = rm.id', 'left')
+ ->where('user_profiles.role', 3)
+ ->where('user_profiles.is_active', 1)
+ ->groupBy('user_profiles.id', 'asc')
+ ->findAll();
- // print_r($data['ppTeamsData']); die;
// dd($data);
@@ -897,6 +934,12 @@
{
$data = $this->request->getPost();
+ $file_data = $this->request->getFiles() ?? null;
+ $data['file_data'] = $file_data ?? null;
+
+ $vehicle_file_data = $this->request->getFiles() ?? null;
+ $data['vehicle_file_data'] = $vehicle_file_data ?? null;
+
// print_r($data); die;
if (empty($data['policy_issue_date'])) {
@@ -1006,6 +1049,28 @@
}
}
+ // policy file upload
+ if(isset($data['doc_name']) && isset($data['file_data']) && !empty($data['doc_name']) && !empty($data['file_data'])){
+ $this->uploadFile($data['doc_name'], $data['file_data'], $insert);
+ }
+
+ // policy file upload
+ if(
+ isset($data['other_docs_name']) && isset($data['vehicle_file_data']) && isset($data['client_id']) && isset($data['vehicle_id']) &&
+ !empty($data['other_docs_name']) && !empty($data['vehicle_file_data']) && !empty($data['client_id']) && !empty($data['vehicle_id'])
+ )
+ {
+ $vdd = [
+ 'other_docs_name' => $data['other_docs_name'],
+ 'file_data' => $data['vehicle_file_data'],
+ 'client_id' => $data['client_id'],
+ 'vehicle_id' => $data['vehicle_id'],
+ ];
+ $clientController = new ClientController();
+ $clientController->uploadVehicleFile($vdd);
+ }
+
+
$client_data = $this->clientModel->where('id', $data['client_id'])->first();
$data['client_kyc'] = $this->clientKYCDocsModel->where('client_id', $data['client_id'])->findAll();
@@ -1917,6 +1982,7 @@
$data['client_kyc_primary_table'] = $clientController->generateKycPrimaryTable($data['client_id']);
$data['client_kyc_other_table'] = $clientController->generateKycOthersTable($data['client_id']);
$data['client_kyc_single_table'] = $clientController->generateKycSingleTable($data['client_id']);
+ $data['client_kyc_dd_data'] = $clientController->fetch_dropdown($data['client_id']);
$data['vehicle_docs'] = $this->clientKYCDocsModel
->where('client_id', $data['client_id'])
@@ -2833,20 +2899,23 @@
//------------------------------------------------------------------------------------------------
//file upload function
- public function uploadFile()
+ public function uploadFile($docs_name = null, $files = null, $pt_id = null)
{
$GoogleDriveController = new GoogleDriveController();
- $files = $this->request->getFiles();
- $doc_name = $this->request->getPost('doc_name[]');
- $pt_id = $this->request->getPost('pt_id');
- $client_policy_id = $this->request->getPost('client_policy_id');
- $data = $this->request->getPost();
+
+
+ if(empty($docs_name) && empty($files) && empty($pt_id) && $this->request){
+ $files = $this->request->getFiles();
+ $docs_name = $this->request->getPost('doc_name');
+ $pt_id = $this->request->getPost('pt_id');
+ $client_policy_id = $this->request->getPost('client_policy_id');
+ }
$uploadFilePath = WRITEPATH . 'uploads/client_kyc_documents';
$uploadData = [];
- foreach ($data['doc_name'] as $key => $docName) {
+ foreach ($docs_name as $key => $docName) {
// Get the corresponding file for this document name
$file = $files['file'][$key];
@@ -2854,7 +2923,7 @@
if (!empty($docName) && $file->isValid() && !$file->hasMoved()) {
// Upload the file
- $uploadedFileName = file_Upload($file, $uploadFilePath);
+ $uploadedFileName = file_Upload_for_lead($file, $uploadFilePath);
if ($uploadedFileName) {
// Prepare data for each document upload
@@ -2879,6 +2948,10 @@
}
}
+ if(!empty($docs_name) && !empty($files) && !empty($pt_id) && !$this->request){
+ return true;
+ }
+
// print_r($uploadData); die;
// $uploadData = uploadFilesToGoogleDrive($files['file'], $doc_name, $pt_id);
@@ -3669,6 +3742,103 @@
return array('status' => $ret_status, 'error_code' => $error_data['error_code'], 'error_data' => $error_data['error_data']);
}
+ public function validateInsurerStatementNew($params)
+ {
+ helper('excel_util_helper');
+
+ //get file info
+ $file_id = $params['file_id'];
+ $file = $this->insurerStatements->find($file_id);
+ // dd($file);
+
+ $date = new \DateTime($file['month']);
+
+ $month = $date->format('m');
+ $year = $date->format('Y');
+
+ $error_data = ['error_code' => '', 'error_data' => []];
+ $status = 'success';
+ $ret_status = true;
+
+ $return = [];
+ if (!isset($file)) {
+ //file not found in DB
+ return array('status' => false, 'msg' => 'statement file not found in DB');
+ }
+ $file_name_with_path = WRITEPATH . "/uploads/statements/" . $file['file_name'];
+
+ //check physical file
+ if (!file_exists($file_name_with_path)) {
+ //file not found update status and reason
+ $message = "Physcial file not found";
+ // echo $message;
+ $this->myLogger->logme('error', ($message . ' for statement file id ' . $file_id));
+ $this->insurerStatements->where('id', $file_id)->set(['file_status' => 'failed', 'reason' => json_encode(['error_code' => 0, 'error_data' => $message])])->update();
+ return array('status' => false, 'error_code' => 0); //0 - Physcial file not found
+ }
+
+ //get excel data to php array
+ $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file_name_with_path);
+ $sheet = $spreadsheet->getActiveSheet();
+
+ $highestRow = $sheet->getHighestRow();
+ $highestColumn = $sheet->getHighestColumn();
+
+ $excel_data = $sheet->rangeToArray('A1:' . $highestColumn . $highestRow, null, true, true, true);
+ unset($excel_data[0]);
+ $excel_data = ExcelSanitizeHelper::sanitizeArrayData($excel_data);
+
+ //get no of line items and update in DB
+ $line_items = 0;
+
+ // get uploaded month transactions data
+ $source_data = $this->PTCOShareDetailsModel->getNonReconcileredPolicyTransactions(insurer_id: $file['insurer_id'], insurer_branch_id: $file['branch_id']);
+
+ // check policy no,insurer and etc in DB for this month
+ // if all good return true, otherwise return false with messssage
+ foreach ($excel_data as $excel_key => $excel_row) {
+
+ $is_row_empty = check_row_is_empty_or_null($excel_row);
+
+ if (!$is_row_empty) {
+
+ $is_source_found = 0;
+
+ $policy_no = preg_replace('/^\p{Z}+|\p{Z}+$/u', '', $excel_row[1]); //policy_number from excel
+ $policy_no = preg_replace( '/[\x{200B}\x{200C}\x{200D}\x{FEFF}\x{00A0}\x{200E}\x{200F}\x{202A}-\x{202E}]/u', '', $excel_row[1]); //policy_number from excel
+
+ $endorsement_no = preg_replace('/^\p{Z}+|\p{Z}+$/u', '', $excel_row[2]??''); //endorsement number from excel
+ $endorsement_no = preg_replace( '/[\x{200B}\x{200C}\x{200D}\x{FEFF}\x{00A0}\x{200E}\x{200F}\x{202A}-\x{202E}]/u', '', $excel_row[2]??''); //endorsement number from excel
+
+ foreach ($source_data as $source_key => $source_row) {
+
+ $source_endorsement_no = $source_row['endorsement_no'] !== null ? $source_row['endorsement_no'] : null;
+
+ if (($policy_no == $source_row['policy_no']) && $endorsement_no == $source_endorsement_no) {
+ $is_source_found = 1;
+ $line_items = $line_items + 1;
+ unset($source_data[$source_key]);
+ continue 2;
+ }
+ }
+
+ if ($is_source_found == 0) {
+ $error_data['error_code'] = 1; //match not found
+ $error_data['error_data'] = array_merge($error_data['error_data'], [$excel_row[0]]); //match not found
+ }
+ }
+ }
+
+ if ($error_data['error_code']) {
+ $status = 'failed';
+ $ret_status = false;
+ }
+
+ //update in DB
+ $this->insurerStatements->where('id', $file_id)->set(['line_items' => $line_items, 'file_status' => $status, 'reason' => json_encode($error_data)])->update();
+ return array('status' => $ret_status, 'error_code' => $error_data['error_code'], 'error_data' => $error_data['error_data']);
+ }
+
public function updateInsurerStatement($params)
{
@@ -4960,7 +5130,7 @@
$policy_transaction_data = [
'issuer' => 2,
- 'issuer_branch' => $current_nhance_branch['id'] ?? null,
+ 'issuer_branch' => $salse['nhance_branch_id'] ?? null,
'client_id' => $client_id,
'policy_type_id' => 8,
'insurer_id' => $current_insurer_branch_data['insurer_id'] ?? null,
@@ -4980,10 +5150,12 @@
'month' => change_date_format($policy_issue_date, 'd/M/Y', 'Y-m-d'),
'sales_generated_by' => $salse['id'] ?? null,
'serviced_by' => $service['id'] ?? null,
+ 'salse_person_manager_id' => $salse['rm_id'] ?? null,
+ 'service_person_manager_id' => $service['rm_id'] ?? null,
+ 'service_person_branch_id' => $service['nhance_branch_id'] ?? null,
'agent_id' => $current_agent_data['id'] ?? null,
'agent_code' => $current_agent_data['agent_code'] ?? null,
'file_id' => $params['file_id'],
-
'endorsement_no' => null,
'client_branch_id' => null,
'client_policy_id' => null,
@@ -5282,11 +5454,14 @@
// -------------------------------------------
// 1. First Stage: Check if vehicle already exists
// -------------------------------------------
- foreach ($vehicle_data as $v) {
- if (strcasecmp(trim($v['vehicle_no']), $vehicle_number) === 0) {
- $vehicle_id = $v['id'];
- $client_id = $v['owner'];
- break;
+
+ if(strtolower(trim($vehicle_number)) != 'new'){
+ foreach ($vehicle_data as $v) {
+ if (strcasecmp(trim($v['vehicle_no']), $vehicle_number) === 0) {
+ $vehicle_id = $v['id'];
+ $client_id = $v['owner'];
+ break;
+ }
}
}
diff --git a/app/Controllers/SalesController.php b/app/Controllers/SalesController.php
new file mode 100644
index 00000000..17a60b0e
--- /dev/null
+++ b/app/Controllers/SalesController.php
@@ -0,0 +1,695 @@
+leadModel = new SalesActualLeadModel();
+ $this->contactModel = new SalesContactPersonModel();
+ $this->activityModel = new SalesActivityModel();
+ $this->noteModel = new SalesLeadNoteModel();
+ }
+
+ // ==================== LEAD APIs ====================
+
+ /**
+ * Get all leads with pagination and filters
+ * GET /api/sales/leads
+ */
+ public function getLeads()
+ {
+ try {
+ $limit = $this->request->getGet('limit') ?? 10;
+ $offset = $this->request->getGet('offset') ?? 0;
+
+ $filters = [
+ 'status' => $this->request->getGet('status'),
+ 'assigned_to' => $this->request->getGet('assigned_to'),
+ 'search' => $this->request->getGet('search'),
+ ];
+
+ $result = $this->leadModel->getLeadsWithFilters($filters, $limit, $offset);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'data' => $result['data'],
+ 'total' => $result['total'],
+ 'limit' => $limit,
+ 'offset' => $offset
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Get single lead with complete details
+ * GET /api/sales/leads/{id}
+ */
+ public function getLead($id)
+ {
+ try {
+ $lead = $this->leadModel->getLeadComplete($id);
+
+ if (!$lead) {
+ return $this->failNotFound('Lead not found');
+ }
+
+ return $this->respond([
+ 'status' => 'success',
+ 'data' => $lead
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Create new lead
+ * POST /api/sales/leads
+ */
+ public function createLead()
+ {
+ try {
+ $data = $this->request->getJSON(true);
+
+ // Set created_by and updated_by from authenticated user
+ $data['created_by'] = $this->getUserId();
+ $data['updated_by'] = $this->getUserId();
+
+ if (!$this->leadModel->insert($data)) {
+ return $this->fail($this->leadModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
+ }
+
+ $leadId = $this->leadModel->getInsertID();
+
+ // Insert contact persons if provided
+ if (!empty($data['contact_persons'])) {
+ foreach ($data['contact_persons'] as $contact) {
+ $contact['lead_id'] = $leadId;
+ $contact['created_by'] = $this->getUserId();
+ $this->contactModel->insert($contact);
+ }
+ }
+
+ $lead = $this->leadModel->getLeadComplete($leadId);
+
+ return $this->respondCreated([
+ 'status' => 'success',
+ 'message' => 'Lead created successfully',
+ 'data' => $lead
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Update lead
+ * PUT /api/sales/leads/{id}
+ */
+ public function updateLead($id)
+ {
+ try {
+ if (!$this->leadModel->find($id)) {
+ return $this->failNotFound('Lead not found');
+ }
+
+ $data = $this->request->getJSON(true);
+ $data['updated_by'] = $this->getUserId();
+
+ if (!$this->leadModel->update($id, $data)) {
+ return $this->fail($this->leadModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
+ }
+
+ $lead = $this->leadModel->getLeadComplete($id);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'message' => 'Lead updated successfully',
+ 'data' => $lead
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Delete lead
+ * DELETE /api/sales/leads/{id}
+ */
+ public function deleteLead($id)
+ {
+ try {
+ if (!$this->leadModel->find($id)) {
+ return $this->failNotFound('Lead not found');
+ }
+
+ $this->leadModel->delete($id);
+
+ return $this->respondDeleted([
+ 'status' => 'success',
+ 'message' => 'Lead deleted successfully'
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Get lead statistics
+ * GET /api/sales/leads/stats
+ */
+ public function getLeadStats()
+ {
+ try {
+ $userId = $this->request->getGet('user_id');
+ $stats = $this->leadModel->getLeadStats($userId);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'data' => $stats
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ // ==================== CONTACT PERSON APIs ====================
+
+ /**
+ * Get contacts for a lead
+ * GET /api/sales/leads/{leadId}/contacts
+ */
+ public function getContacts($leadId)
+ {
+ try {
+ $contacts = $this->contactModel->getContactsByLead($leadId);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'data' => $contacts
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Create contact person
+ * POST /api/sales/contacts
+ */
+ public function createContact()
+ {
+ try {
+ $data = $this->request->getJSON(true);
+ $data['created_by'] = $this->getUserId();
+ $data['updated_by'] = $this->getUserId();
+
+ if (!$this->contactModel->insert($data)) {
+ return $this->fail($this->contactModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
+ }
+
+ $contactId = $this->contactModel->getInsertID();
+ $contact = $this->contactModel->find($contactId);
+
+ return $this->respondCreated([
+ 'status' => 'success',
+ 'message' => 'Contact created successfully',
+ 'data' => $contact
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Update contact person
+ * PUT /api/sales/contacts/{id}
+ */
+ public function updateContact($id)
+ {
+ try {
+ if (!$this->contactModel->find($id)) {
+ return $this->failNotFound('Contact not found');
+ }
+
+ $data = $this->request->getJSON(true);
+ $data['updated_by'] = $this->getUserId();
+
+ if (!$this->contactModel->update($id, $data)) {
+ return $this->fail($this->contactModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
+ }
+
+ $contact = $this->contactModel->find($id);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'message' => 'Contact updated successfully',
+ 'data' => $contact
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Delete contact person
+ * DELETE /api/sales/contacts/{id}
+ */
+ public function deleteContact($id)
+ {
+ try {
+ if (!$this->contactModel->find($id)) {
+ return $this->failNotFound('Contact not found');
+ }
+
+ $this->contactModel->delete($id);
+
+ return $this->respondDeleted([
+ 'status' => 'success',
+ 'message' => 'Contact deleted successfully'
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Set primary contact
+ * PUT /api/sales/contacts/{id}/set-primary
+ */
+ public function setPrimaryContact($id)
+ {
+ try {
+ $contact = $this->contactModel->find($id);
+
+ if (!$contact) {
+ return $this->failNotFound('Contact not found');
+ }
+
+ $this->contactModel->setPrimaryContact($id, $contact['lead_id']);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'message' => 'Primary contact updated successfully'
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ // ==================== ACTIVITY APIs ====================
+
+ /**
+ * Get all activities with filters
+ * GET /api/sales/activities
+ */
+ public function getActivities()
+ {
+ try {
+ $limit = $this->request->getGet('limit') ?? 10;
+ $offset = $this->request->getGet('offset') ?? 0;
+
+ $filters = [
+ 'status' => $this->request->getGet('status'),
+ 'activity_type' => $this->request->getGet('activity_type'),
+ 'assigned_to' => $this->request->getGet('assigned_to'),
+ 'date_from' => $this->request->getGet('date_from'),
+ 'date_to' => $this->request->getGet('date_to'),
+ ];
+
+ $result = $this->activityModel->getActivitiesWithFilters($filters, $limit, $offset);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'data' => $result['data'],
+ 'total' => $result['total'],
+ 'limit' => $limit,
+ 'offset' => $offset
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Get activities for a specific lead
+ * GET /api/sales/leads/{leadId}/activities
+ */
+ public function getLeadActivities($leadId)
+ {
+ try {
+ $status = $this->request->getGet('status');
+ $activities = $this->activityModel->getActivitiesByLead($leadId, $status);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'data' => $activities
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Get activity timeline for a lead
+ * GET /api/sales/leads/{leadId}/timeline
+ */
+ public function getActivityTimeline($leadId)
+ {
+ try {
+ $timeline = $this->activityModel->getActivityTimeline($leadId);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'data' => $timeline
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Get upcoming activities for user
+ * GET /api/sales/activities/upcoming
+ */
+ public function getUpcomingActivities()
+ {
+ try {
+ $userId = $this->getUserId();
+ $days = $this->request->getGet('days') ?? 7;
+ $limit = $this->request->getGet('limit') ?? 10;
+
+ $activities = $this->activityModel->getUpcomingActivities($userId, $days, $limit);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'data' => $activities
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Create activity
+ * POST /api/sales/activities
+ */
+ public function createActivity()
+ {
+ try {
+ $data = $this->request->getJSON(true);
+ $data['created_by'] = $this->getUserId();
+ $data['updated_by'] = $this->getUserId();
+
+ if (!$this->activityModel->insert($data)) {
+ return $this->fail($this->activityModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
+ }
+
+ $activityId = $this->activityModel->getInsertID();
+ $activity = $this->activityModel->find($activityId);
+
+ return $this->respondCreated([
+ 'status' => 'success',
+ 'message' => 'Activity created successfully',
+ 'data' => $activity
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Update activity
+ * PUT /api/sales/activities/{id}
+ */
+ public function updateActivity($id)
+ {
+ try {
+ if (!$this->activityModel->find($id)) {
+ return $this->failNotFound('Activity not found');
+ }
+
+ $data = $this->request->getJSON(true);
+ $data['updated_by'] = $this->getUserId();
+
+ if (!$this->activityModel->update($id, $data)) {
+ return $this->fail($this->activityModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
+ }
+
+ $activity = $this->activityModel->find($id);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'message' => 'Activity updated successfully',
+ 'data' => $activity
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Complete activity
+ * POST /api/sales/activities/{id}/complete
+ */
+ public function completeActivity($id)
+ {
+ try {
+ $activity = $this->activityModel->find($id);
+
+ if (!$activity) {
+ return $this->failNotFound('Activity not found');
+ }
+
+ if ($activity['status'] === 'completed') {
+ return $this->fail('Activity is already completed', ResponseInterface::HTTP_BAD_REQUEST);
+ }
+
+ $data = $this->request->getJSON(true);
+ $data['updated_by'] = $this->getUserId();
+
+ $this->activityModel->completeActivity($id, $data);
+
+ // Create follow-up activity if requested
+ if (!empty($data['create_followup']) && $data['create_followup'] === true) {
+ $followupData = [
+ 'lead_id' => $activity['lead_id'],
+ 'activity_type' => $data['followup_type'] ?? 'Call',
+ 'notes' => $data['followup_notes'] ?? '',
+ 'scheduled_date' => $data['followup_date'] ?? null,
+ 'assigned_to' => $activity['assigned_to'],
+ 'parent_activity_id' => $id,
+ 'created_by' => $this->getUserId(),
+ 'updated_by' => $this->getUserId(),
+ ];
+
+ $this->activityModel->insert($followupData);
+ }
+
+ $updatedActivity = $this->activityModel->find($id);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'message' => 'Activity completed successfully',
+ 'data' => $updatedActivity
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Delete activity
+ * DELETE /api/sales/activities/{id}
+ */
+ public function deleteActivity($id)
+ {
+ try {
+ if (!$this->activityModel->find($id)) {
+ return $this->failNotFound('Activity not found');
+ }
+
+ $this->activityModel->delete($id);
+
+ return $this->respondDeleted([
+ 'status' => 'success',
+ 'message' => 'Activity deleted successfully'
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Get pending activities count
+ * GET /api/sales/activities/pending-count
+ */
+ public function getPendingCount()
+ {
+ try {
+ $userId = $this->getUserId();
+ $count = $this->activityModel->getPendingActivitiesCount($userId);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'data' => ['count' => $count]
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ // ==================== LEAD NOTES APIs ====================
+
+ /**
+ * Get notes for a lead
+ * GET /api/sales/leads/{leadId}/notes
+ */
+ public function getLeadNotes($leadId)
+ {
+ try {
+ $notes = $this->noteModel->getNotesByLead($leadId);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'data' => $notes
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Create note
+ * POST /api/sales/notes
+ */
+ public function createNote()
+ {
+ try {
+ $data = $this->request->getJSON(true);
+ $data['user_id'] = $this->getUserId();
+ $data['created_by'] = $this->getUserId();
+ $data['updated_by'] = $this->getUserId();
+
+ if (!$this->noteModel->insert($data)) {
+ return $this->fail($this->noteModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
+ }
+
+ $noteId = $this->noteModel->getInsertID();
+ $note = $this->noteModel->find($noteId);
+
+ return $this->respondCreated([
+ 'status' => 'success',
+ 'message' => 'Note created successfully',
+ 'data' => $note
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Update note
+ * PUT /api/sales/notes/{id}
+ */
+ public function updateNote($id)
+ {
+ try {
+ $note = $this->noteModel->find($id);
+
+ if (!$note) {
+ return $this->failNotFound('Note not found');
+ }
+
+ // Check if user owns the note
+ if ($note['user_id'] != $this->getUserId()) {
+ return $this->failUnauthorized('You are not authorized to update this note');
+ }
+
+ $data = $this->request->getJSON(true);
+ $data['updated_by'] = $this->getUserId();
+
+ if (!$this->noteModel->update($id, $data)) {
+ return $this->fail($this->noteModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
+ }
+
+ $updatedNote = $this->noteModel->find($id);
+
+ return $this->respond([
+ 'status' => 'success',
+ 'message' => 'Note updated successfully',
+ 'data' => $updatedNote
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Delete note
+ * DELETE /api/sales/notes/{id}
+ */
+ public function deleteNote($id)
+ {
+ try {
+ $note = $this->noteModel->find($id);
+
+ if (!$note) {
+ return $this->failNotFound('Note not found');
+ }
+
+ // Check if user owns the note
+ if ($note['user_id'] != $this->getUserId()) {
+ return $this->failUnauthorized('You are not authorized to delete this note');
+ }
+
+ $this->noteModel->delete($id);
+
+ return $this->respondDeleted([
+ 'status' => 'success',
+ 'message' => 'Note deleted successfully'
+ ]);
+ } catch (\Exception $e) {
+ return $this->fail($e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ // ==================== HELPER METHODS ====================
+
+ /**
+ * Get current user ID from session or JWT token
+ * You'll need to implement this based on your authentication system
+ */
+ private function getUserId()
+ {
+ // If using session
+ if (session()->has('user_id')) {
+ return session()->get('user_id');
+ }
+
+ // If using JWT, decode token and get user_id
+ // Example: return $this->decodeJWT()['user_id'];
+
+ // For development/testing, you can return a default value
+ return 1;
+ }
+}
\ No newline at end of file
diff --git a/app/Controllers/TicketController.php b/app/Controllers/TicketController.php
index ac104bf3..73997e99 100644
--- a/app/Controllers/TicketController.php
+++ b/app/Controllers/TicketController.php
@@ -371,6 +371,11 @@ class TicketController extends BaseController
$data['insurer_list'] = $this->insurerModel->where('is_active', 1)->findAll();
$data['tpa_list'] = $this->TPAModel->where('is_active', 1)->findAll();
$data['ticket_data'] = $this->ticketSearch(1);
+ $data['date_type'] = [
+ 'ticket_created_date' => 'Created Date',
+ 'ticket_updated_date' => 'Updated Date',
+ ];
+
return $this->loadLayout('ticket_search', $data);
} else {
@@ -401,6 +406,7 @@ class TicketController extends BaseController
}
else{
$data['ticket_data'] = $this->ticketSearch();
+ // print_r($data['ticket_data']);die;
$html = view('ticket_list', $data);
return $this->respond(['status' => true, 'html' => $html], 200);
}
@@ -411,6 +417,7 @@ class TicketController extends BaseController
public function ticketSearch($action = null)
{
$db = db_connect();
+ $rawWhere = [];
// log_message('error', 'Ticket Search Action: ' . $action);
$subquery = $db->table('ticket_master tm')
->select([
@@ -464,6 +471,7 @@ class TicketController extends BaseController
'tm.insured_name',
'c.short_name',
'DATE_FORMAT(tm.created_at, "%d-%m-%Y") AS ticket_created_date',
+ 'DATE_FORMAT(tm.updated_at, "%d-%m-%Y") AS ticket_updated_date',
'COALESCE(tat_category.tat, "0-6 Days") AS tat',
'tm.claim_status_id',
@@ -557,14 +565,24 @@ class TicketController extends BaseController
} else {
$search_data = $this->request->getPost();
// print_r($search_data); die()
- $where = [];
+ $where = [];
foreach ($search_data as $search_objects => $key) {
if ($key != null && $key != '' && $key != 0 && $search_objects != 'is_dashboard') {
- $where[$search_objects] = $key;
+
+ if($search_objects == "date_type" && $search_data[$search_objects] === 'ticket_created_date'){
+ $startDate = date('Y-m-d 00:00:00', strtotime($search_data['start_date']));
+ $endDate = date('Y-m-d 23:59:59', strtotime($search_data['end_date']));
+ $rawWhere[] = "tm.created_at BETWEEN '$startDate' AND '$endDate'";
+ }
+ elseif($search_objects == "date_type" && $search_data[$search_objects] === 'ticket_updated_date'){
+ $startDate = date('Y-m-d 00:00:00', strtotime($search_data['start_date']));
+ $endDate = date('Y-m-d 23:59:59', strtotime($search_data['end_date']));
+ $rawWhere[] = "tm.updated_at BETWEEN '$startDate' AND '$endDate'";
+ }elseif($search_objects != "date_type" && $search_objects != "start_date" && $search_objects != "end_date"){
+ $where[$search_objects] = $key;
+ }
}
}
- // print_rr($where);
-
}
$query = $db->table('ticket_master tm')
->select([
@@ -584,6 +602,7 @@ class TicketController extends BaseController
'tm.insured_name',
'c.short_name',
'DATE_FORMAT(tm.created_at, "%d-%m-%Y") AS ticket_created_date',
+ 'DATE_FORMAT(tm.updated_at, "%d-%m-%Y") AS ticket_updated_date',
'COALESCE(tat_category.tat, "0-6 Days") AS tat',
'(SELECT first_name FROM user_profiles WHERE user_profiles.id = tm.acm_id) AS acm_name',
@@ -641,9 +660,14 @@ class TicketController extends BaseController
->where('tm.is_active', 1)
->where($where)
->orderBy('tm.id', 'DESC');
+ if (!empty($rawWhere)) {
+ foreach ($rawWhere as $condition) {
+ $query->where($condition, null, false); // raw SQL
+ }
+ }
$data = $query->get()->getResultArray();
- // dd($db->getLastQuery()->getQuery());
+ // dd($db->getLastQuery()->getQuery());die;
// dd($data);
if ($action == 5){
diff --git a/app/Controllers/UserController.php b/app/Controllers/UserController.php
index 3879b49d..22451e89 100755
--- a/app/Controllers/UserController.php
+++ b/app/Controllers/UserController.php
@@ -585,44 +585,79 @@ class UserController extends AdminController
try{
// Listing
if ($method === 'get') {
- $types = $this->partnerStaffModel->findAll();
- if (empty($types)) {
- return $this->response->setJSON(['status' => 'error','message' => 'No Staff found'])->setStatusCode(404);
+ $manager = $this->partnerStaffModel->where('role_id',1)->findAll();
+ if (empty($manager)) {
+ return $this->response->setJSON(['status' => 'error','message' => 'No Manager found'])->setStatusCode(404);
}
- return $this->response->setJSON(['status' => 'success','data' => $types])->setStatusCode(200);
+ return $this->response->setJSON(['status' => 'success','data' => $manager])->setStatusCode(200);
}
// add/update
if ($method === 'post') {
-
$data = $this->request->getPost();
- if (!empty($data['id'])) {
- $text = "update";
- $data['updated_by'] = get_session_userid();
- $result = $this->partnerStaffModel->update($data['id'], $data);
- $updateID = $data['id'];
- } else {
- $text = "create";
- $data['role_id'] = 1;
- $data['created_by'] = get_session_userid();
- $insertID = $this->partnerStaffModel->insert($data);
- if($insertID){
- $details['manager_id'] = $insertID;
- $details['updated_by'] = get_session_userid();
- $this->partnerStaffModel->update($insertID, $details);
+ $id = !empty($data['PrimaryKey']) ? $data['PrimaryKey'] : null;
+
+ // don't forgot same means just unset the key because partner_staff some UNIQUE KEY sets in table thats why
+ if ($id) {
+ $existing = $this->partnerStaffModel->find($id);
+ if ($existing) {
+ if ($data['email'] === $existing['email']) { unset($data['email']); }
+ if ($data['mobile'] === $existing['mobile']) { unset($data['mobile']); }
}
- $result = true;
}
- $id = isset($insertID) && !empty($insertID) ? $insertID : ($updateID ?? null);
+ $errors = [];
+
+ // Check Email Duplicate (if it wasn't unset)
+ if (isset($data['email'])) {
+ $count = $this->partnerStaffModel->where('email', $data['email'])->countAllResults();
+ if ($count > 0) $errors['email'] = "This email is already taken by another user.";
+ }
+
+ // Check Mobile Duplicate (if it wasn't unset)
+ if (isset($data['mobile'])) {
+ $count = $this->partnerStaffModel->where('mobile', $data['mobile'])->countAllResults();
+ if ($count > 0) $errors['mobile'] = "This mobile is already taken by another user.";
+ }
+
+ // If there are duplicates, stop and return error
+ if (!empty($errors)) {
+ return $this->response->setJSON([
+ 'status' => 'error',
+ 'message' => 'Duplicate detected',
+ 'errors' => $errors
+ ])->setStatusCode(400);
+ }
+
+ // --- Save/Update ---
+ if ($id) {
+ $text = "update";
+ $data['updated_by'] = get_session_userid();
+
+ $result = $this->partnerStaffModel->update($updateID, $data);
+ } else {
+ $text = "create";
+ $data['role_id'] = 1;
+ $data['created_by'] = get_session_userid();
+ $id = $this->partnerStaffModel->insert($data);
+ if($id){
+ $details['manager_id'] = $id;
+ $details['updated_by'] = get_session_userid();
+ $this->partnerStaffModel->update($id, $details);
+ }
+ $result = true;
+
+ }
+
+
return $this->response->setJSON([
'status' => $id ? 'success' : 'error',
'message' => $id ? "Staff {$text}d successfully" : "Unable to {$text} staff. Please try again.",
'id' => $id
])->setStatusCode($id ? 200 : 400);
- }
+ }
// delete
if ($method === 'put') {
$data = $this->request->getRawInput();
diff --git a/app/Helpers/TPADataCompareHelper.php b/app/Helpers/TPADataCompareHelper.php
new file mode 100644
index 00000000..79907b37
--- /dev/null
+++ b/app/Helpers/TPADataCompareHelper.php
@@ -0,0 +1,239 @@
+format('Y-m-d');
+ }
+ }
+
+ $ts = strtotime($date);
+ return $ts ? date('Y-m-d', $ts) : null;
+ }
+
+ public static function normalizeName(?string $name): ?string
+ {
+ if ($name === null) {
+ return null;
+ }
+
+ $name = trim($name);
+ // $name = preg_replace('/\.+/', ' ', $name);
+ // $name = preg_replace('/\s+/', ' ', $name);
+ return mb_strtolower($name);
+ }
+
+ public static function normalizeEmpId(?string $empId): ?string
+ {
+ if ($empId === null) {
+ return null;
+ }
+
+ $empId = trim($empId);
+ $empId = preg_replace('/[\s\-\/]/', '', $empId);
+ return $empId === '' ? null : $empId;
+ }
+
+ public static function normalizeGender(?string $gender): ?string
+ {
+ if ($gender === null) {
+ return null;
+ }
+
+ $g = strtoupper(trim($gender));
+ return in_array($g, ['M', 'MALE']) ? 'M'
+ : (in_array($g, ['F', 'FEMALE']) ? 'F' : $g);
+ }
+
+ public static function normalizeRelation(?string $relation): ?string
+ {
+ return $relation === null ? null : strtoupper(trim($relation));
+ }
+
+ /* =========================
+ Core normalization
+ ========================= */
+
+ public static function normalizeEmployees(
+ array $rows,
+ array $keyMap,
+ ?string $pkKey = null
+ ): array {
+ $normalized = [];
+ // print_r($rows);die();
+ foreach ($rows as $index => $row) {
+ // echo "Normalizing record #" . ($index + 1) . "\n";
+ $item = [
+ 'pk' => $pkKey && isset($row[$pkKey]) ? $row[$pkKey] : null,
+ '__raw' => $row,
+ ];
+
+ foreach ($keyMap as $canonical => $possibleKeys) {
+ $value = null;
+
+ foreach ($possibleKeys as $key) {
+ if (isset($row[$key]) && $row[$key] !== '') {
+ $value = $row[$key];
+ break;
+ }
+ }
+
+ switch ($canonical) {
+ case 'emp_id':
+ $item['emp_id'] = self::normalizeEmpId($value);
+ break;
+ case 'name':
+ $item['name'] = self::normalizeName($value);
+ break;
+ case 'dob':
+ $item['dob'] = self::parseDateToYmd($value);
+ break;
+ case 'gender':
+ $item['gender'] = self::normalizeGender($value);
+ break;
+ case 'relation':
+ $item['relation'] = self::normalizeRelation($value);
+ break;
+ default:
+ $item[$canonical] = $value;
+ }
+ }
+
+ if (!empty($item['pk'])) {
+ $normalized[$item['pk']] = $item;
+ } else {
+ $normalized['__no_emp_' . $index] = $item;
+ }
+
+ // echo "Normalizing record #" . ($index + 1) . "\n";
+ }
+
+ return $normalized;
+ }
+
+ /* =========================
+ Main comparison
+ ========================= */
+
+ public static function compareDbVsMediassist(
+ array $dbData,
+ array $mediassistAPIdata
+ ): array {
+ // echo count($dbData) . " records in DB data\n";die();
+ $dbKeyMap = [
+ 'emp_id' => ['emp_code'],
+ 'name' => ['name'],
+ 'dob' => ['dob'],
+ 'gender' => ['gender'],
+ 'relation' => ['relationship'],
+ ];
+
+ $mediassistKeyMap = [
+ 'emp_id' => ['priBenefEmpCode'],
+ 'name' => ['benefName', 'priBeneficiaryName'],
+ 'dob' => ['benefDOB'],
+ 'gender' => ['benefSex'],
+ 'relation' => ['relName'],
+ ];
+
+ $A = self::normalizeEmployees($dbData, $dbKeyMap, 'id');
+
+ $B = self::normalizeEmployees($mediassistAPIdata, $mediassistKeyMap);
+ dd($B);
+ $report = [
+ 'summary' => [
+ 'total_db' => count($A),
+ 'total_mediassist' => count($B),
+ 'matched' => 0,
+ 'mismatched' => 0,
+ 'only_in_db' => 0,
+ 'only_in_mediassist' => 0,
+ ],
+ 'exact_matches' => [],
+ 'field_mismatches' => [],
+ 'only_in_db' => [],
+ 'only_in_mediassist' => [],
+ ];
+
+ foreach ($A as $empId => $rowA) {
+
+ if (!isset($B[$empId])) {
+ $report['only_in_db'][] = [
+ 'pk' => $rowA['pk'],
+ 'emp_id' => $empId,
+ 'data' => $rowA,
+ ];
+ continue;
+ }
+
+ $rowB = $B[$empId];
+ $diff = [];
+
+ foreach (self::COMPARE_FIELDS as $field) {
+ if (($rowA[$field] ?? null) !== ($rowB[$field] ?? null)) {
+ $diff[$field] = [
+ 'db' => $rowA[$field] ?? null,
+ 'mediassist' => $rowB[$field] ?? null,
+ ];
+ }
+ }
+
+ if (empty($diff)) {
+ $report['exact_matches'][] = [
+ 'pk' => $rowA['pk'],
+ 'emp_id' => $empId,
+ ];
+ $report['summary']['matched']++;
+ } else {
+ $report['field_mismatches'][] = [
+ 'pk' => $rowA['pk'],
+ 'emp_id' => $empId,
+ 'differences' => $diff,
+ ];
+ $report['summary']['mismatched']++;
+ }
+
+ unset($B[$empId]);
+ }
+
+ foreach ($B as $empId => $rowB) {
+ $report['only_in_mediassist'][] = [
+ 'pk' => null,
+ 'emp_id' => $empId,
+ 'data' => $rowB,
+ ];
+ }
+
+ $report['summary']['only_in_db'] = count($report['only_in_db']);
+ $report['summary']['only_in_mediassist'] = count($report['only_in_mediassist']);
+
+ return $report;
+ }
+}
diff --git a/app/Helpers/TPADataCompareHelper2.php b/app/Helpers/TPADataCompareHelper2.php
new file mode 100644
index 00000000..5ff171f3
--- /dev/null
+++ b/app/Helpers/TPADataCompareHelper2.php
@@ -0,0 +1,363 @@
+format('Y-m-d');
+ }
+ $ts = strtotime($date);
+ return $ts ? date('Y-m-d', $ts) : null;
+ }
+
+ public static function normalizeName(?string $name): ?string
+ {
+ if ($name === null) return null;
+ $s = trim($name);
+ // $s = preg_replace('/\.+/', ' ', $s);
+ // $s = preg_replace('/\s+/', ' ', $s);
+ return mb_strtolower($s);
+ }
+
+ public static function normalizeEmpId(?string $id): ?string
+ {
+ if ($id === null) return null;
+ $s = trim($id);
+ // $s = preg_replace('/[\s\-\/]/', '', $s);
+ // $s = preg_replace('/[^\p{L}\p{N}]/u', '', $s);
+ return $s === '' ? null : $s;
+ }
+
+ public static function normalizeGender(?string $g): ?string
+ {
+ if ($g === null) return null;
+ $u = strtoupper(trim($g));
+ if (in_array($u, ['M','MALE'])) return 'M';
+ if (in_array($u, ['F','FEMALE'])) return 'F';
+ return $u;
+ }
+
+ public static function normalizeRelation(?string $r): ?string
+ {
+ if ($r === null) return null;
+ return strtoupper(trim($r));
+ }
+
+ /* -------------------------------------
+ Normalize rows -> canonical structure
+ Returns array of items (not keyed), each item contains:
+ - 'pk' (DB primary key or null)
+ - 'emp_id' (employee code)
+ - 'name','dob','gender','relation' (normalized)
+ - '__raw' original row
+ - '__rowid' generated unique row id for API rows (if needed)
+ ------------------------------------- */
+ public static function normalizeRows(array $rows, array $keyMap, ?string $pkKey = null): array
+ {
+ $out = [];
+ $idx = 0;
+ foreach ($rows as $r) {
+ $item = [
+ 'pk' => ($pkKey && isset($r[$pkKey])) ? $r[$pkKey] : null,
+ '__raw' => $r,
+ '__rowid' => null,
+ ];
+
+ // fill canonical fields
+ foreach ($keyMap as $canonical => $candidates) {
+ $val = null;
+ foreach ($candidates as $k) {
+ if (is_array($r) && array_key_exists($k, $r) && $r[$k] !== '') {
+ $val = $r[$k];
+ break;
+ }
+ }
+ switch ($canonical) {
+ case 'emp_id': $item['emp_id'] = self::normalizeEmpId((string)$val); break;
+ case 'name': $item['name'] = self::normalizeName((string)$val); break;
+ case 'dob': $item['dob'] = self::parseDateToYmd((string)$val); break;
+ case 'gender': $item['gender'] = self::normalizeGender((string)$val); break;
+ case 'relation': $item['relation'] = self::normalizeRelation((string)$val); break;
+ default: $item[$canonical] = $val;
+ }
+ }
+
+ // create a stable row id (for API rows without PK) so they remain unique
+ $item['__rowid'] = $item['pk'] ?? ('api_row_' . $idx++);
+
+ $out[] = $item;
+ }
+
+ return $out;
+ }
+
+ /* -------------------------
+ Build grouped index by emp_code:
+ returns [ emp_code => [ item1, item2, ... ] ]
+ Use '__no_emp' key for rows without emp_id
+ ------------------------- */
+ public static function groupByEmpCode(array $items): array
+ {
+ $groups = [];
+ foreach ($items as $it) {
+ $key = $it['emp_id'] ?? '__no_emp';
+ if ($key === null || $key === '') $key = '__no_emp';
+ $groups[$key][] = $it;
+ }
+ return $groups;
+ }
+
+ /* -------------------------
+ Find a match for a DB row inside a mediassist group (array of items).
+ Matching priority:
+ 1) unique ids present in mediassist (if db raw has any of those too)
+ 2) exact name + dob
+ 3) exact name + relation
+ 4) fuzzy name + dob (threshold %)
+ If found, returns index of matched item in $group array; else null.
+ Note: $group is passed by reference so matched item can be removed by caller.
+ ------------------------- */
+ public static function findMatchInGroup(array $dbRow, array $group, int $fuzzyThreshold = 85): ?int
+ {
+ // 1) match on unique ids if DB row contains any of those (rare)
+ // foreach (self::MEDIASSIST_UNIQUE_IDS as $uidField) {
+ // $dbVal = $dbRow['__raw'][$uidField] ?? null;
+ // if ($dbVal) {
+ // foreach ($group as $i => $gItem) {
+ // $gVal = $gItem['__raw'][$uidField] ?? null;
+ // if ($gVal && (string)$gVal === (string)$dbVal) return $i;
+ // }
+ // }
+ // }
+
+ // 2) match exact name + dob (both normalized)
+ if (!empty($dbRow['name']) && !empty($dbRow['dob'])) {
+ foreach ($group as $i => $gItem) {
+ if (!empty($gItem['name']) && !empty($gItem['dob'])
+ && $dbRow['name'] === $gItem['name']
+ && $dbRow['dob'] === $gItem['dob']) {
+ return $i;
+ }
+ }
+ }
+
+ // 3) match exact name + relation (if dob missing or unreliable)
+ if (!empty($dbRow['name']) && !empty($dbRow['relation'])) {
+ foreach ($group as $i => $gItem) {
+ if (!empty($gItem['name']) && !empty($gItem['relation'])
+ && $dbRow['name'] === $gItem['name']
+ && $dbRow['relation'] === $gItem['relation']) {
+ return $i;
+ }
+ }
+ }
+
+ // 4) fuzzy name + dob (if dob present)
+ if ($fuzzyThreshold > 0 && !empty($dbRow['name'])) {
+ foreach ($group as $i => $gItem) {
+ if (empty($gItem['name'])) continue;
+ $score = 0.0;
+ similar_text($dbRow['name'], $gItem['name'], $score); // percent
+ $dobMatches = (!empty($dbRow['dob']) && !empty($gItem['dob']) && $dbRow['dob'] === $gItem['dob']);
+ // require DOB match OR higher threshold if dob missing
+ if ($dobMatches && $score >= max(60, $fuzzyThreshold - 10)) { // slightly relaxed
+ return $i;
+ }
+ if (!$dobMatches && $score >= $fuzzyThreshold) {
+ // if names are very similar even w/o dob, accept (cautious)
+ return $i;
+ }
+ }
+ }
+
+ // no match
+ return null;
+ }
+
+ /* -------------------------
+ Main comparison routine (grouped matching)
+ Options:
+ - fuzzy_threshold (default 85)
+ - try_global_search_if_emp_missing (bool, default false)
+ ------------------------- */
+ public static function compareDbVsMediassistGrouped(
+ array $dbRows,
+ array $mediassistAPIdata,
+ string $dbPkKey = 'id',
+ array $options = [],
+ array $dbKeyMap = null,
+ array $mediassistKeyMap = null,
+ ): array {
+
+
+ $dbKeyMap = [
+ 'emp_id' => ['emp_code'],
+ 'name' => ['name'],
+ 'dob' => ['dob'],
+ 'gender' => ['gender'],
+ 'relation' => ['relationship'],
+ ];
+
+ $mediassistKeyMap = [
+ 'emp_id' => ['priBenefEmpCode'],
+ 'name' => ['benefName', 'priBeneficiaryName'],
+ 'dob' => ['benefDOB'],
+ 'gender' => ['benefSex'],
+ 'relation' => ['relName'],
+ ];
+
+
+ $fuzzyThreshold = $options['fuzzy_threshold'] ?? 85;
+ $tryGlobalSearch = $options['try_global_search_if_emp_missing'] ?? false;
+
+ // normalize to lists
+ $normDb = self::normalizeRows($dbRows, $dbKeyMap, $dbPkKey);
+ $normApi = self::normalizeRows($mediassistAPIdata, $mediassistKeyMap, null);
+ // dd($normApi);
+ // group by emp_code
+ $dbGroups = self::groupByEmpCode($normDb);
+ $apiGroups = self::groupByEmpCode($normApi);
+
+ // prepare report
+ $report = [
+ 'summary' => [
+ 'total_db' => count($normDb),
+ 'total_mediassist' => count($normApi),
+ 'matched' => 0,
+ 'mismatched' => 0,
+ 'only_in_db' => 0,
+ 'only_in_mediassist' => 0,
+ ],
+ 'exact_matches' => [],
+ 'field_mismatches' => [],
+ 'only_in_db' => [],
+ 'only_in_mediassist' => [],
+ ];
+
+ // For each db group (emp_code), try match each dbRow with api group entries
+ foreach ($dbGroups as $empCode => $dbItems) {
+ $apiItems = $apiGroups[$empCode] ?? [];
+
+ // We'll mutate a local copy of apiItems to mark matched items (so we can remove)
+ $remainingApi = $apiItems;
+
+ foreach ($dbItems as $dbRow) {
+ $matchedIndex = null;
+ if (!empty($empCode) && !empty($remainingApi)) {
+ $matchedIndex = self::findMatchInGroup($dbRow, $remainingApi, $fuzzyThreshold);
+ }
+
+ // If empCode not present in API and global search allowed, try searching all API rows (costly)
+ if ($matchedIndex === null && $tryGlobalSearch && empty($apiItems)) {
+ // attempt global search across all api groups (can be heavy)
+ foreach ($apiGroups as $gKey => $gList) {
+ $mi = self::findMatchInGroup($dbRow, $gList, $fuzzyThreshold);
+ if ($mi !== null) {
+ // found in another group
+ $matchedIndex = $mi;
+ // use that group as remainingApi reference so we can remove matched later
+ $remainingApi = $apiGroups[$gKey];
+ break;
+ }
+ }
+ }
+
+ if ($matchedIndex !== null) {
+ // found match — use that api row
+ $apiMatch = $remainingApi[$matchedIndex];
+
+ // compare fields
+ $diff = [];
+ foreach (self::COMPARE_FIELDS as $f) {
+ $vDb = $dbRow[$f] ?? null;
+ $vApi = $apiMatch[$f] ?? null;
+ if ($vDb !== $vApi) {
+ $diff[$f] = ['db' => $vDb, 'mediassist' => $vApi];
+ }
+ }
+
+ if (empty($diff)) {
+ $report['exact_matches'][] = [
+ 'pk' => $dbRow['pk'],
+ 'emp_id' => $dbRow['emp_id'],
+ 'db_rowid' => $dbRow['__rowid'],
+ 'api_rowid' => $apiMatch['__rowid'],
+ ];
+ $report['summary']['matched']++;
+ } else {
+ $report['field_mismatches'][] = [
+ 'pk' => $dbRow['pk'],
+ 'emp_id' => $dbRow['emp_id'],
+ 'db_rowid' => $dbRow['__rowid'],
+ 'api_rowid' => $apiMatch['__rowid'],
+ 'differences' => $diff
+ ];
+ $report['summary']['mismatched']++;
+ }
+
+ // remove matched api item from remainingApi (so it's not matched again)
+ array_splice($remainingApi, $matchedIndex, 1);
+ } else {
+ // no match found for this dbRow inside api group => only_in_db
+ $report['only_in_db'][] = [
+ 'pk' => $dbRow['pk'],
+ 'emp_id' => $dbRow['emp_id'],
+ 'db_rowid' => $dbRow['__rowid'],
+ 'data' => $dbRow
+ ];
+ }
+ } // end each dbRow
+
+ // after processing all dbRows in this empCode group, any remainingApi are only_in_mediassist
+ foreach ($remainingApi as $leftApi) {
+ $report['only_in_mediassist'][] = [
+ 'pk' => null,
+ 'emp_id' => $leftApi['emp_id'],
+ 'api_rowid' => $leftApi['__rowid'],
+ 'data' => $leftApi
+ ];
+ }
+ } // end each emp group
+
+ // Also handle any api groups whose emp_code does not exist in DB at all
+ foreach ($apiGroups as $empCode => $apiItems) {
+ if (isset($dbGroups[$empCode])) continue; // already handled
+ foreach ($apiItems as $ai) {
+ $report['only_in_mediassist'][] = [
+ 'pk' => null,
+ 'emp_id' => $ai['emp_id'],
+ 'api_rowid' => $ai['__rowid'],
+ 'data' => $ai
+ ];
+ }
+ }
+
+ // summary counts
+ $report['summary']['only_in_db'] = count($report['only_in_db']);
+ $report['summary']['only_in_mediassist'] = count($report['only_in_mediassist']);
+
+ return $report;
+ }
+}
diff --git a/app/Helpers/excel_util_helper.php b/app/Helpers/excel_util_helper.php
index 918a04c3..f1269f3d 100755
--- a/app/Helpers/excel_util_helper.php
+++ b/app/Helpers/excel_util_helper.php
@@ -3041,6 +3041,11 @@ if (!function_exists('validate_indian_vehicle_number')) {
function validate_indian_vehicle_number($number)
{
$number = strtoupper(trim($number));
+ $new_vehicle = strtolower(trim($number));
+
+ if($new_vehicle == "new"){
+ return ['status' => true, 'error' => null];
+ }
// Normal Format:
// 2 letters (state) + 2 digits (district) + 1 or 2 letters (series) + 4 digits
@@ -3120,9 +3125,18 @@ if (!function_exists('check_agent_exist')) {
if (!function_exists('check_rto_data')) {
function check_rto_data($row, $rto_master)
- {
+ {
// Vehicle number from uploaded row
$vehicle_no = strtoupper(trim($row[2])); // Example: TN10AB1234
+ $new_vehicle = strtolower(trim($row[2]));
+
+ if($new_vehicle == "new"){
+ return [
+ 'status' => true,
+ 'error' => null,
+ 'rto_data' => []
+ ];
+ }
// Must be at least 4 characters to extract RTO
if (strlen($vehicle_no) < 4) {
diff --git a/app/Models/FAQModel.php b/app/Models/FAQModel.php
new file mode 100644
index 00000000..7fe5dd48
--- /dev/null
+++ b/app/Models/FAQModel.php
@@ -0,0 +1,55 @@
+select("
partner_invoice.*,
@@ -104,14 +105,21 @@ class InvoiceModel extends Model
WHEN payout_status = 2 THEN 'Completed'
END AS status_text,
- partner_agent.name as agent_name
+ partner_agent.name as agent_name,
+ partner_pos.name as pos_name
+
")
- ->join('partner_agent', 'partner_invoice.agent_id = partner_agent.id')
+ ->join('partner_agent', 'partner_invoice.agent_id = partner_agent.id','left')
+ ->join('partner_pos', 'partner_invoice.pos_id = partner_pos.id')
->where('partner_invoice.is_active', 1)
->where('partner_invoice.broker_id', 1);
- if(!empty($agent_id)){
- $data->where('partner_invoice.agent_id', $agent_id);
+ // if(!empty($agent_id)){
+ // $data->where('partner_invoice.agent_id', $agent_id);
+ // } // because dropdown hided
+
+ if(!empty($pos_id)){
+ $data->where('partner_invoice.pos_id', $pos_id);
}
if(!empty($status_id)){
@@ -127,7 +135,8 @@ class InvoiceModel extends Model
->where('partner_invoice.invoice_date <=', $endDate);
}
- if(!empty($agent_id) && !empty($status_id) && !empty($start_date) && !empty($end_date)){
+ // if(!empty($agent_id) && !empty($status_id) && !empty($start_date) && !empty($end_date)){
+ if(!empty($pos_id) && !empty($status_id) && !empty($start_date) && !empty($end_date)){
$fromDate = date('Y-m-d', strtotime('-60 days'));
$toDate = date('Y-m-d 23:59:59');
@@ -152,6 +161,21 @@ class InvoiceModel extends Model
return $this->db->table('partner_agent')->get()->getResultArray();
}
+ public function posList(array $params = [])
+ {
+ $builder = $this->db->table('partner_pos')
+ ->select('partner_pos.*, partner_staff.name AS manager_name')
+ ->join('partner_staff', 'partner_staff.id = partner_pos.manager_id', 'left')
+ ->orderBy('partner_pos.id', 'DESC');
+
+ if (isset($params['is_active'])) {
+ $builder->where('partner_pos.is_active', $params['is_active']);
+ }
+
+ return $builder->get()->getResultArray();
+ }
+
+
public function utrSummary($invoice_id)
{
$data = $this->select("
@@ -193,7 +217,7 @@ class InvoiceModel extends Model
}
- public function payoutList($flag, $agentId = null, $invoiceId = null)
+ public function payoutList($flag, $posId = null, $invoiceId = null)
{
$builder = $this->db->table('policy_transaction pt')
->select('
@@ -209,17 +233,20 @@ class InvoiceModel extends Model
pii.commission_amount as paid_amount,
pi.payout_status,
pp.id as partner_policy_id,
- pp.policy_transaction_id
+ pp.policy_transaction_id,
+ pt.pos_id as posId
')
->join('partner_invoice_items pii','pii.policy_no = pt.policy_no','left')
->join('partner_invoice pi','pi.id = pii.invoice_id','left')
->join('partner_policy pp','pt.policy_no = pp.policy_number AND pt.agent_id = pp.agent_id AND pt.id = pp.policy_transaction_id')
->where('pt.is_active',1)
- ->where('pt.agent_id IS NOT NULL');
+ ->where('pt.pos_id IS NOT NULL');
+ // ->where('pt.agent_id IS NOT NULL');
if ($flag == 1) { // Add mode
// EXCLUDE all policies that exist in partner_invoice_items
- $builder->where("pt.id NOT IN (SELECT policy_id FROM partner_invoice_items)", null, false);
+ $builder->where("pp.id NOT IN (SELECT policy_id FROM partner_invoice_items)", null, false);
+ $builder->where("pt.policy_no NOT IN (SELECT policy_no FROM partner_invoice_items)", null, false);
}
if ($flag == 2) { // Edit mode
@@ -228,16 +255,22 @@ class InvoiceModel extends Model
if ($invoiceId) {
$builder->where('pi.id', $invoiceId); // only policies of this invoice
}
- if ($agentId) {
- $builder->where('pt.agent_id', $agentId);
+ // if ($agentId) {
+ // $builder->where('pt.agent_id', $agentId);
+ // }
+ if ($posId) {
+ $builder->where('pt.pos_id', $posId);
}
}
if ($flag == 3) { // Extra policies
// Policies not assigned to any invoice
$builder->where('pii.id IS NULL', null, false);
- if ($agentId) {
- $builder->where('pt.agent_id', $agentId);
+ // if ($agentId) {
+ // $builder->where('pt.agent_id', $agentId);
+ // }
+ if ($posId) {
+ $builder->where('pt.pos_id', $posId);
}
}
diff --git a/app/Models/PartnerPosModel.php b/app/Models/PartnerPosModel.php
new file mode 100644
index 00000000..4b16c161
--- /dev/null
+++ b/app/Models/PartnerPosModel.php
@@ -0,0 +1,135 @@
+ 'required|min_length[3]',
+ // 'email' => 'permit_empty|valid_email|is_unique[partner_pos.email,id,{id}]',
+ // 'mobile' => 'permit_empty|numeric|max_length[20]|is_unique[partner_pos.mobile,id,{id}]',
+ // 'manager_id' => 'required|integer',
+ // ];
+
+ // protected $validationMessages = [
+ // 'email' => [
+ // 'is_unique' => 'Sorry, that email address is already registered.',
+ // ],
+ // ];
+
+ /**
+ * @var bool Whether to skip validation during inserts and updates.
+ */
+ protected $skipValidation = false;
+
+ /**
+ * @var bool Whether to clean data for SQL Injection.
+ */
+ protected $cleanValidationRules = true;
+
+ protected $beforeInsert = ["checkAndADDCreatedByValue"];
+ protected $beforeUpdate = ["checkAndUpdateUpdatedByValue"];
+
+ protected function checkAndADDCreatedByValue(array $data)
+ {
+ if (empty($data['data']['created_by'])) {
+ $data['data']['created_by'] = get_session_userid();
+ }
+
+ return $data;
+ }
+
+ protected function checkAndUpdateUpdatedByValue(array $data)
+ {
+ if (empty($data['data']['updated_by'])) {
+ $data['data']['updated_by'] = get_session_userid();
+ }
+
+ $data['data']['updated_on'] = date('Y-m-d H:i:s');
+
+ return $data;
+ }
+
+}
\ No newline at end of file
diff --git a/app/Models/PartnerStaffModel.php b/app/Models/PartnerStaffModel.php
index 340a0ec5..8c5ca862 100755
--- a/app/Models/PartnerStaffModel.php
+++ b/app/Models/PartnerStaffModel.php
@@ -16,7 +16,7 @@ class PartnerStaffModel extends Model
protected $useSoftDeletes = false;
// allowed fields for insert/update
- protected $allowedFields = ['name','email','mobile','emp_id','role_id','email_otp','is_active','created_by','updated_by','manager_id'];
+ protected $allowedFields = ['name','email','mobile','emp_id','role_id','email_otp','is_active','created_by','updated_by','manager_id','retention_rate','nhance_branch_id'];
// automatic timestamps
protected $useTimestamps = true;
diff --git a/app/Models/PolicyTransactionModel.php b/app/Models/PolicyTransactionModel.php
index 1ac58a29..2dc9af06 100644
--- a/app/Models/PolicyTransactionModel.php
+++ b/app/Models/PolicyTransactionModel.php
@@ -2430,6 +2430,575 @@
return $result;
}
+ public function getBDSReportListSingleUnion($start_date = 0, $end_date = 0, $client_id = 0, $insurer_id = 0, $policy_type_id = 0, $date_type = 0, $issuer = 0, $client_branch_id = 0, $insurer_branch_id = 0, $client_policy_id = 0, $user_id = 0, $where = [])
+ {
+
+ $statement_month_condition = '';
+ if ($date_type == 'statement_month' && $start_date != 0 && $end_date != 0) {
+ $statement_month_condition = "
+ WHERE statement_month >= '" . $start_date . "'
+ AND statement_month <= '" . $end_date . "'
+ ";
+ }
+
+ $default_date_filter = '';
+ if ($client_id == 0 && $insurer_id == 0 && $policy_type_id == 0 && $date_type == 0 && $issuer == 0) {
+
+ $fromDate = date('Y-m-d', strtotime('-90 days'));
+ $toDate = date('Y-m-d 23:59:59');
+
+ if (empty($where)) {
+
+ $default_date_filter = "
+ AND pt.created_at >= '" . $fromDate . "'
+ AND pt.created_at <= '" . $toDate . "'
+ ";
+ }
+ }
+
+ $conditions = ""; // start safely
+
+ // 1. Role-based restrictions
+ if (
+ (!in_array(get_role_id(), [1, 5])) &&
+ !(
+ in_array(MANAGEMENT_TEAM_ID, user_team()) ||
+ in_array(FINANCE_TEAM_ID, user_team()) ||
+ in_array(BUSINESS_TEAM_ID, user_team())
+ )
+ ) {
+ if (get_role_id() == 4 && in_array(POS_TEAM_ID, user_team())) {
+ $conditions .= " AND pt.created_by = " . get_session_userid();
+ }
+ }
+
+ // 2. Dynamic $where array
+ if (!empty($where)) {
+ foreach ($where as $column => $value) {
+ $value = addslashes($value);
+ $conditions .= " AND `$column` = '$value' ";
+ }
+ }
+
+ // 3. Date condition (except statement_month)
+ if ($start_date != 0 && $end_date != 0 && $date_type != 0 && $date_type != 'statement_month') {
+
+ $startDate = date('Y-m-d 00:00:00', strtotime($start_date));
+ $endDate = date('Y-m-d 23:59:59', strtotime($end_date));
+
+ if ($date_type === "policy_issue_date") {
+ $conditions .= " AND pcsd.pt_policy_issue_date >= '$startDate' ";
+ $conditions .= " AND pcsd.pt_policy_issue_date <= '$endDate' ";
+ } else {
+ $conditions .= " AND pt.$date_type >= '$startDate' ";
+ $conditions .= " AND pt.$date_type <= '$endDate' ";
+ }
+ }
+
+ // 4. Common filters
+ if ($client_id != 0) {
+ $conditions .= " AND pt.client_id = $client_id ";
+ }
+
+ if ($insurer_id != 0) {
+ $conditions .= " AND pt.insurer_id = $insurer_id ";
+ }
+
+ if ($client_branch_id != 0) {
+ $conditions .= " AND pt.client_branch_id = $client_branch_id ";
+ }
+
+ if ($insurer_branch_id != 0) {
+ $conditions .= " AND pt.insurer_branch_id = $insurer_branch_id ";
+ }
+
+ if ($client_policy_id != 0) {
+ $conditions .= " AND pt.client_policy_id = $client_policy_id ";
+ }
+
+ if ($user_id != 0) {
+ $conditions .= " AND pt.created_by = $user_id ";
+ }
+
+ if ($policy_type_id != 0) {
+ $conditions .= " AND pt.policy_type_id = $policy_type_id ";
+ }
+
+ if ($issuer != 0) {
+ $conditions .= " AND pt.issuer = $issuer ";
+ }
+
+
+ $sql = "
+ SELECT * FROM (
+
+ SELECT
+ pt.id AS id,
+ pt.endorsement_no,
+ pt.ref,
+
+ pt.data_received_date,
+ pt.renewal_date,
+ pt.installment,
+ nhance_branch.branch_name as nhance_branch,
+
+ DATE_FORMAT(pt.policy_issue_date, '%d %b %Y') AS policy_issue_date,
+ DATE_FORMAT(pcsd.pt_policy_issue_date, '%b %Y') AS policy_issue_month,
+ pt.month as statement_month,
+
+ 'no statement uploaded' AS statement_uploaded,
+
+ CASE
+ WHEN c.client_type = 1 THEN 'Group'
+ WHEN c.client_type = 2 THEN 'Retail'
+ ELSE '-'
+ END AS client_type,
+
+ CASE
+ WHEN pt.revenue_type = 'NA' THEN 'Fresh'
+ ELSE 'Renewal'
+ END AS revenue_type,
+
+ CASE
+ WHEN pt.action_type = 'inception' THEN 'Policy'
+ ELSE 'Endorsement'
+ END AS action_type,
+
+ pt.action_type as action_type_string,
+
+ c.client_name,
+ c.short_name AS client_short_name,
+ cb.branch_name AS client_branch_name,
+ cb.address1 AS client_address,
+ ptype.policy_type,
+ ptype.bap,
+ ins.id AS insurer_id,
+ ins.name AS insurer_name,
+ ins.short_name AS insurer_short_name,
+ ib.branch_name AS insurer_branch_name,
+ ib.branch_code AS insurer_branch_code,
+
+ created_user.first_name AS user_name,
+ v.vehicle_no,
+ tpa.name AS tpa_name,
+
+ pcsd.remark AS remarks,
+ pcsd.cop_amt AS bp_amt,
+ pcsd.exp_amt,
+ pcsd.id AS pt_id,
+
+ su.first_name AS salse_person_name,
+ se.first_name AS service_person_name,
+
+ ROUND(pcsd.cop_amt + pcsd.cotp_amt + pcsd.cotep_amt, 2) AS premium_wo_gst,
+ ROUND((pcsd.cop_amt + pcsd.cotp_amt + pcsd.cotep_amt) * 0.18, 2) AS gst_amount,
+ ROUND((pcsd.cop_amt + pcsd.cotp_amt + pcsd.cotep_amt) * 1.18, 2) AS total_premium,
+
+ ROUND(pcsd.cotp_amt + pcsd.cotep_amt, 2) AS tp_or_ter,
+ DATEDIFF(pt.policy_end_date, CURDATE()) AS days,
+ (pcsd.agreed_tp_per + pcsd.agreed_tep_per) AS agreed_tp_or_ter_per,
+
+ ROUND(pcsd.exp_amt, 2) AS total_irda_amt,
+ 0.00 AS reward,
+ 0.00 AS billed_amt,
+
+ CASE
+ WHEN pcsd.co_share_type IN (0,1) THEN pt.policy_no
+ WHEN pcsd.co_share_type > 1 THEN
+ IFNULL(NULLIF(pcsd.follower_policy_no,''), pt.policy_no)
+ ELSE pt.policy_no
+ END AS policy_no,
+
+ CASE
+ WHEN pt.co_share = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS co_share,
+
+ CASE
+ WHEN pt.bro_payable_by = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS bro_payable_by,
+
+ pcsd.non_comm_per_amt,
+
+ pcsd.bp_igst,
+ pcsd.bp_sgst,
+ pcsd.bp_cgst,
+
+ pcsd.agreed_bp_per,
+ ROUND(pcsd.agreed_tp_per + pcsd.agreed_tep_per,2) AS agreed_tp_per,
+
+ pcsd.standerd_bp_per,
+ ROUND(pcsd.standerd_tp_per + pcsd.standerd_tep_per,2) AS standerd_tp_per,
+
+ 0 AS actual_bp_amt,
+ ROUND(0, 2) AS actual_tp_amt,
+
+ 0 AS actual_bp_per,
+ ROUND(0, 2) AS actual_tp_per,
+
+ 0 AS actual_tep_brokerage_amt,
+ ROUND(0, 2) AS actual_tp_brokerage_amt,
+
+ service_branch.branch_name as service_branch,
+
+ salse_manager.first_name as salse_manager_name,
+ service_manager.first_name as service_manager_name,
+ cd_master.cd_ac_no,
+ pt.rollover_date,
+ pt.policy_holder_name,
+
+ CASE
+ WHEN pt.same_as_proposer = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS same_as_proposer,
+
+ pcsd.follower_policy_no,
+ pcsd.co_share_per,
+ pcsd.stamp_duty
+
+ FROM policy_transaction pt
+ LEFT JOIN pt_co_share_details pcsd ON pt.id = pcsd.pt_id
+ JOIN clients c ON c.id = pt.client_id
+ LEFT JOIN client_branch cb ON pt.client_branch_id = cb.id
+ LEFT JOIN client_policy cp ON pt.client_policy_id = cp.id
+ LEFT JOIN user_profiles up ON pt.created_by = up.id
+ LEFT JOIN vehicle v ON pt.vehicle_id = v.id
+ LEFT JOIN policy_type ptype ON pt.policy_type_id = ptype.id
+ LEFT JOIN insurers ins ON pcsd.insurer_id = ins.id
+ LEFT JOIN insurer_branch ib ON pcsd.insurer_branch_id = ib.id
+ LEFT JOIN tpa ON pt.tpa_id = tpa.id
+ LEFT JOIN tpa_branch tb ON pt.tpa_branch_id = tb.id
+ LEFT JOIN user_profiles su ON pt.sales_generated_by = su.id
+ LEFT JOIN user_profiles se ON pt.serviced_by = se.id
+ LEFT JOIN user_profiles created_user ON pt.created_by = created_user.id
+ LEFT JOIN nhance_branch ON pt.issuer_branch = nhance_branch.id
+
+ LEFT JOIN nhance_branch service_branch ON pt.service_person_branch_id = service_branch.id
+ LEFT JOIN user_profiles salse_manager ON pt.salse_person_manager_id = salse_manager.id
+ LEFT JOIN user_profiles service_manager ON pt.service_person_manager_id = service_manager.id
+ LEFT JOIN cd_master ON pt.cd_ac_pk = cd_master.id
+
+ WHERE pt.is_active = 1
+ AND pcsd.is_active = 1
+ $default_date_filter
+ $conditions
+
+ GROUP BY pt.policy_no, pt.endorsement_no, pcsd.insurer_id
+
+ UNION ALL
+
+ SELECT
+ pt.id AS id,
+ pt.endorsement_no,
+ pt.ref,
+
+ pt.data_received_date,
+ pt.renewal_date,
+ pt.installment,
+ nhance_branch.branch_name as nhance_branch,
+
+ DATE_FORMAT(pt.policy_issue_date, '%d %b %Y') AS policy_issue_date,
+ DATE_FORMAT(IF(insq.month IS NULL, pcsd.pt_policy_issue_date, insq.month),'%b %Y') AS policy_issue_month,
+ insq.month as statement_month,
+
+ 'statement uploaded' AS statement_uploaded,
+
+ CASE
+ WHEN c.client_type = 1 THEN 'Group'
+ WHEN c.client_type = 2 THEN 'Retail'
+ ELSE '-'
+ END AS client_type,
+
+ CASE
+ WHEN pt.revenue_type = 'NA' THEN 'Fresh'
+ ELSE 'Renewal'
+ END AS revenue_type,
+
+ CASE
+ WHEN
+ IFNULL(cssd.actual_tep_brokerage_amt, 0) = 0
+ AND IFNULL(cssd.actual_tp_brokerage_amt, 0) = 0
+ AND IFNULL(cssd.actual_bp_brokerage_amt, 0) = 0
+ AND IFNULL(cssd.reward, 0) != 0
+ THEN 'Rewards'
+
+ WHEN pt.action_type = 'inception'
+ THEN 'Policy'
+
+ ELSE 'Endorsement'
+ END AS action_type,
+
+
+ pt.action_type as action_type_string,
+
+ c.client_name,
+ c.short_name AS client_short_name,
+ cb.branch_name AS client_branch_name,
+ cb.address1 AS client_address,
+ ptype.policy_type,
+ ptype.bap,
+ ins.id AS insurer_id,
+ ins.name AS insurer_name,
+ ins.short_name AS insurer_short_name,
+ ib.branch_name AS insurer_branch_name,
+ ib.branch_code AS insurer_branch_code,
+
+ created_user.first_name as user_name,
+
+ v.vehicle_no,
+ tpa.name AS tpa_name,
+
+ pcsd.remark AS remarks,
+ pcsd.cop_amt AS bp_amt,
+ pcsd.exp_amt,
+ pcsd.id AS pt_id,
+
+ su.first_name AS salse_person_name,
+ se.first_name AS service_person_name,
+
+ ROUND(pcsd.cop_amt + pcsd.cotp_amt + pcsd.cotep_amt,2) AS premium_wo_gst,
+ ROUND((pcsd.cop_amt + pcsd.cotp_amt + pcsd.cotep_amt) * 0.18,2) AS gst_amount,
+ ROUND((pcsd.cop_amt + pcsd.cotp_amt + pcsd.cotep_amt) * 1.18,2) AS total_premium,
+
+ ROUND(pcsd.cotp_amt + pcsd.cotep_amt,2) AS tp_or_ter,
+ DATEDIFF(pt.policy_end_date, CURDATE()) AS days,
+ (pcsd.agreed_tp_per + pcsd.agreed_tep_per) AS agreed_tp_or_ter_per,
+
+ CASE
+ WHEN insq.month = pt.month THEN pcsd.exp_amt
+ ELSE 0
+ END AS total_irda_amt_2,
+
+ COALESCE((
+ SELECT SUM(cs.reward)
+ FROM co_share_stmt_details cs
+ JOIN insurer_statements i ON cs.statement_id = i.id
+ WHERE cs.co_share_id = pcsd.id AND i.month = insq.month
+ AND i.is_active = 1
+ AND cs.is_active = 1
+ GROUP BY pt.policy_no, i.month, pcsd.insurer_id
+ ),0) AS reward,
+
+ COALESCE((
+ SELECT SUM(
+ COALESCE(cs.actual_bp_brokerage_amt,0) +
+ COALESCE(cs.actual_tp_brokerage_amt,0) +
+ COALESCE(cs.actual_tep_brokerage_amt,0) +
+ COALESCE(cs.reward,0)
+ )
+ FROM co_share_stmt_details cs
+ JOIN insurer_statements i ON cs.statement_id = i.id
+ WHERE cs.co_share_id = pcsd.id
+ AND i.invoice_status IS NOT NULL
+ AND i.month = insq.month
+ AND i.is_active = 1
+ AND cs.is_active = 1
+ GROUP BY pt.policy_no, i.month, pcsd.insurer_id
+ ),0) AS billed_amt,
+
+ CASE
+ WHEN pcsd.co_share_type IN (0,1) THEN pt.policy_no
+ WHEN pcsd.co_share_type > 1
+ THEN IFNULL(NULLIF(pcsd.follower_policy_no,''), pt.policy_no)
+ ELSE pt.policy_no
+ END AS policy_no,
+
+ CASE
+ WHEN pt.co_share = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS co_share,
+
+ CASE
+ WHEN pt.bro_payable_by = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS bro_payable_by,
+
+ pcsd.non_comm_per_amt,
+
+ pcsd.bp_igst,
+ pcsd.bp_sgst,
+ pcsd.bp_cgst,
+
+ pcsd.agreed_bp_per,
+ ROUND(pcsd.agreed_tp_per + pcsd.agreed_tep_per,2) AS agreed_tp_per,
+
+ pcsd.standerd_bp_per,
+ ROUND(pcsd.standerd_tp_per + pcsd.standerd_tep_per,2) AS standerd_tp_per,
+
+ cssd.actual_bp_amt,
+ ROUND(cssd.actual_tp_amt + cssd.actual_tep_amt,2) AS actual_tp_amt,
+
+ cssd.actual_bp_per,
+ ROUND(cssd.actual_tp_per + cssd.actual_tep_per,2) AS actual_tp_per,
+
+ cssd.actual_tep_brokerage_amt,
+ ROUND(cssd.actual_tp_brokerage_amt + cssd.actual_bp_brokerage_amt,2) AS actual_tp_brokerage_amt,
+
+ service_branch.branch_name as service_branch,
+
+ salse_manager.first_name as salse_manager_name,
+ service_manager.first_name as service_manager_name,
+ cd_master.cd_ac_no,
+ pt.rollover_date,
+ pt.policy_holder_name,
+
+ CASE
+ WHEN pt.same_as_proposer = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS same_as_proposer,
+
+ pcsd.follower_policy_no,
+ pcsd.co_share_per,
+ pcsd.stamp_duty
+
+ FROM policy_transaction pt
+ LEFT JOIN pt_co_share_details pcsd ON pt.id = pcsd.pt_id
+ LEFT JOIN co_share_stmt_details cssd ON pcsd.id = cssd.co_share_id
+ LEFT JOIN insurer_statements insq ON cssd.statement_id = insq.id
+ JOIN clients c ON c.id = pt.client_id
+ LEFT JOIN client_branch cb ON pt.client_branch_id = cb.id
+ LEFT JOIN client_policy cp ON pt.client_policy_id = cp.id
+ LEFT JOIN user_profiles up ON pt.created_by = up.id
+ LEFT JOIN vehicle v ON pt.vehicle_id = v.id
+ LEFT JOIN policy_type ptype ON pt.policy_type_id = ptype.id
+ LEFT JOIN insurers ins ON pcsd.insurer_id = ins.id
+ LEFT JOIN insurer_branch ib ON pcsd.insurer_branch_id = ib.id
+ LEFT JOIN tpa ON pt.tpa_id = tpa.id
+ LEFT JOIN tpa_branch tb ON pt.tpa_branch_id = tb.id
+ LEFT JOIN user_profiles su ON pt.sales_generated_by = su.id
+ LEFT JOIN user_profiles se ON pt.serviced_by = se.id
+ LEFT JOIN user_profiles created_user ON pt.created_by = created_user.id
+ LEFT JOIN nhance_branch ON pt.issuer_branch = nhance_branch.id
+
+ LEFT JOIN nhance_branch service_branch ON pt.service_person_branch_id = service_branch.id
+ LEFT JOIN user_profiles salse_manager ON pt.salse_person_manager_id = salse_manager.id
+ LEFT JOIN user_profiles service_manager ON pt.service_person_manager_id = service_manager.id
+ LEFT JOIN cd_master ON pt.cd_ac_pk = cd_master.id
+
+
+ WHERE pt.is_active = 1
+ AND pcsd.is_active = 1
+ AND cssd.is_active = 1
+ AND insq.is_active = 1
+ $default_date_filter
+ $conditions
+
+ GROUP BY pt.policy_no, pt.endorsement_no, pcsd.insurer_id, insq.month
+
+ ) AS final_result
+
+ $statement_month_condition
+ -- GROUP BY statement_month, insurer_name, policy_no
+ -- WHERE id = 6063
+
+ ORDER BY
+ id DESC,
+ policy_no ASC,
+ insurer_branch_name ASC,
+ statement_uploaded ASC,
+ statement_month ASC,
+ STR_TO_DATE(policy_issue_month, '%b %Y') ASC
+ ";
+ $query = $this->db->query($sql);
+ $result = $query->getResultArray();
+ // dd($result);
+ // dd($this->db->getLastQuery());
+
+ $keys = [];
+ $filtered = [];
+
+ foreach ($result as $row) {
+
+ $endorsement_number = "-";
+ if(!empty($row['endorsement_no'])){
+ $endorsement_number = $row['endorsement_no'];
+ }
+
+ $key = $row['statement_month'].'|'.$row['insurer_name'].'|'.$row['policy_no'] . '|' . $endorsement_number;
+
+ // If uploaded record exists, always keep it and override previous
+ if ($row['statement_uploaded'] === 'statement uploaded') {
+ $filtered[$key] = $row; // overwrite no-statement row if it exists
+ $keys[$key] = true;
+
+ }
+ // Keep "no statement uploaded" only if uploaded one not added yet
+ elseif (!isset($keys[$key])) {
+ $filtered[$key] = $row;
+ }
+ }
+
+ $result = array_values($filtered);
+ // dd($result);
+
+ // $runningBilled = [];
+ // $totalIrdaMap = [];
+ // $final = [];
+
+ // foreach ($result as $row) {
+ // $ptId = $row['pt_id'];
+
+ // // Store total_irda_amt only once (first non-zero value)
+ // if (!isset($totalIrdaMap[$ptId]) && $row['total_irda_amt'] > 0) {
+ // $totalIrdaMap[$ptId] = $row['total_irda_amt'];
+ // }
+
+ // // Initialize running sum
+ // if (!isset($runningBilled[$ptId])) {
+ // $runningBilled[$ptId] = 0;
+ // }
+
+ // // Add billed amount to running total
+ // $runningBilled[$ptId] += (float)$row['billed_amt'];
+
+ // // Calculate unbilled amount
+ // $row['unbilled_amount'] = ($totalIrdaMap[$ptId] ?? 0) - $runningBilled[$ptId];
+
+ // $final[] = $row;
+ // }
+
+ $totalBilled = [];
+ $totalIrdaMap = [];
+
+ foreach ($result as $row) {
+ $key = $row['pt_id'].'-'.$row['insurer_id'];
+
+ // Sum billed amount
+ $totalBilled[$key] = ($totalBilled[$key] ?? 0)
+ + (float) ($row['billed_amt'] ?? 0);
+
+ // Store total_irda_amt once
+ if (!isset($totalIrdaMap[$key]) && ($row['total_irda_amt'] ?? 0) > 0) {
+ $totalIrdaMap[$key] = (float) $row['total_irda_amt'];
+ }
+ }
+
+
+ $ptSeen = [];
+ $final = [];
+
+ foreach ($result as $row) {
+ $ptId = $row['pt_id'].'-'.$row['insurer_id'];
+ if (!isset($ptSeen[$ptId])) {
+ // First entry → set unbilled amount
+ $row['unbilled_amount'] = ($totalIrdaMap[$ptId] ?? 0) - ($totalBilled[$ptId] ?? 0);
+
+ $ptSeen[$ptId] = true;
+ } else {
+ // Other entries → zero
+ $row['unbilled_amount'] = 0;
+ }
+
+ $final[] = $row;
+ }
+
+ $result = $final;
+
+ // print_rr($result); die;
+ return $result;
+ }
+
public function getBDSReportList($start_date = 0, $end_date = 0, $client_id = 0, $insurer_id = 0, $policy_type_id = 0, $date_type = 0, $issuer = 0, $client_branch_id = 0, $insurer_branch_id = 0, $client_policy_id = 0, $user_id = 0, $where = [])
{
@@ -2572,6 +3141,7 @@
cb.address1 AS client_address,
ptype.policy_type,
ptype.bap,
+ ins.id AS insurer_id,
ins.name AS insurer_name,
ins.short_name AS insurer_short_name,
ib.branch_name AS insurer_branch_name,
@@ -2596,7 +3166,6 @@
ROUND(pcsd.cotp_amt + pcsd.cotep_amt, 2) AS tp_or_ter,
DATEDIFF(pt.policy_end_date, CURDATE()) AS days,
(pcsd.agreed_tp_per + pcsd.agreed_tep_per) AS agreed_tp_or_ter_per,
- pcsd.agreed_bp_per,
ROUND(pcsd.exp_amt, 2) AS total_irda_amt,
0.00 AS reward,
@@ -2607,7 +3176,56 @@
WHEN pcsd.co_share_type > 1 THEN
IFNULL(NULLIF(pcsd.follower_policy_no,''), pt.policy_no)
ELSE pt.policy_no
- END AS policy_no
+ END AS policy_no,
+
+ CASE
+ WHEN pt.co_share = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS co_share,
+
+ CASE
+ WHEN pt.bro_payable_by = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS bro_payable_by,
+
+ pcsd.non_comm_per_amt,
+
+ pcsd.bp_igst,
+ pcsd.bp_sgst,
+ pcsd.bp_cgst,
+
+ pcsd.agreed_bp_per,
+ ROUND(pcsd.agreed_tp_per + pcsd.agreed_tep_per,2) AS agreed_tp_per,
+
+ pcsd.standerd_bp_per,
+ ROUND(pcsd.standerd_tp_per + pcsd.standerd_tep_per,2) AS standerd_tp_per,
+
+ 0 AS actual_bp_amt,
+ ROUND(0, 2) AS actual_tp_amt,
+
+ 0 AS actual_bp_per,
+ ROUND(0, 2) AS actual_tp_per,
+
+ 0 AS actual_tep_brokerage_amt,
+ ROUND(0, 2) AS actual_tp_brokerage_amt,
+
+ service_branch.branch_name as service_branch,
+
+ salse_manager.first_name as salse_manager_name,
+ service_manager.first_name as service_manager_name,
+ cd_master.cd_ac_no,
+ pt.rollover_date,
+ pt.policy_holder_name,
+
+ CASE
+ WHEN pt.same_as_proposer = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS same_as_proposer,
+
+ pcsd.follower_policy_no,
+ pcsd.co_share_per,
+ pcsd.stamp_duty,
+ 'first' as query
FROM policy_transaction pt
LEFT JOIN pt_co_share_details pcsd ON pt.id = pcsd.pt_id
@@ -2626,6 +3244,11 @@
LEFT JOIN user_profiles created_user ON pt.created_by = created_user.id
LEFT JOIN nhance_branch ON pt.issuer_branch = nhance_branch.id
+ LEFT JOIN nhance_branch service_branch ON pt.service_person_branch_id = service_branch.id
+ LEFT JOIN user_profiles salse_manager ON pt.salse_person_manager_id = salse_manager.id
+ LEFT JOIN user_profiles service_manager ON pt.service_person_manager_id = service_manager.id
+ LEFT JOIN cd_master ON pt.cd_ac_pk = cd_master.id
+
WHERE pt.is_active = 1
AND pcsd.is_active = 1
$default_date_filter
@@ -2675,20 +3298,13 @@
cb.address1 AS client_address,
ptype.policy_type,
ptype.bap,
+ ins.id AS insurer_id,
ins.name AS insurer_name,
ins.short_name AS insurer_short_name,
ib.branch_name AS insurer_branch_name,
ib.branch_code AS insurer_branch_code,
- CASE
- WHEN
- IFNULL(cssd.actual_tep_brokerage_amt,0) = 0 AND
- IFNULL(cssd.actual_tp_brokerage_amt,0) = 0 AND
- IFNULL(cssd.actual_bp_brokerage_amt,0) = 0 AND
- IFNULL(cssd.reward,0) != 0
- THEN 'Rewards'
- ELSE created_user.first_name
- END AS user_name,
+ created_user.first_name as user_name,
v.vehicle_no,
tpa.name AS tpa_name,
@@ -2708,29 +3324,19 @@
ROUND(pcsd.cotp_amt + pcsd.cotep_amt,2) AS tp_or_ter,
DATEDIFF(pt.policy_end_date, CURDATE()) AS days,
(pcsd.agreed_tp_per + pcsd.agreed_tep_per) AS agreed_tp_or_ter_per,
- pcsd.agreed_bp_per,
CASE
WHEN insq.month = pt.month THEN pcsd.exp_amt
ELSE 0
END AS total_irda_amt_2,
- COALESCE((
- SELECT SUM(cs.reward)
- FROM co_share_stmt_details cs
- JOIN insurer_statements i ON cs.statement_id = i.id
- WHERE cs.co_share_id = pcsd.id AND i.month = insq.month
- AND i.is_active = 1
- AND cs.is_active = 1
- GROUP BY pt.policy_no, i.month, pcsd.insurer_id
- ),0) AS reward,
+ 0 AS reward,
COALESCE((
SELECT SUM(
COALESCE(cs.actual_bp_brokerage_amt,0) +
COALESCE(cs.actual_tp_brokerage_amt,0) +
- COALESCE(cs.actual_tep_brokerage_amt,0) +
- COALESCE(cs.reward,0)
+ COALESCE(cs.actual_tep_brokerage_amt,0)
)
FROM co_share_stmt_details cs
JOIN insurer_statements i ON cs.statement_id = i.id
@@ -2747,7 +3353,56 @@
WHEN pcsd.co_share_type > 1
THEN IFNULL(NULLIF(pcsd.follower_policy_no,''), pt.policy_no)
ELSE pt.policy_no
- END AS policy_no
+ END AS policy_no,
+
+ CASE
+ WHEN pt.co_share = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS co_share,
+
+ CASE
+ WHEN pt.bro_payable_by = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS bro_payable_by,
+
+ pcsd.non_comm_per_amt,
+
+ pcsd.bp_igst,
+ pcsd.bp_sgst,
+ pcsd.bp_cgst,
+
+ pcsd.agreed_bp_per,
+ ROUND(pcsd.agreed_tp_per + pcsd.agreed_tep_per,2) AS agreed_tp_per,
+
+ pcsd.standerd_bp_per,
+ ROUND(pcsd.standerd_tp_per + pcsd.standerd_tep_per,2) AS standerd_tp_per,
+
+ cssd.actual_bp_amt,
+ ROUND(cssd.actual_tp_amt + cssd.actual_tep_amt,2) AS actual_tp_amt,
+
+ cssd.actual_bp_per,
+ ROUND(cssd.actual_tp_per + cssd.actual_tep_per,2) AS actual_tp_per,
+
+ cssd.actual_tep_brokerage_amt,
+ ROUND(cssd.actual_tp_brokerage_amt + cssd.actual_bp_brokerage_amt,2) AS actual_tp_brokerage_amt,
+
+ service_branch.branch_name as service_branch,
+
+ salse_manager.first_name as salse_manager_name,
+ service_manager.first_name as service_manager_name,
+ cd_master.cd_ac_no,
+ pt.rollover_date,
+ pt.policy_holder_name,
+
+ CASE
+ WHEN pt.same_as_proposer = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS same_as_proposer,
+
+ pcsd.follower_policy_no,
+ pcsd.co_share_per,
+ pcsd.stamp_duty,
+ 'second' as query
FROM policy_transaction pt
LEFT JOIN pt_co_share_details pcsd ON pt.id = pcsd.pt_id
@@ -2768,6 +3423,12 @@
LEFT JOIN user_profiles created_user ON pt.created_by = created_user.id
LEFT JOIN nhance_branch ON pt.issuer_branch = nhance_branch.id
+ LEFT JOIN nhance_branch service_branch ON pt.service_person_branch_id = service_branch.id
+ LEFT JOIN user_profiles salse_manager ON pt.salse_person_manager_id = salse_manager.id
+ LEFT JOIN user_profiles service_manager ON pt.service_person_manager_id = service_manager.id
+ LEFT JOIN cd_master ON pt.cd_ac_pk = cd_master.id
+
+
WHERE pt.is_active = 1
AND pcsd.is_active = 1
AND cssd.is_active = 1
@@ -2777,11 +3438,185 @@
GROUP BY pt.policy_no, pt.endorsement_no, pcsd.insurer_id, insq.month
+
+ UNION ALL
+
+ SELECT
+
+ pt.id AS id,
+ pt.endorsement_no,
+ pt.ref,
+
+ pt.data_received_date,
+ pt.renewal_date,
+ pt.installment,
+ nhance_branch.branch_name as nhance_branch,
+
+ DATE_FORMAT(pt.policy_issue_date, '%d %b %Y') AS policy_issue_date,
+ DATE_FORMAT(IF(insq.month IS NULL, pcsd.pt_policy_issue_date, insq.month),'%b %Y') AS policy_issue_month,
+ insq.month as statement_month,
+
+ 'statement uploaded' AS statement_uploaded,
+
+ CASE
+ WHEN c.client_type = 1 THEN 'Group'
+ WHEN c.client_type = 2 THEN 'Retail'
+ ELSE '-'
+ END AS client_type,
+
+ CASE
+ WHEN pt.revenue_type = 'NA' THEN 'Fresh'
+ ELSE 'Renewal'
+ END AS revenue_type,
+
+ CASE
+ WHEN IFNULL(cssd.reward,0) <> 0 THEN 'Rewards'
+ ELSE 'Rewards'
+ END AS action_type,
+
+
+ pt.action_type as action_type_string,
+
+ c.client_name,
+ c.short_name AS client_short_name,
+ cb.branch_name AS client_branch_name,
+ cb.address1 AS client_address,
+ ptype.policy_type,
+ ptype.bap,
+ ins.id AS insurer_id,
+ ins.name AS insurer_name,
+ ins.short_name AS insurer_short_name,
+ ib.branch_name AS insurer_branch_name,
+ ib.branch_code AS insurer_branch_code,
+
+ created_user.first_name as user_name,
+
+ v.vehicle_no,
+ tpa.name AS tpa_name,
+
+ pcsd.remark AS remarks,
+ pcsd.cop_amt AS bp_amt,
+ pcsd.exp_amt,
+ pcsd.id AS pt_id,
+
+ su.first_name AS salse_person_name,
+ se.first_name AS service_person_name,
+
+ ROUND(pcsd.cop_amt + pcsd.cotp_amt + pcsd.cotep_amt,2) AS premium_wo_gst,
+ ROUND((pcsd.cop_amt + pcsd.cotp_amt + pcsd.cotep_amt) * 0.18,2) AS gst_amount,
+ ROUND((pcsd.cop_amt + pcsd.cotp_amt + pcsd.cotep_amt) * 1.18,2) AS total_premium,
+
+ ROUND(pcsd.cotp_amt + pcsd.cotep_amt,2) AS tp_or_ter,
+ DATEDIFF(pt.policy_end_date, CURDATE()) AS days,
+ (pcsd.agreed_tp_per + pcsd.agreed_tep_per) AS agreed_tp_or_ter_per,
+
+ 0 AS total_irda_amt_2,
+
+ COALESCE((
+ SELECT SUM(cs.reward)
+ FROM co_share_stmt_details cs
+ JOIN insurer_statements i ON cs.statement_id = i.id
+ WHERE cs.co_share_id = pcsd.id
+ AND i.month = insq.month
+ AND i.is_active = 1
+ AND cs.is_active = 1
+ GROUP BY pt.policy_no, i.month, pcsd.insurer_id
+ ),0) AS reward,
+
+ 0 AS billed_amt,
+
+ CASE
+ WHEN pcsd.co_share_type IN (0,1) THEN pt.policy_no
+ WHEN pcsd.co_share_type > 1
+ THEN IFNULL(NULLIF(pcsd.follower_policy_no,''), pt.policy_no)
+ ELSE pt.policy_no
+ END AS policy_no,
+
+ CASE
+ WHEN pt.co_share = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS co_share,
+
+ CASE
+ WHEN pt.bro_payable_by = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS bro_payable_by,
+
+ pcsd.non_comm_per_amt,
+
+ pcsd.bp_igst,
+ pcsd.bp_sgst,
+ pcsd.bp_cgst,
+
+ pcsd.agreed_bp_per,
+ ROUND(pcsd.agreed_tp_per + pcsd.agreed_tep_per,2) AS agreed_tp_per,
+
+ pcsd.standerd_bp_per,
+ ROUND(pcsd.standerd_tp_per + pcsd.standerd_tep_per,2) AS standerd_tp_per,
+
+ cssd.actual_bp_amt,
+ ROUND(cssd.actual_tp_amt + cssd.actual_tep_amt,2) AS actual_tp_amt,
+
+ cssd.actual_bp_per,
+ ROUND(cssd.actual_tp_per + cssd.actual_tep_per,2) AS actual_tp_per,
+
+ cssd.actual_tep_brokerage_amt,
+ ROUND(cssd.actual_tp_brokerage_amt + cssd.actual_bp_brokerage_amt,2) AS actual_tp_brokerage_amt,
+
+ service_branch.branch_name as service_branch,
+
+ salse_manager.first_name as salse_manager_name,
+ service_manager.first_name as service_manager_name,
+ cd_master.cd_ac_no,
+ pt.rollover_date,
+ pt.policy_holder_name,
+
+ CASE
+ WHEN pt.same_as_proposer = 1 THEN 'Yes'
+ ELSE 'No'
+ END AS same_as_proposer,
+
+ pcsd.follower_policy_no,
+ pcsd.co_share_per,
+ pcsd.stamp_duty,
+ 'third' as query
+
+ FROM policy_transaction pt
+ LEFT JOIN pt_co_share_details pcsd ON pt.id = pcsd.pt_id
+ LEFT JOIN co_share_stmt_details cssd ON pcsd.id = cssd.co_share_id
+ LEFT JOIN insurer_statements insq ON cssd.statement_id = insq.id
+ JOIN clients c ON c.id = pt.client_id
+ LEFT JOIN client_branch cb ON pt.client_branch_id = cb.id
+ LEFT JOIN client_policy cp ON pt.client_policy_id = cp.id
+ LEFT JOIN user_profiles up ON pt.created_by = up.id
+ LEFT JOIN vehicle v ON pt.vehicle_id = v.id
+ LEFT JOIN policy_type ptype ON pt.policy_type_id = ptype.id
+ LEFT JOIN insurers ins ON pcsd.insurer_id = ins.id
+ LEFT JOIN insurer_branch ib ON pcsd.insurer_branch_id = ib.id
+ LEFT JOIN tpa ON pt.tpa_id = tpa.id
+ LEFT JOIN tpa_branch tb ON pt.tpa_branch_id = tb.id
+ LEFT JOIN user_profiles su ON pt.sales_generated_by = su.id
+ LEFT JOIN user_profiles se ON pt.serviced_by = se.id
+ LEFT JOIN user_profiles created_user ON pt.created_by = created_user.id
+ LEFT JOIN nhance_branch ON pt.issuer_branch = nhance_branch.id
+
+ LEFT JOIN nhance_branch service_branch ON pt.service_person_branch_id = service_branch.id
+ LEFT JOIN user_profiles salse_manager ON pt.salse_person_manager_id = salse_manager.id
+ LEFT JOIN user_profiles service_manager ON pt.service_person_manager_id = service_manager.id
+ LEFT JOIN cd_master ON pt.cd_ac_pk = cd_master.id
+
+ WHERE pt.is_active = 1
+ AND pcsd.is_active = 1
+ AND cssd.is_active = 1
+ AND insq.is_active = 1
+ $default_date_filter
+ $conditions
+
) AS final_result
$statement_month_condition
-- GROUP BY statement_month, insurer_name, policy_no
- -- WHERE id = 6063
+ -- WHERE id = 6143
ORDER BY
id DESC,
@@ -2793,65 +3628,97 @@
";
$query = $this->db->query($sql);
$result = $query->getResultArray();
+ // $countofalldata = count($result);
// dd($result);
// dd($this->db->getLastQuery());
- $keys = [];
+ $keys = [];
$filtered = [];
foreach ($result as $row) {
- $endorsement_number = "-";
- if(!empty($row['endorsement_no'])){
- $endorsement_number = $row['endorsement_no'];
+ // Normalize endorsement number
+ $endorsement_number = !empty($row['endorsement_no']) ? $row['endorsement_no'] : '-';
+
+ $reward = (float) ($row['reward'] ?? 0);
+ $billed_amt = (float) ($row['billed_amt'] ?? 0);
+
+ if($reward == 0.00 && $billed_amt == 0.00 && $row['statement_uploaded'] === 'statement uploaded'){
+ continue;
}
- $key = $row['statement_month'].'|'.$row['insurer_name'].'|'.$row['policy_no'] . '|' . $endorsement_number;
+ if ($reward > 0 && ($row['billed_amt'] ?? 0) == 0.00) {
+ $row['total_irda_amt'] = '0.00';
+ $row['billed_amt'] =
+ $reward; $rewardFlag = 'R'; // Reward row
+ } else {
+ $rewardFlag = 'N'; // Normal row
+ }
- // If uploaded record exists, always keep it and override previous
+ // ✅ Stable key (NO reward flag)
+ $key = implode('|', [
+ $row['statement_month'],
+ $row['insurer_name'],
+ $row['policy_no'],
+ $endorsement_number,
+ $rewardFlag
+ ]);
+
+ // Prefer "statement uploaded"
if ($row['statement_uploaded'] === 'statement uploaded') {
- $filtered[$key] = $row; // overwrite no-statement row if it exists
+ $filtered[$key] = $row;
$keys[$key] = true;
-
- }
- // Keep "no statement uploaded" only if uploaded one not added yet
+ }
+ // Keep no-statement only if uploaded not present
elseif (!isset($keys[$key])) {
$filtered[$key] = $row;
}
}
+ // Reindex final output
$result = array_values($filtered);
- // dd($result);
- $runningBilled = [];
+ // dd($countofalldata, $result);
+
+ // --------------------------------------------------------------------------------------------------------
+
+ $totalBilled = [];
$totalIrdaMap = [];
- $final = [];
foreach ($result as $row) {
- $ptId = $row['pt_id'];
+ $key = $row['pt_id'].'-'.$row['insurer_id'];
- // Store total_irda_amt only once (first non-zero value)
- if (!isset($totalIrdaMap[$ptId]) && $row['total_irda_amt'] > 0) {
- $totalIrdaMap[$ptId] = $row['total_irda_amt'];
+ // Sum billed amount
+ $totalBilled[$key] = ($totalBilled[$key] ?? 0)
+ + (float) ($row['billed_amt'] ?? 0);
+
+ // Store total_irda_amt once
+ if (!isset($totalIrdaMap[$key]) && ($row['total_irda_amt'] ?? 0) > 0) {
+ $totalIrdaMap[$key] = (float) $row['total_irda_amt'];
}
+ }
- // Initialize running sum
- if (!isset($runningBilled[$ptId])) {
- $runningBilled[$ptId] = 0;
+ $ptSeen = [];
+ $final = [];
+
+ foreach ($result as $row) {
+ $ptId = $row['pt_id'].'-'.$row['insurer_id'];
+ if (!isset($ptSeen[$ptId])) {
+ // First entry → set unbilled amount
+ $row['unbilled_amount'] = (float) (($totalIrdaMap[$ptId] ?? 0) - ($totalBilled[$ptId] ?? 0)) ?? '0.00';
+
+ $ptSeen[$ptId] = true;
+ } else {
+ // Other entries → zero
+ $row['unbilled_amount'] = '0.00';
}
- // Add billed amount to running total
- $runningBilled[$ptId] += (float)$row['billed_amt'];
-
- // Calculate unbilled amount
- $row['unbilled_amount'] = ($totalIrdaMap[$ptId] ?? 0) - $runningBilled[$ptId] ;
-
$final[] = $row;
}
$result = $final;
- // print_rr($result); die;
+ // dd($result); die;
return $result;
}
diff --git a/app/Models/SalesActivityModel.php b/app/Models/SalesActivityModel.php
new file mode 100644
index 00000000..dc70086c
--- /dev/null
+++ b/app/Models/SalesActivityModel.php
@@ -0,0 +1,166 @@
+ 'required|integer',
+ 'activity_type' => 'required|in_list[Call,Email,Meeting,Demo,Share,Todo]',
+ 'notes' => 'required',
+ 'scheduled_date' => 'required|valid_date',
+ 'assigned_to' => 'required|integer',
+ 'status' => 'in_list[pending,completed]',
+ ];
+
+ protected $validationMessages = [
+ 'lead_id' => [
+ 'required' => 'Lead ID is required',
+ ],
+ 'activity_type' => [
+ 'required' => 'Activity type is required',
+ ],
+ 'notes' => [
+ 'required' => 'Activity notes are required',
+ ],
+ ];
+
+ protected $skipValidation = false;
+
+ /**
+ * Get activities by lead with user details
+ */
+ public function getActivitiesByLead($leadId, $status = null)
+ {
+ $builder = $this->select('activities.*, user_profiles.username as assigned_to_name')
+ ->join('user_profiles', 'user_profiles.id = activities.assigned_to', 'left')
+ ->where('activities.lead_id', $leadId);
+
+ if ($status) {
+ $builder->where('activities.status', $status);
+ }
+
+ return $builder->orderBy('activities.scheduled_date', 'DESC')->findAll();
+ }
+
+ /**
+ * Get all activities with filters
+ */
+ public function getActivitiesWithFilters($filters = [], $limit = 10, $offset = 0)
+ {
+ $builder = $this->select('activities.*, actual_leads.company_name, user_profiles.username as assigned_to_name')
+ ->join('actual_leads', 'actual_leads.lead_id = activities.lead_id', 'left')
+ ->join('user_profiles', 'user_profiles.id = activities.assigned_to', 'left');
+
+ if (!empty($filters['status'])) {
+ $builder->where('activities.status', $filters['status']);
+ }
+
+ if (!empty($filters['activity_type'])) {
+ $builder->where('activities.activity_type', $filters['activity_type']);
+ }
+
+ if (!empty($filters['assigned_to'])) {
+ $builder->where('activities.assigned_to', $filters['assigned_to']);
+ }
+
+ if (!empty($filters['date_from'])) {
+ $builder->where('activities.scheduled_date >=', $filters['date_from']);
+ }
+
+ if (!empty($filters['date_to'])) {
+ $builder->where('activities.scheduled_date <=', $filters['date_to']);
+ }
+
+ return [
+ 'data' => $builder->orderBy('activities.scheduled_date', 'DESC')
+ ->limit($limit, $offset)->findAll(),
+ 'total' => $builder->countAllResults(false)
+ ];
+ }
+
+ /**
+ * Complete an activity
+ */
+ public function completeActivity($activityId, $completionData)
+ {
+ return $this->update($activityId, [
+ 'status' => 'completed',
+ 'completion_notes' => $completionData['completion_notes'],
+ 'completed_date' => date('Y-m-d H:i:s'),
+ 'updated_by' => $completionData['updated_by'] ?? null
+ ]);
+ }
+
+ /**
+ * Get pending activities count by user
+ */
+ public function getPendingActivitiesCount($userId)
+ {
+ return $this->where('assigned_to', $userId)
+ ->where('status', 'pending')
+ ->where('scheduled_date <=', date('Y-m-d H:i:s'))
+ ->countAllResults();
+ }
+
+ /**
+ * Get upcoming activities for a user
+ */
+ public function getUpcomingActivities($userId, $days = 7, $limit = 10)
+ {
+ $endDate = date('Y-m-d H:i:s', strtotime("+{$days} days"));
+
+ return $this->select('activities.*, actual_leads.company_name')
+ ->join('actual_leads', 'actual_leads.lead_id = activities.lead_id', 'left')
+ ->where('activities.assigned_to', $userId)
+ ->where('activities.status', 'pending')
+ ->where('activities.scheduled_date <=', $endDate)
+ ->orderBy('activities.scheduled_date', 'ASC')
+ ->limit($limit)
+ ->findAll();
+ }
+
+ /**
+ * Get activity timeline for a lead
+ */
+ public function getActivityTimeline($leadId)
+ {
+ return $this->select('activities.*, user_profiles.username as assigned_to_name')
+ ->join('user_profiles', 'user_profiles.id = activities.assigned_to', 'left')
+ ->where('activities.lead_id', $leadId)
+ ->orderBy('activities.scheduled_date', 'DESC')
+ ->findAll();
+ }
+}
\ No newline at end of file
diff --git a/app/Models/SalesActualLeadModel.php b/app/Models/SalesActualLeadModel.php
new file mode 100644
index 00000000..6b8aee8f
--- /dev/null
+++ b/app/Models/SalesActualLeadModel.php
@@ -0,0 +1,145 @@
+ 'required|min_length[2]|max_length[255]',
+ 'email' => 'required|valid_email|max_length[255]',
+ 'phone' => 'required|min_length[10]|max_length[20]',
+ 'status' => 'in_list[New,Potential,Prospects,Non prospects]',
+ ];
+
+ protected $validationMessages = [
+ 'company_name' => [
+ 'required' => 'Company name is required',
+ ],
+ 'email' => [
+ 'required' => 'Email is required',
+ 'valid_email' => 'Please provide a valid email address',
+ ],
+ 'phone' => [
+ 'required' => 'Phone number is required',
+ ],
+ ];
+
+ protected $skipValidation = false;
+
+ /**
+ * Get lead with assigned user details
+ */
+ public function getLeadWithUser($leadId)
+ {
+ return $this->select('actual_leads.*, user_profiles.username as assigned_to_name, user_profiles.email as assigned_to_email')
+ ->join('user_profiles', 'user_profiles.id = actual_leads.assigned_to', 'left')
+ ->where('actual_leads.lead_id', $leadId)
+ ->first();
+ }
+
+ /**
+ * Get all leads with pagination and filters
+ */
+ public function getLeadsWithFilters($filters = [], $limit = 10, $offset = 0)
+ {
+ $builder = $this->select('actual_leads.*, user_profiles.username as assigned_to_name')
+ ->join('user_profiles', 'user_profiles.id = actual_leads.assigned_to', 'left');
+
+ if (!empty($filters['status'])) {
+ $builder->where('actual_leads.status', $filters['status']);
+ }
+
+ if (!empty($filters['assigned_to'])) {
+ $builder->where('actual_leads.assigned_to', $filters['assigned_to']);
+ }
+
+ if (!empty($filters['search'])) {
+ $builder->groupStart()
+ ->like('actual_leads.company_name', $filters['search'])
+ ->orLike('actual_leads.email', $filters['search'])
+ ->orLike('actual_leads.phone', $filters['search'])
+ ->groupEnd();
+ }
+
+ return [
+ 'data' => $builder->limit($limit, $offset)->findAll(),
+ 'total' => $builder->countAllResults(false)
+ ];
+ }
+
+ /**
+ * Get lead with all related data (contacts, activities, notes)
+ */
+ public function getLeadComplete($leadId)
+ {
+ $lead = $this->getLeadWithUser($leadId);
+
+ if (!$lead) {
+ return null;
+ }
+
+ $contactModel = new SalesContactPersonModel();
+ $activityModel = new SalesActivityModel();
+ $noteModel = new SalesLeadNoteModel();
+
+ $lead['contact_persons'] = $contactModel->where('lead_id', $leadId)->findAll();
+ $lead['activities'] = $activityModel->getActivitiesByLead($leadId);
+ $lead['notes'] = $noteModel->getNotesByLead($leadId);
+
+ return $lead;
+ }
+
+ /**
+ * Get leads statistics
+ */
+ public function getLeadStats($userId = null)
+ {
+ $builder = $this->builder();
+
+ if ($userId) {
+ $builder->where('assigned_to', $userId);
+ }
+
+ $stats = [
+ 'total' => $builder->countAllResults(false),
+ 'new' => $builder->where('status', 'New')->countAllResults(false),
+ 'potential' => $builder->where('status', 'Potential')->countAllResults(false),
+ 'prospects' => $builder->where('status', 'Prospects')->countAllResults(false),
+ 'non_prospects' => $builder->where('status', 'Non prospects')->countAllResults(false),
+ ];
+
+ return $stats;
+ }
+}
\ No newline at end of file
diff --git a/app/Models/SalesContactPersonModel.php b/app/Models/SalesContactPersonModel.php
new file mode 100644
index 00000000..3a7073bb
--- /dev/null
+++ b/app/Models/SalesContactPersonModel.php
@@ -0,0 +1,95 @@
+ 'required|integer',
+ 'name' => 'required|min_length[2]|max_length[100]',
+ 'mobile' => 'required|min_length[10]|max_length[20]',
+ ];
+
+ protected $validationMessages = [
+ 'lead_id' => [
+ 'required' => 'Lead ID is required',
+ ],
+ 'name' => [
+ 'required' => 'Contact person name is required',
+ ],
+ 'mobile' => [
+ 'required' => 'Mobile number is required',
+ ],
+ ];
+
+ protected $skipValidation = false;
+
+ /**
+ * Get all contacts for a lead
+ */
+ public function getContactsByLead($leadId)
+ {
+ return $this->where('lead_id', $leadId)
+ ->orderBy('is_primary', 'DESC')
+ ->findAll();
+ }
+
+ /**
+ * Get primary contact for a lead
+ */
+ public function getPrimaryContact($leadId)
+ {
+ return $this->where('lead_id', $leadId)
+ ->where('is_primary', 1)
+ ->first();
+ }
+
+ /**
+ * Set a contact as primary (unset others)
+ */
+ public function setPrimaryContact($contactId, $leadId)
+ {
+ $this->db->transStart();
+
+ // Unset all primary contacts for this lead
+ $this->where('lead_id', $leadId)
+ ->set(['is_primary' => 0])
+ ->update();
+
+ // Set the specified contact as primary
+ $this->update($contactId, ['is_primary' => 1]);
+
+ $this->db->transComplete();
+
+ return $this->db->transStatus();
+ }
+}
\ No newline at end of file
diff --git a/app/Models/SalesLeadNoteModel.php b/app/Models/SalesLeadNoteModel.php
new file mode 100644
index 00000000..c8d9654f
--- /dev/null
+++ b/app/Models/SalesLeadNoteModel.php
@@ -0,0 +1,75 @@
+ 'required|integer',
+ 'user_id' => 'required|integer',
+ 'note_text' => 'required|min_length[3]',
+ ];
+
+ protected $validationMessages = [
+ 'lead_id' => [
+ 'required' => 'Lead ID is required',
+ ],
+ 'note_text' => [
+ 'required' => 'Note text is required',
+ ],
+ ];
+
+ protected $skipValidation = false;
+
+ /**
+ * Get notes by lead with user details
+ */
+ public function getNotesByLead($leadId)
+ {
+ return $this->select('lead_notes.*, user_profiles.username')
+ ->join('user_profiles', 'user_profiles.id = lead_notes.user_id', 'left')
+ ->where('lead_notes.lead_id', $leadId)
+ ->orderBy('lead_notes.created_at', 'DESC')
+ ->findAll();
+ }
+
+ /**
+ * Get notes by user
+ */
+ public function getNotesByUser($userId, $limit = 20, $offset = 0)
+ {
+ return $this->select('lead_notes.*, actual_leads.company_name')
+ ->join('actual_leads', 'actual_leads.lead_id = lead_notes.lead_id', 'left')
+ ->where('lead_notes.user_id', $userId)
+ ->orderBy('lead_notes.created_at', 'DESC')
+ ->limit($limit, $offset)
+ ->findAll();
+ }
+}
\ No newline at end of file
diff --git a/app/Models/TpaApiDataModel.php b/app/Models/TpaApiDataModel.php
new file mode 100644
index 00000000..50958fac
--- /dev/null
+++ b/app/Models/TpaApiDataModel.php
@@ -0,0 +1,108 @@
+ 'required|integer',
+ // 'temp_code' => 'required|string|max_length[50]',
+ // 'tname' => 'required|string|max_length[150]',
+ // 'tgender' => 'permit_empty|in_list[M,F,O]',
+ // 'tage' => 'permit_empty|integer'
+ // ];
+
+ protected $validationMessages = [];
+ protected $skipValidation = false;
+
+
+
+
+ public function insertBatchWithChunkLog( array $rows,int $chunkSize = 500,?string $logRef = null)
+ {
+ if (empty($rows)) {
+ return [
+ 'status' => false,
+ 'inserted' => 0,
+ 'failed_batches' => 0,
+ 'message' => 'No data'
+ ];
+ }
+
+ $db = \Config\Database::connect();
+ $this->skipValidation(true);
+
+ $inserted = 0;
+ $failedBatches = 0;
+ $batchNo = 0;
+
+ foreach (array_chunk($rows, $chunkSize) as $chunk) {
+ $batchNo++;
+
+ try {
+ $db->transStart();
+
+ $this->insertBatch($chunk);
+
+ if ($db->transStatus() === false) {
+ throw new \RuntimeException('DB transaction failed');
+ }
+
+ $db->transComplete();
+ $inserted += count($chunk);
+
+ } catch (\Throwable $e) {
+ $db->transRollback();
+ $failedBatches++;
+
+ // 🔴 Batch-level error log
+ log_message('error', json_encode([
+ 'type' => 'BATCH_INSERT_FAILED',
+ 'table' => $this->table,
+ 'batch_no' => $batchNo,
+ 'batch_size'=> count($chunk),
+ 'ref' => $logRef,
+ 'error' => $e->getMessage()
+ ]));
+ }
+ }
+
+ return [
+ 'status' => $failedBatches === 0,
+ 'inserted' => $inserted,
+ 'failed_batches' => $failedBatches,
+ 'total_batches' => $batchNo
+ ];
+ }
+
+}
diff --git a/app/Views/UserList.php b/app/Views/UserList.php
index 1382369b..e1f0f127 100755
--- a/app/Views/UserList.php
+++ b/app/Views/UserList.php
@@ -489,9 +489,9 @@ table.dataTable tbody td {
-
+