Merge branch 'dev' of bitbucket.org:jubilian/nhance into dev
This commit is contained in:
commit
5dd4a5fcbb
@ -139,3 +139,11 @@ LEAD_CLIENT_FROM_MAIL_ID =
|
|||||||
# BDS Daily Report Emails Configuration
|
# BDS Daily Report Emails Configuration
|
||||||
bds.dailyReportEmails =
|
bds.dailyReportEmails =
|
||||||
|
|
||||||
|
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
# MEDI ASSIST WELLNESS SSO Configuration
|
||||||
|
#--------------------------------------------------------------------
|
||||||
|
|
||||||
|
MEDIASSIST_WELLNESS_KEY =
|
||||||
|
MEDIASSIST_WELLNESS_IV =
|
||||||
|
MEDIASSIST_WELLNESS_LOGIN_URL =
|
||||||
|
|||||||
@ -221,11 +221,11 @@ class Acl
|
|||||||
],
|
],
|
||||||
'#^/rfq#' => [
|
'#^/rfq#' => [
|
||||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||||
'teams' => [SALES_TEAM_ID]
|
'teams' => [BUSINESS_SUPPORT_TEAM_ID, SALES_TEAM_ID]
|
||||||
],
|
],
|
||||||
'#^/sales#' => [
|
'#^/sales#' => [
|
||||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID],
|
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID],
|
||||||
'teams' => [SALES_TEAM_ID]
|
'teams' => [BUSINESS_SUPPORT_TEAM_ID, SALES_TEAM_ID]
|
||||||
],
|
],
|
||||||
|
|
||||||
// ===================== CMS / CONTENT =====================
|
// ===================== CMS / CONTENT =====================
|
||||||
|
|||||||
@ -69,8 +69,8 @@ class Filters extends BaseConfig
|
|||||||
'before' => [
|
'before' => [
|
||||||
'HttpRequestLog' => ['except' => 'cli/*'],
|
'HttpRequestLog' => ['except' => 'cli/*'],
|
||||||
'Cors',
|
'Cors',
|
||||||
'AclFilter' => ['except' => ['login', 'logout', 'auth/*', 'oauth2callback','claim-form-download', 'claims-feedback-form', 'autobookstackLogin','employeeRest/*','processjob','getCommission','downloadEmployeeEcardZip']],
|
'AclFilter' => ['except' => ['login', 'logout', 'auth/*', 'oauth2callback','claim-form-download', 'claims-feedback-form', 'autobookstackLogin','employeeRest/*','processjob','getCommission','downloadEmployeeEcardZip', 'downloadClaimFile/*']],
|
||||||
'SecurityInputFilter' => ['except' => ['/client/notification/create','/ticket/crud_mail_template/*','test_mail','leads/sendMail'] ],
|
'SecurityInputFilter' => ['except' => ['/client/notification/create','/ticket/crud_mail_template/*','test_mail','leads/sendMail', 'ticket/reply'] ],
|
||||||
'GlobalPostFileUploadGuard'
|
'GlobalPostFileUploadGuard'
|
||||||
// 'csrf',
|
// 'csrf',
|
||||||
// 'invalidchars',
|
// 'invalidchars',
|
||||||
|
|||||||
@ -100,6 +100,7 @@ $routes->group("/user", ["filter" => "authMVC"], function ($routes) {
|
|||||||
$routes->get("list", "UserController::list");
|
$routes->get("list", "UserController::list");
|
||||||
$routes->get("getuser/(:hash)", "UserController::getuser/$1");
|
$routes->get("getuser/(:hash)", "UserController::getuser/$1");
|
||||||
$routes->get("deactive/(:hash)", "UserController::deactive/$1");
|
$routes->get("deactive/(:hash)", "UserController::deactive/$1");
|
||||||
|
$routes->get("activateUser/(:any)", "UserController::activateUser/$1");
|
||||||
$routes->get("rolesandteams", "UserController::getRolesAndTeams");
|
$routes->get("rolesandteams", "UserController::getRolesAndTeams");
|
||||||
$routes->get("getUserActivityHistory", "UserController::getUserActivityHistory");
|
$routes->get("getUserActivityHistory", "UserController::getUserActivityHistory");
|
||||||
$routes->match(['get','post','put'], 'partner', 'UserController::partner');
|
$routes->match(['get','post','put'], 'partner', 'UserController::partner');
|
||||||
@ -227,6 +228,8 @@ $routes->group("/employee", ["filter" => "authMVC"], function ($routes) {
|
|||||||
$routes->get("retail-endorsement-list", "EmployeeController::retailendorsementlist");
|
$routes->get("retail-endorsement-list", "EmployeeController::retailendorsementlist");
|
||||||
$routes->post("retail-endorsement-save", "EmployeeController::retailendorsementsave");
|
$routes->post("retail-endorsement-save", "EmployeeController::retailendorsementsave");
|
||||||
$routes->get("getTPADataVariationReport/(:num)", "EmployeeController::getTPADataVariationReport/$1");
|
$routes->get("getTPADataVariationReport/(:num)", "EmployeeController::getTPADataVariationReport/$1");
|
||||||
|
$routes->get("getTPADataVariationReportView/(:num)", "EmployeeController::getTPADataVariationReport/$1/view");
|
||||||
|
$routes->get("proceedTPADataVariationNextStep/(:num)", "EmployeeController::proceedTPADataVariationNextStep/$1");
|
||||||
$routes->get("bulkGenerateEcardAndStoreinS3", "EmployeeController::bulkGenerateEcardAndStoreinS3");
|
$routes->get("bulkGenerateEcardAndStoreinS3", "EmployeeController::bulkGenerateEcardAndStoreinS3");
|
||||||
$routes->get('clearCdSession', 'EmployeeController::clearCdSession');
|
$routes->get('clearCdSession', 'EmployeeController::clearCdSession');
|
||||||
$routes->get('checkSessionStatus', 'EmployeeController::checkSessionStatus');
|
$routes->get('checkSessionStatus', 'EmployeeController::checkSessionStatus');
|
||||||
@ -447,6 +450,9 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) {
|
|||||||
$routes->post('insufficientCdBalanceHrMailSend', 'EmployeeController::insufficientCdBalanceHrMailSend');
|
$routes->post('insufficientCdBalanceHrMailSend', 'EmployeeController::insufficientCdBalanceHrMailSend');
|
||||||
$routes->get('getTpaClaimDumpErrorData/(:any)', 'TicketServiceController::getTpaClaimDumpErrorData/$1');
|
$routes->get('getTpaClaimDumpErrorData/(:any)', 'TicketServiceController::getTpaClaimDumpErrorData/$1');
|
||||||
$routes->get('croneDailyActivityReport', 'DashboardController::croneDailyActivityReport');
|
$routes->get('croneDailyActivityReport', 'DashboardController::croneDailyActivityReport');
|
||||||
|
$routes->get('insertSampleTpaApiData/(:any)', 'TestingController::insertSampleTpaApiData/$1');
|
||||||
|
$routes->get('listEmployeeCountByClientPolicy', 'TestingController::listEmployeeCountByClientPolicy');
|
||||||
|
$routes->get('testMediAssistWellness','TestingController::testMediAssistWellness');
|
||||||
});
|
});
|
||||||
|
|
||||||
$routes->post("policy_tranction/sendInstallmentRemainderMail","PolicyTransactionController::sendInstallmentRemainderMail");
|
$routes->post("policy_tranction/sendInstallmentRemainderMail","PolicyTransactionController::sendInstallmentRemainderMail");
|
||||||
@ -1036,6 +1042,10 @@ $routes->group('sales', function($routes) {
|
|||||||
$routes->get('dashboard', 'SalesController::dashboard');
|
$routes->get('dashboard', 'SalesController::dashboard');
|
||||||
$routes->get('branchLevelDashboard', 'SalesController::branchLevelDashboard');
|
$routes->get('branchLevelDashboard', 'SalesController::branchLevelDashboard');
|
||||||
$routes->get('salesManagerLevelDashboard', 'SalesController::salesManagerLevelDashboard');
|
$routes->get('salesManagerLevelDashboard', 'SalesController::salesManagerLevelDashboard');
|
||||||
|
|
||||||
|
//Validations
|
||||||
|
$routes->get('checkDuplicate', 'SalesController::checkDuplicate');
|
||||||
|
$routes->get('searchClients', 'SalesController::searchClients');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Expence Module Route Group
|
// Expence Module Route Group
|
||||||
|
|||||||
@ -425,13 +425,17 @@ class ApiServiceController extends BaseController
|
|||||||
$userParams = [];
|
$userParams = [];
|
||||||
foreach ($data as $row) {
|
foreach ($data as $row) {
|
||||||
|
|
||||||
if($row['wellness_vendor_id'] != null)
|
if($row['wellness_vendor_id'] == $this->vidal_primary_key)
|
||||||
{
|
{
|
||||||
$vidalApiController = new VidalApiController();
|
$vidalApiController = new VidalApiController();
|
||||||
return $vidalApiController->getWellnessSSORedirectUrl($row['email']);
|
return $vidalApiController->getWellnessSSORedirectUrl($row['email']);
|
||||||
|
|
||||||
|
}else if ($row['wellness_vendor_id'] == $this->medi_assist_primary_key)
|
||||||
|
{
|
||||||
|
$mediAssistController = new MediAssistApiController();
|
||||||
|
return $mediAssistController->getWellnessSSORedirectUrl($row['planId'], $row['memberId']);
|
||||||
}
|
}
|
||||||
else if ($row['planId'] != null) // VISIT
|
else if ($row['planId'] != null && empty($row['wellness_vendor_id'])) // VISIT
|
||||||
{
|
{
|
||||||
$userParams['name'] = $row['name'];
|
$userParams['name'] = $row['name'];
|
||||||
$userParams['email'] = $row['email'];
|
$userParams['email'] = $row['email'];
|
||||||
|
|||||||
@ -226,6 +226,14 @@ class AppContentManagementController extends AdminController
|
|||||||
|
|
||||||
if ($this->request->getMethod() === 'post') {
|
if ($this->request->getMethod() === 'post') {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------------
|
||||||
|
* STEP 1: INITIAL VALIDATION
|
||||||
|
* --------------------------------------------------------------------------
|
||||||
|
* These are the basic validation rules. For 'content' and 'notes', we only
|
||||||
|
* check if they are provided and within the allowed length.
|
||||||
|
* The more advanced security check for script tags happens next.
|
||||||
|
*/
|
||||||
$rules = [
|
$rules = [
|
||||||
'fe_id' => [
|
'fe_id' => [
|
||||||
'rules' => 'permit_empty|integer|is_natural',
|
'rules' => 'permit_empty|integer|is_natural',
|
||||||
@ -239,7 +247,7 @@ class AppContentManagementController extends AdminController
|
|||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Type is required',
|
'required' => 'Type is required',
|
||||||
'max_length' => 'Type cannot exceed 255 characters',
|
'max_length' => 'Type cannot exceed 255 characters',
|
||||||
'regex_match' => 'Type contains invalid characters'
|
'regex_match' => 'Type can contain only letters, numbers, spaces, _ and -'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'content_section' => [
|
'content_section' => [
|
||||||
@ -247,7 +255,7 @@ class AppContentManagementController extends AdminController
|
|||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Content Section is required',
|
'required' => 'Content Section is required',
|
||||||
'max_length' => 'Content Section cannot exceed 255 characters',
|
'max_length' => 'Content Section cannot exceed 255 characters',
|
||||||
'regex_match' => 'Content Section contains invalid characters'
|
'regex_match' => 'Content Section can contain only letters, numbers, spaces, _ and -'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'heading' => [
|
'heading' => [
|
||||||
@ -255,25 +263,23 @@ class AppContentManagementController extends AdminController
|
|||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Heading is required',
|
'required' => 'Heading is required',
|
||||||
'max_length' => 'Heading cannot exceed 255 characters',
|
'max_length' => 'Heading cannot exceed 255 characters',
|
||||||
'regex_match' => 'Heading contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
|
'regex_match' => 'Heading can contain only letters, numbers, spaces, and these characters: . , ; : ! ? ( ) & / -'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'content' => [
|
'content' => [
|
||||||
'rules' => 'required|max_length[5000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]',
|
'rules' => 'required|max_length[5000]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Content is required',
|
'required' => 'Content is required',
|
||||||
'max_length' => 'Content cannot exceed 5000 characters',
|
'max_length' => 'Content cannot exceed 5000 characters',
|
||||||
'regex_match' => 'Content contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
|
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'notes' => [
|
'notes' => [
|
||||||
'rules' => 'required|max_length[1500]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]',
|
'rules' => 'required|max_length[1500]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Notes are required',
|
'required' => 'Notes are required',
|
||||||
'max_length' => 'Notes cannot exceed 1500 characters',
|
'max_length' => 'Notes cannot exceed 1500 characters',
|
||||||
'regex_match' => 'Notes contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
|
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!$this->validate($rules)) {
|
if (!$this->validate($rules)) {
|
||||||
@ -284,9 +290,60 @@ class AppContentManagementController extends AdminController
|
|||||||
'errors' => $this->validator->getErrors()
|
'errors' => $this->validator->getErrors()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
$request_post_data = $this->request->getPost();
|
|
||||||
$data = sanitizeInputArrayAdvanced($request_post_data);
|
/**************************************************************************
|
||||||
$id = $data['fe_id'] ?? null;
|
* REFACTORED SANITIZATION LOGIC (XSS Protection)
|
||||||
|
**************************************************************************
|
||||||
|
*
|
||||||
|
* Per the user's request, we are avoiding the generic `sanitizeInputArrayAdvanced`
|
||||||
|
* on the `content` and `notes` fields, as they require special HTML
|
||||||
|
* handling.
|
||||||
|
*
|
||||||
|
* The new process is:
|
||||||
|
* 1. Get the raw `content` and `notes` directly from the POST request.
|
||||||
|
* 2. Perform the critical XSS validation on this raw content using `hasXssTags()`.
|
||||||
|
* If it fails, the request is rejected immediately. This satisfies all
|
||||||
|
* the failure test cases (Tests 4-9).
|
||||||
|
* 3. Take all *other* POST data and sanitize it using the generic
|
||||||
|
* `sanitizeInputArrayAdvanced` function.
|
||||||
|
* 4. Sanitize the now-validated `content` and `notes` using our specific
|
||||||
|
* `sanitizeHtml()` function, which allows safe HTML.
|
||||||
|
* 5. Combine the sanitized data into a final array for database insertion.
|
||||||
|
*
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
// Step 1: Get raw `content` and `notes`.
|
||||||
|
$rawContent = $this->request->getPost('content');
|
||||||
|
$rawNotes = $this->request->getPost('notes');
|
||||||
|
|
||||||
|
// Step 2: Perform critical XSS validation on raw input.
|
||||||
|
$xssErrors = [];
|
||||||
|
if ($this->hasXssTags($rawContent)) {
|
||||||
|
$xssErrors['content'] = 'Content contains restricted tags. Script, iframe and event handlers are not allowed';
|
||||||
|
}
|
||||||
|
if ($this->hasXssTags($rawNotes)) {
|
||||||
|
$xssErrors['notes'] = 'Notes contains restricted tags. Script, iframe and event handlers are not allowed';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($xssErrors)) {
|
||||||
|
return $this->response->setStatusCode(400)->setJSON([
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'Input validation failed',
|
||||||
|
'code' => 400,
|
||||||
|
'errors' => $xssErrors
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Sanitize all *other* POST data.
|
||||||
|
$otherPostData = $this->request->getPost();
|
||||||
|
unset($otherPostData['content'], $otherPostData['notes']);
|
||||||
|
$data = sanitizeInputArrayAdvanced($otherPostData);
|
||||||
|
|
||||||
|
// Step 4 & 5: Sanitize and re-combine `content` and `notes`.
|
||||||
|
$data['content'] = $this->sanitizeHtml($rawContent);
|
||||||
|
$data['notes'] = $this->sanitizeHtml($rawNotes);
|
||||||
|
|
||||||
|
$id = $data['fe_id'] ?? null;
|
||||||
|
|
||||||
if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) {
|
if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) {
|
||||||
return $this->response->setStatusCode(400)->setJSON([
|
return $this->response->setStatusCode(400)->setJSON([
|
||||||
@ -408,27 +465,29 @@ class AppContentManagementController extends AdminController
|
|||||||
if ($method === 'post') {
|
if ($method === 'post') {
|
||||||
$rules = [
|
$rules = [
|
||||||
'category' => [
|
'category' => [
|
||||||
'rules' => 'required|max_length[100]|alpha_numeric_space',
|
// Allow letters, numbers, spaces and / - . , " '
|
||||||
|
'rules' => 'required|max_length[100]|regex_match[/^[a-zA-Z0-9 \\/\\-\\.\,\"\\\']+$/]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Category is required',
|
'required' => 'Category is required',
|
||||||
'max_length' => 'Category cannot exceed 100 characters',
|
'max_length' => 'Category cannot exceed 100 characters',
|
||||||
'alpha_numeric_space' => 'Category contains invalid characters'
|
'regex_match' => 'Category can contain only letters, numbers, spaces, and these characters: / - . , " \'',
|
||||||
|
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'question' => [
|
'question' => [
|
||||||
'rules' => 'required|max_length[1000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]',
|
// Allow only letters, numbers, spaces and basic punctuation . , ; : ! ? ( ) & / -
|
||||||
|
'rules' => 'required|max_length[1000]|regex_match[/^[a-zA-Z0-9 _\\-.,;:!?()&\\/]+$/]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Question is required',
|
'required' => 'Question is required',
|
||||||
'max_length' => 'Question cannot exceed 1000 characters',
|
'max_length' => 'Question cannot exceed 1000 characters',
|
||||||
'regex_match' => 'Question contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
|
'regex_match' => 'Question can contain only letters, numbers, spaces, and these characters: . , ; : ! ? ( ) & / -',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'answer' => [
|
'answer' => [
|
||||||
'rules' => 'required|max_length[5000]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/\r\n]+$/]',
|
'rules' => 'required|max_length[5000]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Answer is required',
|
'required' => 'Answer is required',
|
||||||
'max_length' => 'Answer cannot exceed 5000 characters',
|
'max_length' => 'Answer cannot exceed 5000 characters',
|
||||||
'regex_match' => 'Answer contains invalid characters. Only letters, numbers, spaces and basic punctuation are allowed'
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
@ -443,9 +502,50 @@ class AppContentManagementController extends AdminController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$request_post_data = $this->request->getPost();
|
/**************************************************************************
|
||||||
$sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data);
|
* XSS PROTECTION FOR 'question' and 'answer'
|
||||||
$data = array_filter($sanitized_post_data, fn($v) => $v !== '' && $v !== null);
|
**************************************************************************
|
||||||
|
*
|
||||||
|
* Applying the same security model as `frontend_content`.
|
||||||
|
*
|
||||||
|
* 1. Validate raw `question` and `answer` for malicious tags using `hasXssTags()`.
|
||||||
|
* If found, reject the request immediately.
|
||||||
|
* 2. Sanitize all *other* fields using the generic `sanitizeInputArrayAdvanced`.
|
||||||
|
* 3. Sanitize the `question` and `answer` using the HTML-aware `sanitizeHtml()`
|
||||||
|
* function to allow safe tags before saving.
|
||||||
|
*
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
// Step 1: Validate raw input for XSS threats.
|
||||||
|
$rawQuestion = $this->request->getPost('question');
|
||||||
|
$rawAnswer = $this->request->getPost('answer');
|
||||||
|
$xssErrors = [];
|
||||||
|
|
||||||
|
if ($this->hasXssTags($rawQuestion)) {
|
||||||
|
$xssErrors['question'] = 'Question contains restricted tags. Script, iframe and event handlers are not allowed';
|
||||||
|
}
|
||||||
|
if ($this->hasXssTags($rawAnswer)) {
|
||||||
|
$xssErrors['answer'] = 'Answer contains restricted tags. Script, iframe and event handlers are not allowed';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($xssErrors)) {
|
||||||
|
return $this->response->setStatusCode(400)->setJSON([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Input validation failed',
|
||||||
|
'code' => 400,
|
||||||
|
'errors' => $xssErrors,
|
||||||
|
'ref' => $ref
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2 & 3: Sanitize and combine data.
|
||||||
|
$otherPostData = $this->request->getPost();
|
||||||
|
unset($otherPostData['question'], $otherPostData['answer']);
|
||||||
|
$data = sanitizeInputArrayAdvanced($otherPostData);
|
||||||
|
|
||||||
|
$data['question'] = $this->sanitizeHtml($rawQuestion);
|
||||||
|
$data['answer'] = $this->sanitizeHtml($rawAnswer);
|
||||||
|
|
||||||
$id = $data['faq_id'] ?? null;
|
$id = $data['faq_id'] ?? null;
|
||||||
|
|
||||||
if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) {
|
if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) {
|
||||||
@ -470,12 +570,6 @@ class AppContentManagementController extends AdminController
|
|||||||
$msg = "Updated";
|
$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([
|
return $this->response->setJSON([
|
||||||
'status' => $status ? 'success' : 'error',
|
'status' => $status ? 'success' : 'error',
|
||||||
'message' => "FAQ $msg " . ($status ? 'successfully' : 'failed'),
|
'message' => "FAQ $msg " . ($status ? 'successfully' : 'failed'),
|
||||||
@ -611,4 +705,105 @@ class AppContentManagementController extends AdminController
|
|||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// Add these two private methods inside AppContentManagementController
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* =================================================================================
|
||||||
|
* HTML SANITIZATION & VALIDATION HELPER METHODS
|
||||||
|
* =================================================================================
|
||||||
|
* The following two methods are the core of the XSS protection logic.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sanitizeHtml()
|
||||||
|
*
|
||||||
|
* This function cleans a string of HTML, ensuring it is safe to display in a browser.
|
||||||
|
* It allows a specific set of safe HTML tags and removes any dangerous attributes
|
||||||
|
* from those tags.
|
||||||
|
*
|
||||||
|
* @param string $input The raw HTML string from user input.
|
||||||
|
* @return string The cleaned, safe HTML string.
|
||||||
|
*/
|
||||||
|
private function sanitizeHtml(string $input): string
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Define a whitelist of allowed HTML tags. Any tag not in this list will be
|
||||||
|
* completely removed. We are allowing basic formatting, lists, tables, etc.
|
||||||
|
*/
|
||||||
|
// ✅ Added <s>, <u>, <h1>-<h6>, <blockquote>, <pre>, <code>, <hr> for Jodit support
|
||||||
|
$allowed_tags = '<p><b><i><s><u><strong><em><ul><ol><li><br><a><img><table><thead><tbody><tr><th><td><span><div><h1><h2><h3><h4><h5><h6><blockquote><pre><code><hr><sub><sup>';
|
||||||
|
|
||||||
|
// Use strip_tags() to remove all tags that are not in our whitelist.
|
||||||
|
$clean = strip_tags($input, $allowed_tags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a blacklist of dangerous attributes. These are often used for XSS
|
||||||
|
* attacks (e.g., `onclick`, `onmouseover`). We search for and remove these
|
||||||
|
* attributes from any remaining tags.
|
||||||
|
*/
|
||||||
|
$dangerous_attrs = [
|
||||||
|
'/\s*on\w+\s*=\s*["\'][^"\']*["\']/i', // e.g., onclick="..."
|
||||||
|
'/\s*on\w+\s*=\s*[^\s>]*/i', // e.g., onclick=...
|
||||||
|
'/\s*javascript\s*:[^"\'"]*/i', // e.g., href="javascript:..."
|
||||||
|
'/\s*vbscript\s*:[^"\'"]*/i', // e.g., href="vbscript:..."
|
||||||
|
];
|
||||||
|
|
||||||
|
// Use preg_replace to find and remove the dangerous attributes.
|
||||||
|
foreach ($dangerous_attrs as $pattern) {
|
||||||
|
$clean = preg_replace($pattern, '', $clean);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hasXssTags()
|
||||||
|
*
|
||||||
|
* This function scans a string for common XSS-related tags, protocols, and event
|
||||||
|
* handlers. It is used as a primary check to quickly reject any input that is
|
||||||
|
* clearly malicious.
|
||||||
|
*
|
||||||
|
* @param string $str The raw string from user input.
|
||||||
|
* @return bool Returns `true` if a dangerous pattern is found, `false` otherwise.
|
||||||
|
*/
|
||||||
|
private function hasXssTags(string $str): bool
|
||||||
|
{
|
||||||
|
// Decode the string to handle entities (e.g., `%3Cscript%3E`) and prevent evasion.
|
||||||
|
$decoded = html_entity_decode($str, ENT_QUOTES, 'UTF-8');
|
||||||
|
$decoded = urldecode($decoded);
|
||||||
|
$decoded = str_replace(["\0", "\x00"], '', $decoded); // Remove null bytes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a blacklist of dangerous patterns. This includes tags like `<script>`
|
||||||
|
* and `<iframe>`, as well as patterns like `javascript:` and `onclick=`.
|
||||||
|
* The `/i` flag makes the search case-insensitive.
|
||||||
|
*/
|
||||||
|
$dangerous_patterns = [
|
||||||
|
'/<\s*script/i', // <script
|
||||||
|
'/<\s*\/\s*script/i', // </script
|
||||||
|
'/javascript\s*:/i', // javascript:
|
||||||
|
'/vbscript\s*:/i', // vbscript:
|
||||||
|
'/<\s*iframe/i', // <iframe>
|
||||||
|
'/<\s*object/i', // <object>
|
||||||
|
'/<\s*embed/i', // <embed>
|
||||||
|
'/<\s*applet/i', // <applet>
|
||||||
|
'/on\w+\s*=/i', // on...= (e.g., onclick=, onmouseover=, onerror=)
|
||||||
|
'/data\s*:\s*text\/html/i', // data:text/html
|
||||||
|
'/expression\s*\(/i', // CSS expression()
|
||||||
|
'/\balert\s*\(/i', // alert(...)
|
||||||
|
];
|
||||||
|
|
||||||
|
// Loop through the patterns and check if any of them exist in the decoded string.
|
||||||
|
foreach ($dangerous_patterns as $pattern) {
|
||||||
|
if (preg_match($pattern, $decoded)) return true; // Found a threat
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, no threats were found.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1004,8 +1004,8 @@ class ClientController extends AdminController
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
'short_name' => [
|
'short_name' => [
|
||||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]|min_length[2]|max_length[15]',
|
'rules' => 'required|regex_match[/^[a-zA-Z0-9_\- ]+$/]|min_length[2]|max_length[15]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Client Short Name is required',
|
'required' => 'Client Short Name is required',
|
||||||
'regex_match' => 'Client Short Name can only contain letters, numbers, hyphens and underscores.',
|
'regex_match' => 'Client Short Name can only contain letters, numbers, hyphens and underscores.',
|
||||||
'min_length' => 'Client Short Name must be at least 2 characters.',
|
'min_length' => 'Client Short Name must be at least 2 characters.',
|
||||||
@ -1013,9 +1013,8 @@ class ClientController extends AdminController
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
'pan' => [
|
'pan' => [
|
||||||
'rules' => 'required|regex_match[/^[A-Z]{5}[0-9]{4}[A-Z]$/]',
|
'rules' => 'permit_empty|regex_match[/^[A-Z]{5}[0-9]{4}[A-Z]$/]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'PAN Number is required.',
|
|
||||||
'regex_match' => 'Invalid PAN format. Example: ABCDE1234F'
|
'regex_match' => 'Invalid PAN format. Example: ABCDE1234F'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -1120,7 +1119,7 @@ class ClientController extends AdminController
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
'short_name' => [
|
'short_name' => [
|
||||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]',
|
'rules' => 'required|regex_match[/^[a-zA-Z0-9_\- ]+$/]|min_length[2]|max_length[15]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Client Short Name is required',
|
'required' => 'Client Short Name is required',
|
||||||
'regex_match' => 'Client Short Name can only contain letters, numbers, hyphens and underscores.',
|
'regex_match' => 'Client Short Name can only contain letters, numbers, hyphens and underscores.',
|
||||||
@ -1129,9 +1128,8 @@ class ClientController extends AdminController
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
'pan' => [
|
'pan' => [
|
||||||
'rules' => 'required|regex_match[/^[A-Z]{5}[0-9]{4}[A-Z]$/]',
|
'rules' => 'permit_empty|regex_match[/^[A-Z]{5}[0-9]{4}[A-Z]$/]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'PAN Number is required.',
|
|
||||||
'regex_match' => 'Invalid PAN format. Example: ABCDE1234F'
|
'regex_match' => 'Invalid PAN format. Example: ABCDE1234F'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@ -1876,10 +1874,10 @@ class ClientController extends AdminController
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
'designation.*' => [
|
'designation.*' => [
|
||||||
'rules' => 'required|alpha_space',
|
'rules' => 'required|regex_match[/^[A-Za-z0-9\-_\/ ]+$/]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Designation is required',
|
'required' => 'Designation is required',
|
||||||
'alpha_space' => 'Designation may contain only letters and spaces'
|
'regex_match' => 'Designation may only contain letters, numbers, /, -, _ and spaces'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'email.*' => [
|
'email.*' => [
|
||||||
@ -2283,10 +2281,10 @@ class ClientController extends AdminController
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
'designation.*' => [
|
'designation.*' => [
|
||||||
'rules' => 'required|alpha_space',
|
'rules' => 'required|regex_match[/^[A-Za-z0-9\-_\/ ]+$/]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Designation is required',
|
'required' => 'Designation is required',
|
||||||
'alpha_space' => 'Designation may contain only letters and spaces'
|
'regex_match' => 'Designation may only contain letters, numbers, /, -, _ and spaces'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'email.*' => [
|
'email.*' => [
|
||||||
@ -2467,10 +2465,10 @@ class ClientController extends AdminController
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
'designation.*' => [
|
'designation.*' => [
|
||||||
'rules' => 'required|alpha_space',
|
'rules' => 'required|regex_match[/^[A-Za-z0-9\-_\/ ]+$/]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Designation is required',
|
'required' => 'Designation is required',
|
||||||
'alpha_space' => 'Designation may contain only letters and spaces'
|
'regex_match' => 'Designation may only contain letters, numbers, /, -, _ and spaces'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'email.*' => [
|
'email.*' => [
|
||||||
@ -5803,6 +5801,99 @@ class ClientController extends AdminController
|
|||||||
|
|
||||||
// print_r($postData); die;
|
// print_r($postData); die;
|
||||||
$client_type = $postData['client_type'] ?? null;
|
$client_type = $postData['client_type'] ?? null;
|
||||||
|
$from_modal = $postData['from_modal'] ?? null;
|
||||||
|
|
||||||
|
$rules = [
|
||||||
|
'client_name' => [
|
||||||
|
'label' => 'Client Name',
|
||||||
|
'rules' => 'required|regex_match[/^[a-zA-Z0-9 ,\/_\-]+$/]',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Client Name is required.',
|
||||||
|
'regex_match' => 'Client Name only allows letters, numbers, spaces, and , / _ -'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'short_name' => [
|
||||||
|
'label' => 'Client Short Name',
|
||||||
|
'rules' => 'required|regex_match[/^[a-zA-Z0-9,\/_\-]+$/]|is_unique[clients.short_name]',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Short Name is required.',
|
||||||
|
'regex_match' => 'Short Name only allows letters, numbers, and , / _ -',
|
||||||
|
'is_unique' => 'This Short Name is already in use.'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'pan' => [
|
||||||
|
'label' => 'PAN',
|
||||||
|
'rules' => 'permit_empty|regex_match[/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/]',
|
||||||
|
'errors' => [
|
||||||
|
'regex_match' => 'Invalid PAN format (Example: ABCDE1234F).'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'entity_type_id' => [
|
||||||
|
'label' => 'Entity Type',
|
||||||
|
'rules' => 'required',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Please select an Entity Type.'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'branch_name' => [
|
||||||
|
'label' => 'Branch Name',
|
||||||
|
'rules' => 'required|regex_match[/^[a-zA-Z0-9 ,\/_\-]+$/]',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Branch Name is required.',
|
||||||
|
'regex_match' => 'Branch Name only allows letters, numbers, spaces, and , / _ -'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'branch_code' => [
|
||||||
|
'label' => 'Branch Code',
|
||||||
|
'rules' => 'required|regex_match[/^[a-zA-Z0-9,\/_\-]+$/]',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Branch Code is required.',
|
||||||
|
'regex_match' => 'Branch Code only allows letters, numbers, and , / _ -'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'name' => [
|
||||||
|
'label' => 'Contact Person Name',
|
||||||
|
'rules' => 'required|regex_match[/^[a-zA-Z0-9 ,\/_\-]+$/]',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Name is required.',
|
||||||
|
'regex_match' => 'Name only allows letters, numbers, spaces, and , / _ -'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'mobile' => [
|
||||||
|
'label' => 'Mobile',
|
||||||
|
'rules' => 'required|numeric|exact_length[10]',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Mobile number is required.',
|
||||||
|
'numeric' => 'Mobile must contain only digits.',
|
||||||
|
'exact_length' => 'Mobile number must be exactly 10 digits.'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'email' => [
|
||||||
|
'label' => 'Email',
|
||||||
|
'rules' => 'required|valid_email',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Email is required.',
|
||||||
|
'valid_email' => 'Please provide a valid email address.'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'gst' => [
|
||||||
|
'label' => 'GST',
|
||||||
|
'rules' => 'required|regex_match[/^\d{2}[A-Z]{5}\d{4}[A-Z]{1}[A-Z\d]{1}[Z]{1}[A-Z\d]{1}$/]',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'GST Number is required.',
|
||||||
|
'regex_match' => 'Invalid GST format (Example: 12ABCDE1234F5Z6).'
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isset($from_modal) && !$this->validate($rules)) {
|
||||||
|
return $this->response->setStatusCode(400)->setJSON([
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'Input validation failed',
|
||||||
|
'code' => 400,
|
||||||
|
'errors' => $this->validator->getErrors()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
$client_insert = null;
|
$client_insert = null;
|
||||||
if ($client_type == 2) {
|
if ($client_type == 2) {
|
||||||
@ -5928,7 +6019,8 @@ class ClientController extends AdminController
|
|||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
if (!$this->validate($rules)) {
|
|
||||||
|
if (!$this->validate($rules)) {
|
||||||
return $this->response->setStatusCode(400)->setJSON([
|
return $this->response->setStatusCode(400)->setJSON([
|
||||||
'status' => false,
|
'status' => false,
|
||||||
'message' => 'Input validation failed',
|
'message' => 'Input validation failed',
|
||||||
@ -5936,6 +6028,7 @@ class ClientController extends AdminController
|
|||||||
'errors' => $this->validator->getErrors()
|
'errors' => $this->validator->getErrors()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->request->getPost();
|
$data = $this->request->getPost();
|
||||||
$sanitized_post_data = sanitizeInputArrayAdvanced($data);
|
$sanitized_post_data = sanitizeInputArrayAdvanced($data);
|
||||||
$id = $sanitized_post_data['vehicle_primary_key'] ?? null;
|
$id = $sanitized_post_data['vehicle_primary_key'] ?? null;
|
||||||
|
|||||||
@ -721,21 +721,43 @@ class DashboardController extends AdminController
|
|||||||
return $this->respond(['status' => false, 'code' => 400, 'message' => 'No template found', 'message2' => 'Failed'], 200);
|
return $this->respond(['status' => false, 'code' => 400, 'message' => 'No template found', 'message2' => 'Failed'], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$emp_data = $this->employeePolicyModel->getEmployeePolicyForEcard($policy_id);
|
$emp_data = $this->employeePolicyModel->getEmployeePolicyForEcard($policy_id);
|
||||||
if (count($emp_data) == 0) {
|
if (count($emp_data) == 0) {
|
||||||
return $this->respond(['status' => false, 'code' => 400, 'message' => 'No employees found', 'message2' => 'Failed'], 200);
|
return $this->respond(['status' => false, 'code' => 400, 'message' => 'No employees found', 'message2' => 'Failed'], 200);
|
||||||
}
|
}
|
||||||
$ids = array_column($emp_data, 'id');
|
$ids = array_column($emp_data, 'id');
|
||||||
|
|
||||||
|
|
||||||
|
$client_policy_data = $this->clientPolicyModel->where('id', $policy_id)->first();
|
||||||
|
$sendMail = false;
|
||||||
|
|
||||||
|
if ($client_policy_data['policy_type_id'] == 3 && $client_policy_data['is_addon'] == 3) {
|
||||||
|
$sendMail = true;
|
||||||
|
} else {
|
||||||
|
$relationships = array_column($emp_data, 'relationship');
|
||||||
|
if (in_array('Self', $relationships)) {
|
||||||
|
$sendMail = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sendMail) {
|
||||||
|
$message = 'Mail Queued';
|
||||||
|
Jobs::addJob(['job_name' => 'sendMailForDownloadingECard','payload' => ['ids' => $ids,'client_policy_id' => $policy_id]]);
|
||||||
|
$this->myLogger->logme('error', 'sendManualEcard - Mail Queued');
|
||||||
|
} else {
|
||||||
|
$message = 'E-card has already been sent to those employees.';
|
||||||
|
$this->myLogger->logme('error', 'sendManualEcard - E-card has already been sent to those employees.');
|
||||||
|
}
|
||||||
|
|
||||||
// print_r(($ids)); die;
|
// print_r(($ids)); die;
|
||||||
// print_r($this->clientPolicyModel->getLastQuery()); die;
|
// print_r($this->clientPolicyModel->getLastQuery()); die;
|
||||||
// $this->myLogger->logme('error', "Policy details fetched: " . json_encode($client_policy_data));
|
// $this->myLogger->logme('error', "Policy details fetched: " . json_encode($client_policy_data));
|
||||||
Jobs::addJob(['job_name' => 'sendMailForDownloadingECard', 'payload' => ['ids' => $ids, 'client_policy_id' => $policy_id]]);
|
// Jobs::addJob(['job_name' => 'sendMailForDownloadingECard', 'payload' => ['ids' => $ids, 'client_policy_id' => $policy_id]]);
|
||||||
// $empEmpDataServiceController = new EmpDataServiceController();
|
// $empEmpDataServiceController = new EmpDataServiceController();
|
||||||
// $empEmpDataServiceController->sendMailForDownloadingECard($ids);
|
// $empEmpDataServiceController->sendMailForDownloadingECard($ids);
|
||||||
|
|
||||||
|
|
||||||
return $this->respond(['status' => true, 'code' => 200, 'message' => 'Mail Queued'], 200);
|
return $this->respond(['status' => true, 'code' => 200, 'message' => $message], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function data_construct_for_bds($data)
|
public function data_construct_for_bds($data)
|
||||||
|
|||||||
@ -3891,6 +3891,13 @@ class EmpDataServiceController extends BaseController
|
|||||||
|
|
||||||
$policy_name = $this->getPolicyNameUsingClientPolicyId($client_policy_id);
|
$policy_name = $this->getPolicyNameUsingClientPolicyId($client_policy_id);
|
||||||
$job_details = new Jobs();
|
$job_details = new Jobs();
|
||||||
|
|
||||||
|
$r = Jobs::addJob(['job_name' => 'visitOffBoard', 'payload' => [
|
||||||
|
'memberIds' => $employee_policy_table_primaryKey ?? [],
|
||||||
|
'policyNumber' => $policy_name['policy_no'] ?? null ,
|
||||||
|
'source' => 'NHANCE'
|
||||||
|
]]);
|
||||||
|
|
||||||
$r = Jobs::addJob(['job_name' => 'cashDepositCalculationForDeletion', 'payload' => [
|
$r = Jobs::addJob(['job_name' => 'cashDepositCalculationForDeletion', 'payload' => [
|
||||||
'employeeIds' => $employee_policy_table_primaryKey,
|
'employeeIds' => $employee_policy_table_primaryKey,
|
||||||
'client_id' => $client_id,
|
'client_id' => $client_id,
|
||||||
@ -4703,7 +4710,7 @@ class EmpDataServiceController extends BaseController
|
|||||||
|
|
||||||
public function getPolicyNameUsingClientPolicyId($client_policy_id)
|
public function getPolicyNameUsingClientPolicyId($client_policy_id)
|
||||||
{
|
{
|
||||||
return $this->clientPolicyModel->select('policy_type.policy_type as policy_name')
|
return $this->clientPolicyModel->select('policy_type.policy_type as policy_name, client_policy.policy_no')
|
||||||
->join('policy_type', 'policy_type.id = client_policy.policy_type_id')
|
->join('policy_type', 'policy_type.id = client_policy.policy_type_id')
|
||||||
->where('client_policy.id', $client_policy_id)
|
->where('client_policy.id', $client_policy_id)
|
||||||
->first();
|
->first();
|
||||||
|
|||||||
@ -316,11 +316,11 @@ class EmployeeController extends AdminController
|
|||||||
$policy_id = isset($post_data['policy_id']) ? $post_data['policy_id'] : $this->request->getPost('policy_id');
|
$policy_id = isset($post_data['policy_id']) ? $post_data['policy_id'] : $this->request->getPost('policy_id');
|
||||||
$branch_id = isset($post_data['client_branch_id']) ? $post_data['client_branch_id'] : $this->request->getPost('branch_id');
|
$branch_id = isset($post_data['client_branch_id']) ? $post_data['client_branch_id'] : $this->request->getPost('branch_id');
|
||||||
$action = isset($post_data['file_action']) ? $post_data['file_action'] : $this->request->getPost('upload-action-type');
|
$action = isset($post_data['file_action']) ? $post_data['file_action'] : $this->request->getPost('upload-action-type');
|
||||||
|
|
||||||
if(empty($post_data)){
|
if (empty($post_data)) {
|
||||||
$hr_file_id = $this->request->getPost('hr_file_id') ?? null;
|
$hr_file_id = $this->request->getPost('hr_file_id') ?? null;
|
||||||
}else{
|
} else {
|
||||||
$hr_file_id = $post_data['hr_file_id'] ?? null ;
|
$hr_file_id = $post_data['hr_file_id'] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$hr_id = $post_data['created_by'] ?? null;
|
$hr_id = $post_data['created_by'] ?? null;
|
||||||
@ -342,9 +342,9 @@ class EmployeeController extends AdminController
|
|||||||
$this->myLogger->logme("error", '{file_id} is less than 1MB, validating on the fly', ['file_id' => $file_id]);
|
$this->myLogger->logme("error", '{file_id} is less than 1MB, validating on the fly', ['file_id' => $file_id]);
|
||||||
//endof validation process
|
//endof validation process
|
||||||
if (isset($result['error_summary']) && count($result['error_summary'])) {
|
if (isset($result['error_summary']) && count($result['error_summary'])) {
|
||||||
if(!empty($post_data)){
|
if (!empty($post_data)) {
|
||||||
return ['status' => false, 'message' => 'File rejected with errors', 'file_id' => $file_id];
|
return ['status' => false, 'message' => 'File rejected with errors', 'file_id' => $file_id];
|
||||||
}else{
|
} else {
|
||||||
return $this->respond(['dataStatus' => false, 'code' => 404, 'message' => 'File rejected with errors'], 200);
|
return $this->respond(['dataStatus' => false, 'code' => 404, 'message' => 'File rejected with errors'], 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -363,6 +363,9 @@ class EmployeeController extends AdminController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$get_data = $this->request->getGet();
|
||||||
|
// dd($get_data);
|
||||||
|
|
||||||
$data['tab_name'] = 'View Inception';
|
$data['tab_name'] = 'View Inception';
|
||||||
$data['page_name'] = 'View Inception';
|
$data['page_name'] = 'View Inception';
|
||||||
|
|
||||||
@ -371,7 +374,7 @@ class EmployeeController extends AdminController
|
|||||||
//for inception upload
|
//for inception upload
|
||||||
$data['actions'] = ['all' => 'All', 'inception' => 'Inception (Employee + Dependents)', 'missed_inception' => 'Missed Inception (Employee + Dependents)', 'addition' => 'Addition (Employee + Dependents)', 'dependent_addition' => 'Dependent Addition (Only Dependents)', 'deletion' => 'Deletion', 'correction' => 'Correction', 'si_enhancement' => 'SI Enhancement'];
|
$data['actions'] = ['all' => 'All', 'inception' => 'Inception (Employee + Dependents)', 'missed_inception' => 'Missed Inception (Employee + Dependents)', 'addition' => 'Addition (Employee + Dependents)', 'dependent_addition' => 'Dependent Addition (Only Dependents)', 'deletion' => 'Deletion', 'correction' => 'Correction', 'si_enhancement' => 'SI Enhancement'];
|
||||||
// $data['actions'] = ['inception' => 'Inception (Employee + Dependents)', 'missed_inception' => 'Missed Inception (Employee + Dependents)', 'addition' => 'Addition (Employee + Dependents)', 'dependent_addition' => 'Dependent Addition (Only Dependents)', 'deletion' => 'Deletion', 'correction' => 'Correction', 'si_enhancement' => 'SI Enhancement','enrollment' => 'Enrolment'];
|
// $data['actions'] = ['inception' => 'Inception (Employee + Dependents)', 'missed_inception' => 'Missed Inception (Employee + Dependents)', 'addition' => 'Addition (Employee + Dependents)', 'dependent_addition' => 'Dependent Addition (Only Dependents)', 'deletion' => 'Deletion', 'correction' => 'Correction', 'si_enhancement' => 'SI Enhancement','enrollment' => 'Enrolment'];
|
||||||
|
|
||||||
// $data['import_or_export'] = ['import' => 'Import', 'export' => 'Export'];
|
// $data['import_or_export'] = ['import' => 'Import', 'export' => 'Export'];
|
||||||
$data['import_or_export'] = ['import' => 'Upload', 'export' => 'Download'];
|
$data['import_or_export'] = ['import' => 'Upload', 'export' => 'Download'];
|
||||||
$data['insurer_or_tpa'] = ['insurer' => 'Insurer', 'tpa' => 'TPA'];
|
$data['insurer_or_tpa'] = ['insurer' => 'Insurer', 'tpa' => 'TPA'];
|
||||||
@ -388,49 +391,90 @@ class EmployeeController extends AdminController
|
|||||||
$user_id = get_session_userid();
|
$user_id = get_session_userid();
|
||||||
|
|
||||||
$query = $this->fileModel
|
$query = $this->fileModel
|
||||||
->select([
|
->select([
|
||||||
'files.id','files.file_name','files.created_by','files.created_at','files.is_active','files.status','files.client_id','files.policy_id','files.client_branch_id','files.action','files.uploaded_by',
|
'files.id',
|
||||||
'up.emp_code',
|
'files.file_name',
|
||||||
// 'up.first_name',
|
'files.created_by',
|
||||||
'c.short_name',
|
'files.created_at',
|
||||||
'cb.branch_name',
|
'files.is_active',
|
||||||
'cp.id as client_policy_id',
|
'files.status',
|
||||||
'"0" as employee_count',
|
'files.client_id',
|
||||||
'"0" as total',
|
'files.policy_id',
|
||||||
'policy_type.policy_type' ,
|
'files.client_branch_id',
|
||||||
'cp.policy_no' ,
|
'files.action',
|
||||||
"CASE
|
'files.uploaded_by',
|
||||||
WHEN files.hr_id IS NOT NULL THEN
|
'up.emp_code',
|
||||||
CONCAT(
|
// 'up.first_name',
|
||||||
(
|
'c.short_name',
|
||||||
SELECT lc.name
|
'cb.branch_name',
|
||||||
FROM level_contacts lc
|
'cp.id as client_policy_id',
|
||||||
WHERE lc.id = files.hr_id
|
'"0" as employee_count',
|
||||||
LIMIT 1
|
'"0" as total',
|
||||||
),
|
'policy_type.policy_type',
|
||||||
' (HR)'
|
'cp.policy_no',
|
||||||
)
|
"CASE
|
||||||
ELSE up.first_name
|
WHEN files.hr_id IS NOT NULL THEN
|
||||||
END AS first_name
|
CONCAT(
|
||||||
"
|
(
|
||||||
])
|
SELECT lc.name
|
||||||
->join('user_profiles up', 'files.created_by = up.id', 'left')
|
FROM level_contacts lc
|
||||||
->join('client_policy cp', 'files.policy_id = cp.id', 'left')
|
WHERE lc.id = files.hr_id
|
||||||
->join('client_branch cb', 'files.client_branch_id = cb.id', 'left')
|
LIMIT 1
|
||||||
->join('policy_type', 'policy_type.id = cp.policy_type_id')
|
),
|
||||||
->join('clients c', 'files.client_id = c.id and files.client_id = cp.client_id', 'left')
|
' (HR)'
|
||||||
->join("client_rm cr","cr.client_id = c.id and cr.is_active = 1",'left')
|
)
|
||||||
->where('files.is_active', 1);
|
ELSE up.first_name
|
||||||
if (in_array($role_id, [2, 3])) {
|
END AS first_name
|
||||||
|
"
|
||||||
// If the user is a Account Manager or Manager, filter by user_id
|
])
|
||||||
$query->where('cr.user_id', (int)$user_id);
|
->join('user_profiles up', 'files.created_by = up.id', 'left')
|
||||||
}
|
->join('client_policy cp', 'files.policy_id = cp.id', 'left')
|
||||||
// ->where('files.created_by', get_session_userid())
|
->join('client_branch cb', 'files.client_branch_id = cb.id', 'left')
|
||||||
$data['fileList'] = $query->groupBy("files.id")->orderBy('files.created_at', 'desc')
|
->join('policy_type', 'policy_type.id = cp.policy_type_id')
|
||||||
// $data['fileList'] = $query
|
->join('clients c', 'files.client_id = c.id and files.client_id = cp.client_id', 'left')
|
||||||
->limit(2000)
|
->join("client_rm cr", "cr.client_id = c.id and cr.is_active = 1", 'left')
|
||||||
->find();
|
->where('files.is_active', 1);
|
||||||
|
|
||||||
|
if (!empty($get_data['start_date'] ?? null) && !empty($get_data['end_date'] ?? null)) {
|
||||||
|
// 1. If dates are provided, use the user's filter
|
||||||
|
$start = change_date_format($get_data['start_date'], 'd/m/Y', 'Y-m-d') . ' 00:00:00';
|
||||||
|
$end = change_date_format($get_data['end_date'], 'd/m/Y', 'Y-m-d') . ' 23:59:59';
|
||||||
|
|
||||||
|
$query->where('files.created_at >=', $start)
|
||||||
|
->where('files.created_at <=', $end);
|
||||||
|
} else {
|
||||||
|
if(empty($get_data['tab_type'] ?? null)){
|
||||||
|
// 2. Default: If no dates are selected, show last 90 days (or all data)
|
||||||
|
$query->where('files.created_at >=', date('Y-m-d 00:00:00', strtotime('-90 days')))
|
||||||
|
->where('files.created_at <=', date('Y-m-d 23:59:59'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($get_data['client_id'] ?? null)){
|
||||||
|
$query->where('files.client_id', $get_data['client_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($get_data['client_branch_id'] ?? null)){
|
||||||
|
$query->where('files.client_branch_id', $get_data['client_branch_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($get_data['policy_id'] ?? null)){
|
||||||
|
$query->where('files.policy_id', $get_data['policy_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($get_data['event_type'] ?? null)){
|
||||||
|
$query->where('files.action', $get_data['event_type']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($role_id, [2, 3])) {
|
||||||
|
// If the user is a Account Manager or Manager, filter by user_id
|
||||||
|
$query->where('cr.user_id', (int)$user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['fileList'] = $query->groupBy("files.id")
|
||||||
|
->orderBy('files.created_at', 'desc')
|
||||||
|
->find();
|
||||||
|
|
||||||
// dd($this->fileModel->getLastQuery());
|
// dd($this->fileModel->getLastQuery());
|
||||||
// dd($data['fileList']);
|
// dd($data['fileList']);
|
||||||
|
|
||||||
@ -467,22 +511,62 @@ class EmployeeController extends AdminController
|
|||||||
policy_type.policy_type
|
policy_type.policy_type
|
||||||
|
|
||||||
")
|
")
|
||||||
->join('client_policy', 'client_policy.id = batch_files.client_policy_id')
|
->join('client_policy', 'client_policy.id = batch_files.client_policy_id')
|
||||||
->join('client_branch', 'client_branch.id = batch_files.client_branch_id')
|
->join('client_branch', 'client_branch.id = batch_files.client_branch_id')
|
||||||
->join('policy_type', 'policy_type.id = client_policy.policy_type_id')
|
->join('policy_type', 'policy_type.id = client_policy.policy_type_id')
|
||||||
->join('clients', 'clients.id = client_policy.client_id')
|
->join('clients', 'clients.id = client_policy.client_id')
|
||||||
->join("client_rm cr","cr.client_id = clients.id and cr.is_active = 1",'left')
|
->join("client_rm cr", "cr.client_id = clients.id and cr.is_active = 1", 'left')
|
||||||
->where('batch_files.is_active', 1);
|
->where('batch_files.is_active', 1);
|
||||||
if (in_array($role_id, [2, 3])) {
|
|
||||||
|
if (!empty($get_data['start_date'] ?? null) && !empty($get_data['end_date'] ?? null)) {
|
||||||
// If the user is a Account Manager or Manager, filter by user_id
|
// 1. If dates are provided, use the user's filter
|
||||||
$query2->where('cr.user_id', (int)$user_id);
|
$start = change_date_format($get_data['start_date'], 'd/m/Y', 'Y-m-d') . ' 00:00:00';
|
||||||
}
|
$end = change_date_format($get_data['end_date'], 'd/m/Y', 'Y-m-d') . ' 23:59:59';
|
||||||
// ->where('files.created_by', get_session_userid())
|
|
||||||
$data['batch_list'] = $query2->groupBy("batch_files.id")->orderBy('batch_files.id', 'desc')
|
$query2->where('batch_files.created_at >=', $start)
|
||||||
->limit(1500)
|
->where('batch_files.created_at <=', $end);
|
||||||
->find();
|
|
||||||
|
} else {
|
||||||
|
if(empty($get_data['tab_type'] ?? null)){
|
||||||
|
// 2. Default: If no dates are selected, show last 90 days (or all data)
|
||||||
|
$query2->where('batch_files.created_at >=', date('Y-m-d 00:00:00', strtotime('-90 days')))
|
||||||
|
->where('batch_files.created_at <=', date('Y-m-d 23:59:59'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($get_data['client_id'] ?? null)){
|
||||||
|
$query2->where('batch_files.client_id', $get_data['client_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($get_data['client_branch_id'] ?? null)){
|
||||||
|
$query2->where('batch_files.client_branch_id', $get_data['client_branch_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($get_data['policy_id'] ?? null)){
|
||||||
|
$query2->where('batch_files.client_policy_id', $get_data['policy_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($get_data['event_type'] ?? null)){
|
||||||
|
$query2->where('batch_files.event_type', $get_data['event_type']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($get_data['insurer_or_tpa'] ?? null)){
|
||||||
|
$query2->where('batch_files.insurer_or_tpa', $get_data['insurer_or_tpa']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($get_data['action_type'] ?? null)){
|
||||||
|
$query2->where('batch_files.actions', $get_data['action_type']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($role_id, [2, 3])) {
|
||||||
|
|
||||||
|
// If the user is a Account Manager or Manager, filter by user_id
|
||||||
|
$query2->where('cr.user_id', (int)$user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['batch_list'] = $query2->groupBy("batch_files.id")
|
||||||
|
->orderBy('batch_files.id', 'desc')
|
||||||
|
->find();
|
||||||
|
|
||||||
|
|
||||||
// dd($data['fileList']);die();
|
// dd($data['fileList']);die();
|
||||||
@ -3942,14 +4026,14 @@ class EmployeeController extends AdminController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTPADataVariationReport($file_id)
|
public function getTPADataVariationReport($file_id, $type = 'download')
|
||||||
{
|
{
|
||||||
$file_info = $this->batchFileModel->where('id', $file_id)->find();
|
$file_info = $this->batchFileModel->where('id', $file_id)->find();
|
||||||
$client_id = $file_info[0]['client_id'];
|
$client_id = $file_info[0]['client_id'];
|
||||||
$client_policy_id = $file_info[0]['client_policy_id'];
|
$client_policy_id = $file_info[0]['client_policy_id'];
|
||||||
$TpaApiDataModel = new TpaApiDataModel();
|
$TpaApiDataModel = new TpaApiDataModel();
|
||||||
$emp_data_wo_tpa_id = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id);
|
$emp_data_wo_tpa_id = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id);
|
||||||
|
|
||||||
|
|
||||||
//loop emp data with TPA data for matches
|
//loop emp data with TPA data for matches
|
||||||
foreach ($emp_data_wo_tpa_id as $db_key => $db_row)
|
foreach ($emp_data_wo_tpa_id as $db_key => $db_row)
|
||||||
@ -3969,10 +4053,10 @@ class EmployeeController extends AdminController
|
|||||||
// die();
|
// die();
|
||||||
//not_in_tpa
|
//not_in_tpa
|
||||||
$tpa_emp_codes = $TpaApiDataModel->select('emp_code')
|
$tpa_emp_codes = $TpaApiDataModel->select('emp_code')
|
||||||
->where('file_id', $file_id)
|
->where('file_id', $file_id)
|
||||||
->where('is_active', 1)
|
->where('is_active', 1)
|
||||||
->groupBy('emp_code')
|
->groupBy('emp_code')
|
||||||
->findAll();
|
->findAll();
|
||||||
$tpa_emp_codes = array_column($tpa_emp_codes, 'emp_code');
|
$tpa_emp_codes = array_column($tpa_emp_codes, 'emp_code');
|
||||||
|
|
||||||
$not_in_tpa = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id,$tpa_emp_codes);
|
$not_in_tpa = $this->employeePolicyModel->getTPADataVariationReport($client_id, $client_policy_id, $file_id,$tpa_emp_codes);
|
||||||
@ -3986,20 +4070,697 @@ class EmployeeController extends AdminController
|
|||||||
->where('is_active',1)
|
->where('is_active',1)
|
||||||
->where('file_id',$file_id)
|
->where('file_id',$file_id)
|
||||||
->whereNotIn('emp_code',$master_emp_codes)
|
->whereNotIn('emp_code',$master_emp_codes)
|
||||||
->findAll();
|
->findAll();
|
||||||
// d($not_in_nhance);die();
|
// d($not_in_nhance);die();
|
||||||
|
|
||||||
if( !empty($not_in_tpa) || !empty($not_in_nhance) || !empty($emp_data_wo_tpa_id) )
|
if (!empty($not_in_tpa) || !empty($not_in_nhance) || !empty($emp_data_wo_tpa_id)) {
|
||||||
{
|
if ($type === 'download') {
|
||||||
$this->exportVariationReportExcel($not_in_tpa,$not_in_nhance,$emp_data_wo_tpa_id);
|
$this->exportVariationReportExcel($not_in_tpa, $not_in_nhance, $emp_data_wo_tpa_id);
|
||||||
}
|
} else {
|
||||||
else
|
$response = [
|
||||||
{
|
'not_in_tpa' => $not_in_tpa,
|
||||||
return false;
|
'not_in_nhance' => $not_in_nhance,
|
||||||
}
|
'mismatch_data' => $emp_data_wo_tpa_id,
|
||||||
// $this->exportVariationReportExcel([],[],[]);
|
];
|
||||||
|
|
||||||
|
return $this->respond(
|
||||||
|
[
|
||||||
|
'status' => true,
|
||||||
|
'code' => 200,
|
||||||
|
'message' => '',
|
||||||
|
'data' => $response,
|
||||||
|
],
|
||||||
|
200
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($type === 'download') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->respond(
|
||||||
|
[
|
||||||
|
'status' => false,
|
||||||
|
'code' => 202,
|
||||||
|
'message' => 'No data found',
|
||||||
|
'data' => [],
|
||||||
|
],
|
||||||
|
200
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function proceedTPADataVariationNextStep($file_id)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (empty($file_id)) {
|
||||||
|
return $this->respond(
|
||||||
|
[
|
||||||
|
'status' => false,
|
||||||
|
'code' => 400,
|
||||||
|
'message' => 'Invalid file reference',
|
||||||
|
'data' => [],
|
||||||
|
],
|
||||||
|
200
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->batchFileModel->find($file_id);
|
||||||
|
|
||||||
|
if (!$file) {
|
||||||
|
return $this->respond(
|
||||||
|
[
|
||||||
|
'status' => false,
|
||||||
|
'code' => 404,
|
||||||
|
'message' => 'File not found',
|
||||||
|
'data' => [],
|
||||||
|
],
|
||||||
|
200
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tab = $this->request->getGet('tab');
|
||||||
|
|
||||||
|
// If the user is proceeding from the "Not in Nhance" tab,
|
||||||
|
// generate an Employee Upload with Events compatible Excel file
|
||||||
|
// and trigger the usual upload pipeline.
|
||||||
|
if ($tab === 'not_in_nhance') {
|
||||||
|
$generationResult = $this->generateEmployeeUploadFromNotInNhance((int) $file_id, $file);
|
||||||
|
|
||||||
|
if (!$generationResult['status']) {
|
||||||
|
return $this->respond(
|
||||||
|
[
|
||||||
|
'status' => false,
|
||||||
|
'code' => 422,
|
||||||
|
'message' => $generationResult['message'] ?? 'Unable to generate employee upload file from Not in Nhance data.',
|
||||||
|
'data' => $generationResult['data'] ?? [],
|
||||||
|
],
|
||||||
|
200
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif ($tab === 'need_to_review') {
|
||||||
|
// For the "Need to Review" tab, generate a Correction Excel
|
||||||
|
// using the same overall pipeline as the Not in Nhance implementation.
|
||||||
|
$generationResult = $this->generateCorrectionUploadFromNeedToReview((int) $file_id, $file);
|
||||||
|
|
||||||
|
if (!$generationResult['status']) {
|
||||||
|
return $this->respond(
|
||||||
|
[
|
||||||
|
'status' => false,
|
||||||
|
'code' => 422,
|
||||||
|
'message' => $generationResult['message'] ?? 'Unable to generate correction upload file from Need to Review data.',
|
||||||
|
'data' => $generationResult['data'] ?? [],
|
||||||
|
],
|
||||||
|
200
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->myLogger->logme(
|
||||||
|
'error',
|
||||||
|
'TPA variation review completed and proceed to next clicked',
|
||||||
|
[
|
||||||
|
'file_id' => $file_id,
|
||||||
|
'user_id' => get_session_userid(),
|
||||||
|
'tab' => $tab,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->respond(
|
||||||
|
[
|
||||||
|
'status' => true,
|
||||||
|
'code' => 200,
|
||||||
|
'message' => $tab === 'not_in_nhance'
|
||||||
|
? 'Employee upload file generated from Not in Nhance data and queued for processing.'
|
||||||
|
: ($tab === 'need_to_review'
|
||||||
|
? 'Correction upload file generated from Need to Review data and queued for processing.'
|
||||||
|
: 'Proceed to next step recorded successfully.'),
|
||||||
|
'data' => [],
|
||||||
|
],
|
||||||
|
200
|
||||||
|
);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->myLogger->logme(
|
||||||
|
'error',
|
||||||
|
'Error while processing TPA variation proceed to next: ' . $e->getMessage(),
|
||||||
|
['file_id' => $file_id]
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->respond(
|
||||||
|
[
|
||||||
|
'status' => false,
|
||||||
|
'code' => 500,
|
||||||
|
'message' => 'Unable to proceed to the next step at the moment.',
|
||||||
|
'data' => [],
|
||||||
|
],
|
||||||
|
200
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate an Employee Upload with Events compatible Excel file
|
||||||
|
* from the Not in Nhance TPA variation data and push it into the
|
||||||
|
* existing employee upload pipeline.
|
||||||
|
*
|
||||||
|
* @param int $batchFileId Batch file id used for TPA variation report.
|
||||||
|
* @param array $batchFile Batch file row from DB.
|
||||||
|
*
|
||||||
|
* @return array ['status' => bool, 'message' => string, 'data' => array]
|
||||||
|
*/
|
||||||
|
protected function generateEmployeeUploadFromNotInNhance(int $batchFileId, array $batchFile): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$clientId = (int) ($batchFile['client_id'] ?? 0);
|
||||||
|
$clientPolicyId = (int) ($batchFile['client_policy_id'] ?? 0);
|
||||||
|
$clientBranchId = (int) ($batchFile['client_branch_id'] ?? 0);
|
||||||
|
|
||||||
|
if (!$clientId || !$clientPolicyId || !$clientBranchId) {
|
||||||
|
return [
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'Incomplete batch file information. Client / policy / branch missing.',
|
||||||
|
'data' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$TpaApiDataModel = new TpaApiDataModel();
|
||||||
|
|
||||||
|
// Get master emp codes from Nhance for this client & policy
|
||||||
|
$masterEmpRows = $this->employeePolicyModel->getTPADataVariationReport(
|
||||||
|
$clientId,
|
||||||
|
$clientPolicyId,
|
||||||
|
$batchFileId,
|
||||||
|
[],
|
||||||
|
true
|
||||||
|
);
|
||||||
|
if (!is_array($masterEmpRows)) {
|
||||||
|
$masterEmpRows = [];
|
||||||
|
}
|
||||||
|
$masterEmpCodes = array_column($masterEmpRows, 'emp_code');
|
||||||
|
|
||||||
|
// Fetch Not in Nhance rows for this batch file
|
||||||
|
$notInNhance = $TpaApiDataModel->select('*')
|
||||||
|
->where('is_active', 1)
|
||||||
|
->where('file_id', $batchFileId)
|
||||||
|
->whereNotIn('emp_code', $masterEmpCodes)
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
if (empty($notInNhance)) {
|
||||||
|
return [
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'No "Not in Nhance" records found for this file.',
|
||||||
|
'data' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$empServiceController = new EmployeeServiceController();
|
||||||
|
$inceptionColumns = $empServiceController->getInceptionExcelColumns();
|
||||||
|
|
||||||
|
if (empty($inceptionColumns)) {
|
||||||
|
return [
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'Unable to load inception Excel column configuration.',
|
||||||
|
'data' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
$sheet->setTitle('Employees');
|
||||||
|
|
||||||
|
// Header row from EmployeeServiceController column definitions
|
||||||
|
$colIndex = 1;
|
||||||
|
foreach ($inceptionColumns as $columnDef) {
|
||||||
|
$headerText = $columnDef['col_name'] ?? '';
|
||||||
|
$columnLetter = Coordinate::stringFromColumnIndex($colIndex);
|
||||||
|
$sheet->setCellValue($columnLetter . '1', $headerText);
|
||||||
|
$colIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to safely format dates as d-M-Y when possible
|
||||||
|
$formatDate = static function ($value): string {
|
||||||
|
if (empty($value)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$ts = strtotime($value);
|
||||||
|
if ($ts === false) {
|
||||||
|
return (string) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return date('d-M-Y', $ts);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map Not in Nhance TPA rows into the inception Excel structure
|
||||||
|
$rowIndex = 2;
|
||||||
|
$sno = 1;
|
||||||
|
|
||||||
|
foreach ($notInNhance as $tpaRow) {
|
||||||
|
$colIndex = 1;
|
||||||
|
|
||||||
|
foreach ($inceptionColumns as $key => $columnDef) {
|
||||||
|
$value = '';
|
||||||
|
|
||||||
|
switch ($key) {
|
||||||
|
case 'sno':
|
||||||
|
$value = $sno;
|
||||||
|
break;
|
||||||
|
case 'emp_id':
|
||||||
|
$value = $tpaRow['emp_code'] ?? '';
|
||||||
|
break;
|
||||||
|
case 'name_of_emp_dep':
|
||||||
|
$value = $tpaRow['name'] ?? '';
|
||||||
|
break;
|
||||||
|
case 'dob':
|
||||||
|
$value = $formatDate($tpaRow['dob'] ?? '');
|
||||||
|
break;
|
||||||
|
case 'gender':
|
||||||
|
$value = $tpaRow['gender'] ?? '';
|
||||||
|
break;
|
||||||
|
case 'relationship':
|
||||||
|
// Normalize relation text to match allowed values
|
||||||
|
$relation = (string) ($tpaRow['relation'] ?? '');
|
||||||
|
$relation = trim(strtolower($relation));
|
||||||
|
$map = [
|
||||||
|
'self' => 'Self',
|
||||||
|
'employee' => 'Self',
|
||||||
|
'spouse' => 'Spouse',
|
||||||
|
'wife' => 'Spouse',
|
||||||
|
'husband' => 'Spouse',
|
||||||
|
'son' => 'Son',
|
||||||
|
'daughter' => 'Daughter',
|
||||||
|
'father' => 'Father',
|
||||||
|
'mother' => 'Mother',
|
||||||
|
'father-in-law' => 'Father in Law',
|
||||||
|
'father in law' => 'Father in Law',
|
||||||
|
'mother-in-law' => 'Mother in Law',
|
||||||
|
'mother in law' => 'Mother in Law',
|
||||||
|
];
|
||||||
|
$value = $map[$relation] ?? ($tpaRow['relation'] ?? '');
|
||||||
|
break;
|
||||||
|
case 'basic_cover_si':
|
||||||
|
$value = $tpaRow['si'] ?? '';
|
||||||
|
break;
|
||||||
|
case 'doc':
|
||||||
|
// Use DOJ from TPA data as Date of Coverage best-effort
|
||||||
|
$value = $formatDate($tpaRow['doj'] ?? '');
|
||||||
|
break;
|
||||||
|
case 'doj':
|
||||||
|
$value = $formatDate($tpaRow['doj'] ?? '');
|
||||||
|
break;
|
||||||
|
case 'pre_existing_ailments':
|
||||||
|
// Default to "0" (No) so validation passes for mandatory field
|
||||||
|
$value = '0';
|
||||||
|
break;
|
||||||
|
case 'change_event':
|
||||||
|
// For addition / dependent_addition, this is mandatory.
|
||||||
|
$value = 'addition';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Non-mapped columns (phone, email, etc.) left blank by default.
|
||||||
|
$value = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$columnLetter = Coordinate::stringFromColumnIndex($colIndex);
|
||||||
|
$sheet->setCellValue($columnLetter . $rowIndex, $value);
|
||||||
|
$colIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rowIndex++;
|
||||||
|
$sno++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-size columns
|
||||||
|
$totalColumns = count($inceptionColumns);
|
||||||
|
for ($c = 1; $c <= $totalColumns; $c++) {
|
||||||
|
$columnLetter = Coordinate::stringFromColumnIndex($c);
|
||||||
|
$sheet->getColumnDimension($columnLetter)->setAutoSize(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Persist the Excel file to the same folder used by manual uploads
|
||||||
|
$fileName = sprintf(
|
||||||
|
'not_in_nhance_employee_upload_%d_%s.xlsx',
|
||||||
|
$batchFileId,
|
||||||
|
date('Ymd_His')
|
||||||
|
);
|
||||||
|
$filePath = WRITEPATH . 'uploads/excel/' . $fileName;
|
||||||
|
|
||||||
|
$writer = new Xlsx($spreadsheet);
|
||||||
|
$writer->save($filePath);
|
||||||
|
|
||||||
|
// Create a new entry in the files table so that the
|
||||||
|
// existing Employee Upload with Events pipeline can process it.
|
||||||
|
$loggedInUserId = $batchFile['created_by'] ?? get_session_userid();
|
||||||
|
$action = 'addition';
|
||||||
|
|
||||||
|
$newFileId = $this->fileModel->insert([
|
||||||
|
'file_name' => $fileName,
|
||||||
|
'client_id' => $clientId,
|
||||||
|
'policy_id' => $clientPolicyId,
|
||||||
|
'created_by' => $loggedInUserId,
|
||||||
|
'status' => 'inprogress',
|
||||||
|
'action' => $action,
|
||||||
|
'client_branch_id'=> $clientBranchId,
|
||||||
|
'uploaded_by' => 1,
|
||||||
|
'hr_file_id' => null,
|
||||||
|
'hr_id' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$newFileId || !is_numeric($newFileId)) {
|
||||||
|
$this->myLogger->logme(
|
||||||
|
'error',
|
||||||
|
'Failed to insert generated Not in Nhance employee upload file into files table',
|
||||||
|
[
|
||||||
|
'batch_file_id' => $batchFileId,
|
||||||
|
'client_id' => $clientId,
|
||||||
|
'client_policy_id' => $clientPolicyId,
|
||||||
|
'client_branch_id' => $clientBranchId,
|
||||||
|
'file_name' => $fileName,
|
||||||
|
'insert_result' => $newFileId,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'Unable to create file record for generated employee upload.',
|
||||||
|
'data' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Run the same format validation used for manual uploads.
|
||||||
|
$validationResult = $empServiceController->excelFileFormatValidation(['file_id' => $newFileId]);
|
||||||
|
|
||||||
|
if (isset($validationResult['error_summary']) && count($validationResult['error_summary'])) {
|
||||||
|
return [
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'File upload was successful, but file format validation failed. Please review the error report.',
|
||||||
|
'data' => ['file_id' => $newFileId],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'status' => true,
|
||||||
|
'message' => 'Employee upload file generated from Not in Nhance data and queued for processing.',
|
||||||
|
'data' => ['file_id' => $newFileId],
|
||||||
|
];
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->myLogger->logme(
|
||||||
|
'error',
|
||||||
|
'Error while generating employee upload from Not in Nhance'.
|
||||||
|
json_encode( [
|
||||||
|
'batch_file_id' => $batchFileId,
|
||||||
|
'exception_message' => $e->getMessage(),
|
||||||
|
'exception_file' => $e->getFile(),
|
||||||
|
'exception_line' => $e->getLine(),
|
||||||
|
'exception_trace' => $e->getTraceAsString(),
|
||||||
|
'client_id' => $batchFile['client_id'] ?? null,
|
||||||
|
'client_policy_id' => $batchFile['client_policy_id'] ?? null,
|
||||||
|
'client_branch_id' => $batchFile['client_branch_id'] ?? null,
|
||||||
|
], JSON_PRETTY_PRINT)
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'Unexpected error while generating employee upload file.',
|
||||||
|
'data' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a Correction Excel file from the Need to Review
|
||||||
|
* TPA variation data and push it into the existing correction
|
||||||
|
* upload pipeline.
|
||||||
|
*
|
||||||
|
* Each mismatched field (name, dob, relationship, email_corporate)
|
||||||
|
* becomes a separate row in the Excel, using the correction
|
||||||
|
* headers defined in EmployeeServiceController::$correction_excel_columns.
|
||||||
|
*
|
||||||
|
* @param int $batchFileId Batch file id used for TPA variation report.
|
||||||
|
* @param array $batchFile Batch file row from DB.
|
||||||
|
*
|
||||||
|
* @return array ['status' => bool, 'message' => string, 'data' => array]
|
||||||
|
*/
|
||||||
|
protected function generateCorrectionUploadFromNeedToReview(int $batchFileId, array $batchFile): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$clientId = (int) ($batchFile['client_id'] ?? 0);
|
||||||
|
$clientPolicyId = (int) ($batchFile['client_policy_id'] ?? 0);
|
||||||
|
$clientBranchId = (int) ($batchFile['client_branch_id'] ?? 0);
|
||||||
|
|
||||||
|
if (!$clientId || !$clientPolicyId || !$clientBranchId) {
|
||||||
|
return [
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'Incomplete batch file information. Client / policy / branch missing.',
|
||||||
|
'data' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$TpaApiDataModel = new TpaApiDataModel();
|
||||||
|
|
||||||
|
// Reuse the same DB + TPA reconciliation used in getTPADataVariationReport
|
||||||
|
$dbRows = $this->employeePolicyModel->getTPADataVariationReport(
|
||||||
|
$clientId,
|
||||||
|
$clientPolicyId,
|
||||||
|
$batchFileId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!is_array($dbRows) || !count($dbRows)) {
|
||||||
|
return [
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'No employee data found for Need to Review.',
|
||||||
|
'data' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$mismatchRows = [];
|
||||||
|
|
||||||
|
foreach ($dbRows as $dbRow) {
|
||||||
|
$tpaRows = $TpaApiDataModel->select('*')
|
||||||
|
->where('emp_code', $dbRow['emp_code'])
|
||||||
|
->where('file_id', $batchFileId)
|
||||||
|
->where('is_active', 1)
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
if (!count($tpaRows)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$match = $this->reconcileDbWithTpa($dbRow, $tpaRows);
|
||||||
|
|
||||||
|
if (($match['status'] ?? '') !== 'matched') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tpaRecord = $match['tpa_record'] ?? [];
|
||||||
|
$notMatching = $match['not_matching'] ?? [];
|
||||||
|
|
||||||
|
if (!is_array($notMatching) || !count($notMatching)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only consider fields that are supported by the correction Excel headers
|
||||||
|
$allowedFields = ['name', 'dob', 'relationship', 'email_corporate'];
|
||||||
|
|
||||||
|
foreach ($notMatching as $field) {
|
||||||
|
if (!in_array($field, $allowedFields, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mismatchRows[] = [
|
||||||
|
'emp_code' => $dbRow['emp_code'] ?? '',
|
||||||
|
'name' => $dbRow['name'] ?? '',
|
||||||
|
'field' => $field,
|
||||||
|
// Use TPA value as the corrected value to be applied in Nhance
|
||||||
|
'value' => $tpaRecord[$field] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!count($mismatchRows)) {
|
||||||
|
return [
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'No mismatched records found to generate correction upload.',
|
||||||
|
'data' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$empServiceController = new EmployeeServiceController();
|
||||||
|
$correctionColumns = $empServiceController->getCorrectionExcelColumns();
|
||||||
|
|
||||||
|
if (empty($correctionColumns)) {
|
||||||
|
return [
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'Unable to load correction Excel column configuration.',
|
||||||
|
'data' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
$sheet->setTitle('Correction');
|
||||||
|
|
||||||
|
// Header row from EmployeeServiceController column definitions
|
||||||
|
$colIndex = 1;
|
||||||
|
foreach ($correctionColumns as $columnDef) {
|
||||||
|
$headerText = $columnDef['col_name'] ?? '';
|
||||||
|
$columnLetter = Coordinate::stringFromColumnIndex($colIndex);
|
||||||
|
$sheet->setCellValue($columnLetter . '1', $headerText);
|
||||||
|
$colIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$todayDisplay = date('d-M-Y');
|
||||||
|
|
||||||
|
$rowIndex = 2;
|
||||||
|
$sno = 1;
|
||||||
|
|
||||||
|
foreach ($mismatchRows as $row) {
|
||||||
|
$colIndex = 1;
|
||||||
|
|
||||||
|
$field_name = $row['field'] ?? '';
|
||||||
|
$field_value = ($field_name == 'dob' ? change_date_format($row['value'] ?? '', 'Y-m-d', 'd-M-Y') : $row['value'] ?? '' );
|
||||||
|
|
||||||
|
foreach ($correctionColumns as $key => $columnDef) {
|
||||||
|
$value = '';
|
||||||
|
|
||||||
|
switch ($key) {
|
||||||
|
case 'sno':
|
||||||
|
$value = $sno;
|
||||||
|
break;
|
||||||
|
case 'emp_id':
|
||||||
|
$value = $row['emp_code'] ?? '';
|
||||||
|
break;
|
||||||
|
case 'name_of_emp_dep':
|
||||||
|
$value = $row['name'] ?? '';
|
||||||
|
break;
|
||||||
|
case 'field':
|
||||||
|
$value = $field_name;
|
||||||
|
break;
|
||||||
|
case 'value':
|
||||||
|
$value = $field_value ?? '';
|
||||||
|
break;
|
||||||
|
case 'date_of_correction':
|
||||||
|
$value = $todayDisplay;
|
||||||
|
break;
|
||||||
|
case 'change_event':
|
||||||
|
$value = 'correction';
|
||||||
|
break;
|
||||||
|
case 'remarks':
|
||||||
|
$value = '';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$value = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$columnLetter = Coordinate::stringFromColumnIndex($colIndex);
|
||||||
|
$sheet->setCellValue($columnLetter . $rowIndex, $value);
|
||||||
|
$colIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rowIndex++;
|
||||||
|
$sno++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-size columns
|
||||||
|
$totalColumns = count($correctionColumns);
|
||||||
|
for ($c = 1; $c <= $totalColumns; $c++) {
|
||||||
|
$columnLetter = Coordinate::stringFromColumnIndex($c);
|
||||||
|
$sheet->getColumnDimension($columnLetter)->setAutoSize(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Persist the Excel file to the same folder used by manual uploads
|
||||||
|
$fileName = sprintf(
|
||||||
|
'need_to_review_correction_upload_%d_%s.xlsx',
|
||||||
|
$batchFileId,
|
||||||
|
date('Ymd_His')
|
||||||
|
);
|
||||||
|
$filePath = WRITEPATH . 'uploads/excel/' . $fileName;
|
||||||
|
|
||||||
|
$writer = new Xlsx($spreadsheet);
|
||||||
|
$writer->save($filePath);
|
||||||
|
|
||||||
|
// Insert into files table so the existing correction pipeline can process it.
|
||||||
|
$loggedInUserId = $batchFile['created_by'] ?? get_session_userid();
|
||||||
|
|
||||||
|
$newFileId = $this->fileModel->insert([
|
||||||
|
'file_name' => $fileName,
|
||||||
|
'client_id' => $clientId,
|
||||||
|
'policy_id' => $clientPolicyId,
|
||||||
|
'created_by' => $loggedInUserId,
|
||||||
|
'status' => 'inprogress',
|
||||||
|
'action' => 'correction',
|
||||||
|
'client_branch_id'=> $clientBranchId,
|
||||||
|
'uploaded_by' => 1,
|
||||||
|
'hr_file_id' => null,
|
||||||
|
'hr_id' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$newFileId || !is_numeric($newFileId)) {
|
||||||
|
$this->myLogger->logme(
|
||||||
|
'error',
|
||||||
|
'Failed to insert generated Need to Review correction upload file into files table',
|
||||||
|
[
|
||||||
|
'batch_file_id' => $batchFileId,
|
||||||
|
'client_id' => $clientId,
|
||||||
|
'client_policy_id' => $clientPolicyId,
|
||||||
|
'client_branch_id' => $clientBranchId,
|
||||||
|
'file_name' => $fileName,
|
||||||
|
'insert_result' => $newFileId,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'Unable to create file record for generated correction upload.',
|
||||||
|
'data' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the same format validation used for manual uploads so that
|
||||||
|
// the correction file enters the normal processing pipeline.
|
||||||
|
$validationResult = $empServiceController->excelFileFormatValidation(['file_id' => $newFileId]);
|
||||||
|
|
||||||
|
if (isset($validationResult['error_summary']) && count($validationResult['error_summary'])) {
|
||||||
|
return [
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'File upload was successful, but correction file format validation failed. Please review the error report.',
|
||||||
|
'data' => ['file_id' => $newFileId],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'status' => true,
|
||||||
|
'message' => 'Correction upload file generated from Need to Review data and queued for processing.',
|
||||||
|
'data' => ['file_id' => $newFileId],
|
||||||
|
];
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->myLogger->logme(
|
||||||
|
'error',
|
||||||
|
'Error while generating correction upload from Need to Review'.
|
||||||
|
json_encode(
|
||||||
|
[
|
||||||
|
'batch_file_id' => $batchFileId,
|
||||||
|
'exception_message' => $e->getMessage(),
|
||||||
|
'exception_file' => $e->getFile(),
|
||||||
|
'exception_line' => $e->getLine(),
|
||||||
|
'exception_trace' => $e->getTraceAsString(),
|
||||||
|
'client_id' => $batchFile['client_id'] ?? null,
|
||||||
|
'client_policy_id' => $batchFile['client_policy_id'] ?? null,
|
||||||
|
'client_branch_id' => $batchFile['client_branch_id'] ?? null,
|
||||||
|
],
|
||||||
|
JSON_PRETTY_PRINT
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'Unexpected error while generating correction upload file.',
|
||||||
|
'data' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -2404,6 +2404,9 @@ class EmployeeRestController extends AdminController
|
|||||||
|
|
||||||
if ($this->request->is('get')) {
|
if ($this->request->is('get')) {
|
||||||
|
|
||||||
|
$client_id = $this->request->getGet('client_id') ?? null;
|
||||||
|
|
||||||
|
|
||||||
$data['claim_status'] = $this->claimStatusModel
|
$data['claim_status'] = $this->claimStatusModel
|
||||||
->select('id,ticket_type, display_name as claim_status')
|
->select('id,ticket_type, display_name as claim_status')
|
||||||
->where('is_active', 1)
|
->where('is_active', 1)
|
||||||
@ -2411,12 +2414,57 @@ class EmployeeRestController extends AdminController
|
|||||||
->groupBy('display_name')
|
->groupBy('display_name')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
$data['ticket_type'] = [
|
if (!empty($client_id)) {
|
||||||
["ticket_type" => "1", "type_name" => "Claim-GMC"],
|
|
||||||
["ticket_type" => "2", "type_name" => "Claim-GPA"],
|
if (is_string($client_id) && preg_match('/^[a-f0-9]{32}$/i', $client_id)) {
|
||||||
["ticket_type" => "3", "type_name" => "EDLI"],
|
$client_data = $this->clientModel->where('MD5(id)', $client_id)->first();
|
||||||
["ticket_type" => "4", "type_name" => "GTLI"],
|
$client_id = $client_data['id'] ?? null;
|
||||||
];
|
}
|
||||||
|
|
||||||
|
$client_policy_data = $this->clientPolicyModel
|
||||||
|
->where('client_id', $client_id)
|
||||||
|
->where('is_active', 1)
|
||||||
|
->groupBy('policy_type_id')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
$data['ticket_type'] = [];
|
||||||
|
$addedTypes = [];
|
||||||
|
|
||||||
|
foreach ($client_policy_data as $value) {
|
||||||
|
|
||||||
|
if (in_array($value['policy_type_id'], [2,3,4,5]) && !in_array('1', $addedTypes)) {
|
||||||
|
$data['ticket_type'][] = ["ticket_type" => "1", "type_name" => "Claim-GMC"];
|
||||||
|
$addedTypes[] = '1';
|
||||||
|
|
||||||
|
} elseif (in_array($value['policy_type_id'], [1]) && !in_array('2', $addedTypes)) {
|
||||||
|
$data['ticket_type'][] = ["ticket_type" => "2", "type_name" => "Claim-GPA"];
|
||||||
|
$addedTypes[] = '2';
|
||||||
|
|
||||||
|
} elseif (in_array($value['policy_type_id'], [6]) && !in_array('3', $addedTypes)) {
|
||||||
|
$data['ticket_type'][] = ["ticket_type" => "3", "type_name" => "EDLI"];
|
||||||
|
$addedTypes[] = '3';
|
||||||
|
|
||||||
|
} elseif (in_array($value['policy_type_id'], [7]) && !in_array('4', $addedTypes)) {
|
||||||
|
$data['ticket_type'][] = ["ticket_type" => "4", "type_name" => "GTLI"];
|
||||||
|
$addedTypes[] = '4';
|
||||||
|
|
||||||
|
} elseif (in_array($value['policy_type_id'], [72]) && !in_array('72', $addedTypes)) {
|
||||||
|
$data['ticket_type'][] = ["ticket_type" => "72", "type_name" => "OPD"];
|
||||||
|
$addedTypes[] = '72';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usort($data['ticket_type'], fn($a,$b) => $a['ticket_type'] <=> $b['ticket_type']);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$data['ticket_type'] = [
|
||||||
|
["ticket_type" => "1", "type_name" => "Claim-GMC"],
|
||||||
|
["ticket_type" => "2", "type_name" => "Claim-GPA"],
|
||||||
|
["ticket_type" => "3", "type_name" => "EDLI"],
|
||||||
|
["ticket_type" => "4", "type_name" => "GTLI"],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
$claim_type = $this->ticketController->claimType;
|
$claim_type = $this->ticketController->claimType;
|
||||||
unset($claim_type[1][2]);
|
unset($claim_type[1][2]);
|
||||||
|
|||||||
@ -2575,4 +2575,29 @@ class EmployeeServiceController extends AdminController
|
|||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the inception Excel column configuration used for
|
||||||
|
* validating and processing Employee Upload with Events files.
|
||||||
|
* This allows other controllers to generate compatible Excel files.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getInceptionExcelColumns(): array
|
||||||
|
{
|
||||||
|
return $this->inception_excel_columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the correction Excel column configuration used for
|
||||||
|
* validating and processing Employee correction files.
|
||||||
|
* This is reused by other controllers when they need to generate
|
||||||
|
* a correction-compatible Excel programmatically.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCorrectionExcelColumns(): array
|
||||||
|
{
|
||||||
|
return $this->correction_excel_columns;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -34,8 +34,7 @@ class ExpenseController extends AdminController
|
|||||||
try {
|
try {
|
||||||
$data['tab_name'] = 'Expense';
|
$data['tab_name'] = 'Expense';
|
||||||
$data['page_name'] = 'Expense';
|
$data['page_name'] = 'Expense';
|
||||||
$descriptionPattern = '/^[a-zA-Z0-9\s\.\,\-\(\)\/\&\+]+$/u';
|
$descriptionPattern = '/^[a-zA-Z0-9\s\.\,\-\(\)\/\&\+\'"]+$/u';
|
||||||
|
|
||||||
// Raw GET filters
|
// Raw GET filters
|
||||||
$rawFilters = $this->request->getGet() ?? [];
|
$rawFilters = $this->request->getGet() ?? [];
|
||||||
$rawFilters = is_array($rawFilters) ? $rawFilters : [];
|
$rawFilters = is_array($rawFilters) ? $rawFilters : [];
|
||||||
@ -97,6 +96,7 @@ class ExpenseController extends AdminController
|
|||||||
// Clients for dropdown
|
// Clients for dropdown
|
||||||
$data['clients'] = $this->clientModel
|
$data['clients'] = $this->clientModel
|
||||||
->select('id, client_name, short_name')
|
->select('id, client_name, short_name')
|
||||||
|
->where('client_type', 1)
|
||||||
->where('is_active', 1)
|
->where('is_active', 1)
|
||||||
->orderBy('client_name', 'ASC')
|
->orderBy('client_name', 'ASC')
|
||||||
->findAll();
|
->findAll();
|
||||||
@ -228,11 +228,12 @@ class ExpenseController extends AdminController
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
'amount' => [
|
'amount' => [
|
||||||
'rules' => 'required|numeric|greater_than_equal_to[0]',
|
'rules' => 'required|numeric|greater_than_equal_to[0]|less_than_equal_to[100000000]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Amount is required',
|
'required' => 'Amount is required',
|
||||||
'numeric' => 'Amount must be numeric',
|
'numeric' => 'Amount must be numeric',
|
||||||
'greater_than_equal_to' => 'Amount cannot be negative',
|
'greater_than_equal_to' => 'Amount cannot be negative',
|
||||||
|
'less_than_equal_to' => 'Amount cannot be greater than 100 Cr.',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@ -54,19 +54,16 @@ class FhplApiController extends BaseController
|
|||||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
|
||||||
if (curl_errno($ch)) {
|
if (curl_errno($ch)) {
|
||||||
return $this->response->setJSON([
|
return ['status' => false,'error' => curl_error($ch),];
|
||||||
'status' => false,
|
|
||||||
'error' => curl_error($ch),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
|
|
||||||
return $this->response->setJSON([
|
return [
|
||||||
'status' => $httpCode === 200,
|
'status' => $httpCode === 200,
|
||||||
'http_code' => $httpCode,
|
'http_code' => $httpCode,
|
||||||
'data' => json_decode($response, true),
|
'data' => json_decode($response, true),
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SubmitClaim($claimId = null) // 515
|
public function SubmitClaim($claimId = null) // 515
|
||||||
@ -193,7 +190,7 @@ class FhplApiController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ['status' => false, 'message' => 'Claim Push API SUCCESS BUT claimsInfo EMPTY'];
|
return ['status' => false, 'message' => 'Claim Push API SUCCESS BUT claimsInfo EMPTY'];
|
||||||
// return $this->response->setJSON($response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ClaimDetail($claimId = null) //515
|
public function ClaimDetail($claimId = null) //515
|
||||||
@ -725,7 +722,7 @@ class FhplApiController extends BaseController
|
|||||||
$tokenResponse = json_decode($this->generateAuthToken()->getBody(), true);
|
$tokenResponse = json_decode($this->generateAuthToken()->getBody(), true);
|
||||||
|
|
||||||
if (empty($tokenResponse['data']['access_token'])) {
|
if (empty($tokenResponse['data']['access_token'])) {
|
||||||
return $this->response->setJSON(['status' => false,'message' => 'FHPL Token generation failed']);
|
return ['status' => false,'message' => 'FHPL Token generation failed'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$token = $tokenResponse['data']['access_token'];
|
$token = $tokenResponse['data']['access_token'];
|
||||||
@ -803,7 +800,7 @@ class FhplApiController extends BaseController
|
|||||||
$tokenResponse = json_decode($this->generateAuthToken()->getBody(), true);
|
$tokenResponse = json_decode($this->generateAuthToken()->getBody(), true);
|
||||||
|
|
||||||
if (empty($tokenResponse['data']['access_token'])) {
|
if (empty($tokenResponse['data']['access_token'])) {
|
||||||
return $this->response->setJSON(['status' => false,'message' => 'FHPL Token generation failed']);
|
return ['status' => false,'message' => 'FHPL Token generation failed'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$token = $tokenResponse['data']['access_token'];
|
$token = $tokenResponse['data']['access_token'];
|
||||||
|
|||||||
@ -61,10 +61,7 @@ class HealthIndiaApiController extends BaseController
|
|||||||
if (curl_errno($ch)) {
|
if (curl_errno($ch)) {
|
||||||
log_message('error', 'HEALTH_INDIA TOKEN GENERATION FAILED | Error: ' . curl_error($ch));
|
log_message('error', 'HEALTH_INDIA TOKEN GENERATION FAILED | Error: ' . curl_error($ch));
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
return $this->response->setJSON([
|
return ['status' => false, 'error' => curl_error($ch)];
|
||||||
'status' => false,
|
|
||||||
'error' => curl_error($ch),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
@ -77,11 +74,11 @@ class HealthIndiaApiController extends BaseController
|
|||||||
log_message('error', 'HEALTH_INDIA TOKEN GENERATION FAILED | Response: ' . $response);
|
log_message('error', 'HEALTH_INDIA TOKEN GENERATION FAILED | Response: ' . $response);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->response->setJSON([
|
return [
|
||||||
'status' => $httpCode === 200,
|
'status' => $httpCode === 200,
|
||||||
'http_code' => $httpCode,
|
'http_code' => $httpCode,
|
||||||
'data' => $responseData,
|
'data' => $responseData,
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SubmitClaim($claimId = null)
|
public function SubmitClaim($claimId = null)
|
||||||
|
|||||||
@ -370,7 +370,9 @@ class LeadsController extends BaseController
|
|||||||
$actual_lead_id = ! empty($actual_lead_id) ? $actual_lead_id : null;
|
$actual_lead_id = ! empty($actual_lead_id) ? $actual_lead_id : null;
|
||||||
$postData = $this->request->getPost();
|
$postData = $this->request->getPost();
|
||||||
$data = $this->prepareLeadData();
|
$data = $this->prepareLeadData();
|
||||||
$rules = [
|
|
||||||
|
|
||||||
|
$rules = [
|
||||||
|
|
||||||
'lead_type' => [
|
'lead_type' => [
|
||||||
'rules' => 'integer',
|
'rules' => 'integer',
|
||||||
@ -380,21 +382,6 @@ class LeadsController extends BaseController
|
|||||||
'rules' => 'required',
|
'rules' => 'required',
|
||||||
'errors' => ['required' => 'Issuer is required'],
|
'errors' => ['required' => 'Issuer is required'],
|
||||||
],
|
],
|
||||||
'entity_type_id' => [
|
|
||||||
'rules' => 'required',
|
|
||||||
'errors' => ['required' => 'Entity Type is required'],
|
|
||||||
],
|
|
||||||
'client_name' => [
|
|
||||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9_ -]+$/]',
|
|
||||||
'errors' => [
|
|
||||||
'required' => 'Client Name is required',
|
|
||||||
'regex_match' => 'Client Name only letters, numbers, space, hyphens and underscores are allowed',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'client_short_name' => [
|
|
||||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]',
|
|
||||||
'errors' => ['required' => 'Client Short Name is required', 'regex_match' => 'Client Short Name only letters, numbers, hyphens and underscores are allowed'],
|
|
||||||
],
|
|
||||||
'gst' => [
|
'gst' => [
|
||||||
'rules' => 'required|regex_match[/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/]',
|
'rules' => 'required|regex_match[/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
@ -460,6 +447,30 @@ class LeadsController extends BaseController
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (isset($postData['lead_type']) && $postData['lead_type'] == 1) {
|
||||||
|
|
||||||
|
$rules['entity_type_id'] = [
|
||||||
|
'rules' => 'required',
|
||||||
|
'errors' => ['required' => 'Entity Type is required'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$rules['client_name'] = [
|
||||||
|
'rules' => 'required|regex_match[/^[a-zA-Z0-9_ -]+$/]',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Client Name is required',
|
||||||
|
'regex_match' => 'Client Name only letters, numbers, space, hyphens and underscores are allowed',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$rules['client_short_name'] = [
|
||||||
|
'rules' => 'required|regex_match[/^[a-zA-Z0-9_-]+$/]',
|
||||||
|
'errors' => [
|
||||||
|
'required' => 'Client Short Name is required',
|
||||||
|
'regex_match' => 'Client Short Name only letters, numbers, hyphens and underscores are allowed'
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($postData as $key => $value) {
|
foreach ($postData as $key => $value) {
|
||||||
// Check if the key starts with 'docs_name_'
|
// Check if the key starts with 'docs_name_'
|
||||||
if (strpos($key, 'docs_name_') === 0) {
|
if (strpos($key, 'docs_name_') === 0) {
|
||||||
@ -476,10 +487,6 @@ class LeadsController extends BaseController
|
|||||||
// print_r($rules); die;
|
// print_r($rules); die;
|
||||||
|
|
||||||
if ((int) $this->request->getPost('lead_form_type') === 2) {
|
if ((int) $this->request->getPost('lead_form_type') === 2) {
|
||||||
$rules['client_type'] = [
|
|
||||||
'rules' => 'required',
|
|
||||||
'errors' => ['required' => 'Client Type is required'],
|
|
||||||
];
|
|
||||||
$rules['policy_type_id'] = [
|
$rules['policy_type_id'] = [
|
||||||
'rules' => 'required',
|
'rules' => 'required',
|
||||||
'errors' => ['required' => 'Policy Type is required'],
|
'errors' => ['required' => 'Policy Type is required'],
|
||||||
@ -4706,8 +4713,9 @@ class LeadsController extends BaseController
|
|||||||
|
|
||||||
// 🔹 Client Details (Single Row)
|
// 🔹 Client Details (Single Row)
|
||||||
$data['actual_lead_client_details'] = $this->leadModel
|
$data['actual_lead_client_details'] = $this->leadModel
|
||||||
->select('company_name, email, phone, address, website, gst_number, status, assigned_to')
|
->select('sales_actual_leads.company_name, clients.short_name, sales_actual_leads.email, sales_actual_leads.phone, sales_actual_leads.address, sales_actual_leads.website, sales_actual_leads.gst_number, sales_actual_leads.status, sales_actual_leads.assigned_to')
|
||||||
->where('lead_id', $actual_lead_id)
|
->join('clients', 'clients.id = sales_actual_leads.client_id', 'left')
|
||||||
|
->where('sales_actual_leads.lead_id', $actual_lead_id)
|
||||||
->first(); // first row only
|
->first(); // first row only
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -573,78 +573,89 @@ class MediAssistApiController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract Claim Status
|
// Extract Claim Status
|
||||||
$claimData = $response['data']['claimsData'][0];
|
$allClaimData = $response['data']['claimsData'];
|
||||||
$currentStatus = $claimData['claim_Current_Status'] ?? '';
|
|
||||||
$tpa_claim_no = $claimData['tpA_CLAIM_NO'] ?? '';
|
|
||||||
$tpa_claim_type = $claimData['typE_OF_CLAIM'] ?? '';
|
|
||||||
$tpa_ailments = ($claimData['ailment'] ?? '') . ' - ' . ($claimData['ailmenT_DESC'] ?? '');
|
|
||||||
|
|
||||||
// VALID STATUS LIST
|
$currentStatus = "";
|
||||||
$validStatuses = [
|
foreach ($allClaimData as $claimData) {
|
||||||
"Claim Received" => 1,
|
|
||||||
"In Progress" => 5,
|
|
||||||
"Processed" => 11,
|
|
||||||
"Claim Paid" => 11,
|
|
||||||
"Denied" => 13,
|
|
||||||
"Cancelled" => 13,
|
|
||||||
|
|
||||||
"Information Awaited" => 4,
|
|
||||||
"Confirmation Awaited" => 4,
|
|
||||||
"Information Awaited Reminder" => 4,
|
|
||||||
"Information Awaited Final Reminder" => 4,
|
|
||||||
"Insurer Concurrence Awaited" => 6,
|
|
||||||
"Closed" => 12,
|
|
||||||
|
|
||||||
"Physical Documents Awaited" => 9,
|
|
||||||
"Processed - Payment Initiated" => 10,
|
|
||||||
"Processed - Transaction Failed" => 10,
|
|
||||||
"Processed - Account Details Updated" => 10,
|
|
||||||
"Processed - Debit Note Raised With Insurer for Payment"=> 10,
|
|
||||||
"Processed - Payment Initiated by Insurer" => 10,
|
|
||||||
"Payment - Refunded to Insurer" => 14,
|
|
||||||
"Processed - Processing Payment" => 10,
|
|
||||||
"Processed - Physical Documents Awaited" => 9,
|
|
||||||
|
|
||||||
// Extra Mappings (based on your DB list)
|
|
||||||
"NON ID" => 1,
|
|
||||||
"ID NOT GENERATED" => 2,
|
|
||||||
"CDA" => 3,
|
|
||||||
"REJECTED" => 8,
|
|
||||||
"APPROVED" => 9,
|
|
||||||
"PAYMENT INITIATED" => 10,
|
|
||||||
"SETTLED" => 11,
|
|
||||||
"RETURNED" => 14,
|
|
||||||
"UNDER PROCESS - TPA" => 61,
|
|
||||||
"DENIAL REVIEW AWAITED" => 66,
|
|
||||||
];
|
|
||||||
|
|
||||||
$updateArray = [
|
$currentStatus = $claimData['claim_Current_Status'] ?? '';
|
||||||
'tpa_claim_status' => $currentStatus,
|
$tpa_claim_no = $claimData['tpA_CLAIM_NO'] ?? '';
|
||||||
'updated_at' => date('Y-m-d H:i:s'),
|
$tpa_claim_type = $claimData['typE_OF_CLAIM'] ?? '';
|
||||||
];
|
$tpa_ailments = ($claimData['ailment'] ?? '') . ' - ' . ($claimData['ailmenT_DESC'] ?? '');
|
||||||
|
|
||||||
if (isset($validStatuses[$currentStatus])) {
|
// VALID STATUS LIST
|
||||||
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
$validStatuses = [
|
||||||
|
"Claim Received" => 1,
|
||||||
|
"In Progress" => 5,
|
||||||
|
"Processed" => 11,
|
||||||
|
"Claim Paid" => 11,
|
||||||
|
"Denied" => 13,
|
||||||
|
"Cancelled" => 13,
|
||||||
|
|
||||||
|
"Information Awaited" => 4,
|
||||||
|
"Confirmation Awaited" => 4,
|
||||||
|
"Information Awaited Reminder" => 4,
|
||||||
|
"Information Awaited Final Reminder" => 4,
|
||||||
|
"Insurer Concurrence Awaited" => 6,
|
||||||
|
"Closed" => 12,
|
||||||
|
|
||||||
|
"Physical Documents Awaited" => 9,
|
||||||
|
"Processed - Payment Initiated" => 10,
|
||||||
|
"Processed - Transaction Failed" => 10,
|
||||||
|
"Processed - Account Details Updated" => 10,
|
||||||
|
"Processed - Debit Note Raised With Insurer for Payment"=> 10,
|
||||||
|
"Processed - Payment Initiated by Insurer" => 10,
|
||||||
|
"Payment - Refunded to Insurer" => 14,
|
||||||
|
"Processed - Processing Payment" => 10,
|
||||||
|
"Processed - Physical Documents Awaited" => 9,
|
||||||
|
|
||||||
|
// Extra Mappings (based on your DB list)
|
||||||
|
"NON ID" => 1,
|
||||||
|
"ID NOT GENERATED" => 2,
|
||||||
|
"CDA" => 3,
|
||||||
|
"REJECTED" => 8,
|
||||||
|
"APPROVED" => 9,
|
||||||
|
"PAYMENT INITIATED" => 10,
|
||||||
|
"SETTLED" => 11,
|
||||||
|
"RETURNED" => 14,
|
||||||
|
"UNDER PROCESS - TPA" => 61,
|
||||||
|
"DENIAL REVIEW AWAITED" => 66,
|
||||||
|
];
|
||||||
|
|
||||||
|
$updateArray = [
|
||||||
|
'tpa_claim_status' => $currentStatus,
|
||||||
|
'updated_at' => date('Y-m-d H:i:s'),
|
||||||
|
'last_updated_by' => 'API',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isset($validStatuses[$currentStatus])) {
|
||||||
|
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
||||||
|
}
|
||||||
|
if (empty($ticket['tpa_claim_id']) || $ticket['tpa_claim_id'] === null) {
|
||||||
|
$updateArray['tpa_claim_id'] = $tpa_claim_no;
|
||||||
|
}
|
||||||
|
if (empty($ticket['claim_number']) || $ticket['claim_number'] === null) {
|
||||||
|
$updateArray['claim_number'] = $tpa_claim_no;
|
||||||
|
}
|
||||||
|
if (!empty($tpa_claim_type)) {
|
||||||
|
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
||||||
|
}
|
||||||
|
if (!empty($tpa_ailments)) {
|
||||||
|
$updateArray['tpa_ailments'] = $tpa_ailments;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if($ticket['doa'] == $this->mediDate($claimData['datE_OF_ADMISSION'] ?? null) || $ticket['claim_number'] == $tpa_claim_no){
|
||||||
|
// UPDATE ticket_master
|
||||||
|
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
||||||
|
|
||||||
|
// LOG UPDATE
|
||||||
|
log_message('error',"MEDI_ASSIST - Claim Status SUCCESS | Updated ticket ID $claimId with Claim Status: $currentStatus");
|
||||||
|
}else{
|
||||||
|
log_message('error', "MEDI_ASSIST | Fetch Claim Status | Claim Status NOT UPDATED | TicketID={$claimId} | TicketDOA={$ticket['doa']} | ClaimDOA={$claimData['datE_OF_ADMISSION']} | Status={$currentStatus}");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (empty($ticket['tpa_claim_id']) || $ticket['tpa_claim_id'] === null) {
|
|
||||||
$updateArray['tpa_claim_id'] = $tpa_claim_no;
|
|
||||||
}
|
|
||||||
if (empty($ticket['claim_number']) || $ticket['claim_number'] === null) {
|
|
||||||
$updateArray['claim_number'] = $tpa_claim_no;
|
|
||||||
}
|
|
||||||
if (!empty($tpa_claim_type)) {
|
|
||||||
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
|
||||||
}
|
|
||||||
if (!empty($tpa_ailments)) {
|
|
||||||
$updateArray['tpa_ailments'] = $tpa_ailments;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// UPDATE ticket_master
|
|
||||||
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
|
||||||
|
|
||||||
// LOG UPDATE
|
|
||||||
log_message('error',"MEDI_ASSIST - Claim Status SUCCESS | Updated ticket ID $claimId with Claim Status: $currentStatus");
|
|
||||||
|
|
||||||
return ['status' => true,'message' => 'Claim Status updated.','updated_status' => $currentStatus,'api_response' => $response];
|
return ['status' => true,'message' => 'Claim Status updated.','updated_status' => $currentStatus,'api_response' => $response];
|
||||||
}
|
}
|
||||||
@ -796,6 +807,10 @@ class MediAssistApiController extends BaseController
|
|||||||
'gender' => strtoupper($row['benefSex'] ?? null),
|
'gender' => strtoupper($row['benefSex'] ?? null),
|
||||||
'self' => strtolower($row['relName'] ?? '') === 'self' ? 1 : 0,
|
'self' => strtolower($row['relName'] ?? '') === 'self' ? 1 : 0,
|
||||||
|
|
||||||
|
'si' => $row['sum_insured'] ?? null,
|
||||||
|
'doj' => $this->mediDate($row['benefWEF'] ?? null),
|
||||||
|
|
||||||
|
|
||||||
'tpa_id' => trim($row['benefMediAssistID'] ?? null),
|
'tpa_id' => trim($row['benefMediAssistID'] ?? null),
|
||||||
'age' => is_numeric($row['benefAge'] ?? null)
|
'age' => is_numeric($row['benefAge'] ?? null)
|
||||||
? (int) $row['benefAge']
|
? (int) $row['benefAge']
|
||||||
@ -890,6 +905,9 @@ class MediAssistApiController extends BaseController
|
|||||||
// CALL API
|
// CALL API
|
||||||
$response = call_third_party_api($url, $method, $headers, $body);
|
$response = call_third_party_api($url, $method, $headers, $body);
|
||||||
|
|
||||||
|
log_message('error','MEDI_ASSIST - 2 hours Claim Status API Response: ' . json_encode($response));
|
||||||
|
|
||||||
|
|
||||||
if ($response['status'] != true || empty($response['data']['claimsData'][0])) {
|
if ($response['status'] != true || empty($response['data']['claimsData'][0])) {
|
||||||
log_message('error','MEDI_ASSIST - Claim Status FAILED | for ticket ID: ' . $claimId.' | response: '.json_encode($response));
|
log_message('error','MEDI_ASSIST - Claim Status FAILED | for ticket ID: ' . $claimId.' | response: '.json_encode($response));
|
||||||
$error_data[$claimId] = [
|
$error_data[$claimId] = [
|
||||||
@ -900,77 +918,94 @@ class MediAssistApiController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract Claim Status
|
// Extract Claim Status
|
||||||
$claimData = $response['data']['claimsData'][0];
|
// $allClaimData = $response['data']['claimsData'][0];
|
||||||
$currentStatus = $claimData['claim_Current_Status'] ?? '';
|
$allClaimData = $response['data']['claimsData'];
|
||||||
$tpa_claim_no = $claimData['tpA_CLAIM_NO'] ?? '';
|
|
||||||
$tpa_claim_type = $claimData['typE_OF_CLAIM'] ?? '';
|
|
||||||
$tpa_ailments = ($claimData['ailment'] ?? '') . ' - ' . ($claimData['ailmenT_DESC'] ?? '');
|
|
||||||
|
|
||||||
// VALID STATUS LIST
|
|
||||||
$validStatuses = [
|
|
||||||
|
|
||||||
"Claim Received" => 1,
|
foreach ($allClaimData as $claimData) {
|
||||||
"In Progress" => 5,
|
|
||||||
"Processed" => 11,
|
$currentStatus = $claimData['claim_Current_Status'] ?? '';
|
||||||
"Claim Paid" => 11,
|
$tpa_claim_no = $claimData['tpA_CLAIM_NO'] ?? '';
|
||||||
"Denied" => 13,
|
$tpa_claim_type = $claimData['typE_OF_CLAIM'] ?? '';
|
||||||
"Cancelled" => 13,
|
$tpa_ailments = ($claimData['ailment'] ?? '') . ' - ' . ($claimData['ailmenT_DESC'] ?? '');
|
||||||
|
|
||||||
"Information Awaited" => 4,
|
|
||||||
"Confirmation Awaited" => 4,
|
|
||||||
"Information Awaited Reminder" => 4,
|
|
||||||
"Information Awaited Final Reminder" => 4,
|
|
||||||
"Insurer Concurrence Awaited" => 6,
|
|
||||||
"Closed" => 12,
|
|
||||||
|
|
||||||
"Physical Documents Awaited" => 9,
|
|
||||||
"Processed - Payment Initiated" => 10,
|
|
||||||
"Processed - Transaction Failed" => 10,
|
|
||||||
"Processed - Account Details Updated" => 10,
|
|
||||||
"Processed - Debit Note Raised With Insurer for Payment"=> 10,
|
|
||||||
"Processed - Payment Initiated by Insurer" => 10,
|
|
||||||
"Payment - Refunded to Insurer" => 14,
|
|
||||||
"Processed - Processing Payment" => 10,
|
|
||||||
"Processed - Physical Documents Awaited" => 9,
|
|
||||||
|
|
||||||
// Extra Mappings (based on your DB list)
|
|
||||||
"NON ID" => 1,
|
|
||||||
"ID NOT GENERATED" => 2,
|
|
||||||
"CDA" => 3,
|
|
||||||
"REJECTED" => 8,
|
|
||||||
"APPROVED" => 9,
|
|
||||||
"PAYMENT INITIATED" => 10,
|
|
||||||
"SETTLED" => 11,
|
|
||||||
"RETURNED" => 14,
|
|
||||||
"UNDER PROCESS - TPA" => 61,
|
|
||||||
"DENIAL REVIEW AWAITED" => 66,
|
|
||||||
];
|
|
||||||
|
|
||||||
$updateArray = [
|
// VALID STATUS LIST
|
||||||
'tpa_claim_status' => $currentStatus,
|
$validStatuses = [
|
||||||
'tpa_claim_id' => $tpa_claim_no,
|
|
||||||
'claim_number' => $tpa_claim_no,
|
|
||||||
'updated_at' => date('Y-m-d H:i:s'),
|
|
||||||
];
|
|
||||||
if (isset($validStatuses[$currentStatus])) {
|
|
||||||
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
|
||||||
}
|
|
||||||
if (!empty($tpa_claim_type)) {
|
|
||||||
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
|
||||||
}
|
|
||||||
if (!empty($tpa_ailments)) {
|
|
||||||
$updateArray['tpa_ailments'] = $tpa_ailments;
|
|
||||||
}
|
|
||||||
|
|
||||||
// UPDATE ticket_master
|
"Claim Received" => 1,
|
||||||
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
"In Progress" => 5,
|
||||||
$status_updated_count ++;
|
"Processed" => 11,
|
||||||
|
"Claim Paid" => 11,
|
||||||
// LOG UPDATE
|
"Denied" => 13,
|
||||||
log_message('error',"MEDI_ASSIST - Claim Status SUCCESS | Updated ticket ID $claimId with Claim Status: $currentStatus");
|
"Cancelled" => 13,
|
||||||
|
|
||||||
|
"Information Awaited" => 4,
|
||||||
|
"Confirmation Awaited" => 4,
|
||||||
|
"Information Awaited Reminder" => 4,
|
||||||
|
"Information Awaited Final Reminder" => 4,
|
||||||
|
"Insurer Concurrence Awaited" => 6,
|
||||||
|
"Closed" => 12,
|
||||||
|
|
||||||
|
"Physical Documents Awaited" => 9,
|
||||||
|
"Processed - Payment Initiated" => 10,
|
||||||
|
"Processed - Transaction Failed" => 10,
|
||||||
|
"Processed - Account Details Updated" => 10,
|
||||||
|
"Processed - Debit Note Raised With Insurer for Payment"=> 10,
|
||||||
|
"Processed - Payment Initiated by Insurer" => 10,
|
||||||
|
"Payment - Refunded to Insurer" => 14,
|
||||||
|
"Processed - Processing Payment" => 10,
|
||||||
|
"Processed - Physical Documents Awaited" => 9,
|
||||||
|
|
||||||
|
// Extra Mappings (based on your DB list)
|
||||||
|
"NON ID" => 1,
|
||||||
|
"ID NOT GENERATED" => 2,
|
||||||
|
"CDA" => 3,
|
||||||
|
"REJECTED" => 8,
|
||||||
|
"APPROVED" => 9,
|
||||||
|
"PAYMENT INITIATED" => 10,
|
||||||
|
"SETTLED" => 11,
|
||||||
|
"RETURNED" => 14,
|
||||||
|
"UNDER PROCESS - TPA" => 61,
|
||||||
|
"DENIAL REVIEW AWAITED" => 66,
|
||||||
|
];
|
||||||
|
|
||||||
|
$updateArray = [
|
||||||
|
'tpa_claim_status' => $currentStatus,
|
||||||
|
'updated_at' => date('Y-m-d H:i:s'),
|
||||||
|
'last_updated_by' => 'API',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isset($validStatuses[$currentStatus])) {
|
||||||
|
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
||||||
|
}
|
||||||
|
if (empty($ticket['tpa_claim_id']) || $ticket['tpa_claim_id'] === null) {
|
||||||
|
$updateArray['tpa_claim_id'] = $tpa_claim_no;
|
||||||
|
}
|
||||||
|
if (empty($ticket['claim_number']) || $ticket['claim_number'] === null) {
|
||||||
|
$updateArray['claim_number'] = $tpa_claim_no;
|
||||||
|
}
|
||||||
|
if (!empty($tpa_claim_type)) {
|
||||||
|
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
||||||
|
}
|
||||||
|
if (!empty($tpa_ailments)) {
|
||||||
|
$updateArray['tpa_ailments'] = $tpa_ailments;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($ticket['doa'] == $this->mediDate($claimData['datE_OF_ADMISSION'] ?? null) || $ticket['claim_number'] == $tpa_claim_no){
|
||||||
|
// UPDATE ticket_master
|
||||||
|
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
||||||
|
$status_updated_count ++;
|
||||||
|
|
||||||
|
// LOG UPDATE
|
||||||
|
log_message('error',"MEDI_ASSIST - Claim Status SUCCESS | Updated ticket ID $claimId with Claim Status: $currentStatus");
|
||||||
|
}else{
|
||||||
|
log_message('error', "MEDI_ASSIST | 2 hours | Claim Status NOT UPDATED | TicketID={$claimId} | TicketDOA={$ticket['doa']} | ClaimDOA={$claimData['datE_OF_ADMISSION']} | Status={$currentStatus}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_message('error',"MEDI_ASSIST - Claim Status ENDED | 2 hours | Total Tickets={".count($TicketData)."} | Status Updated={$status_updated_count} | Errors={".count($error_data)."}");
|
||||||
|
|
||||||
return $this->response->setJSON([
|
return $this->response->setJSON([
|
||||||
'status' => true,
|
'status' => true,
|
||||||
'message' => 'Claim Status updated.',
|
'message' => 'Claim Status updated.',
|
||||||
@ -1314,7 +1349,72 @@ class MediAssistApiController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getWellnessSSORedirectUrl($planId, $emp_code)
|
||||||
|
{
|
||||||
|
log_message('error', 'MEDI_ASSIST - Wellness SSO URL generation | Plan ID: ' . $planId . ' | Employee code: ' . $emp_code);
|
||||||
|
if (empty($planId) || empty($emp_code)) {
|
||||||
|
log_message('error', 'MEDI_ASSIST - Wellness SSO URL generation | Plan ID and employee code are required.');
|
||||||
|
return [
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Coming soon........!',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configuration – prefer environment variables, fall back to demo values
|
||||||
|
$keyString = env('MEDIASSIST_WELLNESS_KEY');
|
||||||
|
$ivString = env('MEDIASSIST_WELLNESS_IV');
|
||||||
|
$loginUrlTemplate = env('MEDIASSIST_WELLNESS_LOGIN_URL');
|
||||||
|
$cipher_algorithm = 'AES-256-CBC';
|
||||||
|
|
||||||
|
if (empty($keyString) || empty($ivString) || empty($loginUrlTemplate)) {
|
||||||
|
log_message('error', 'MEDI_ASSIST - Wellness SSO URL generation | Key string, IV string and login URL are required.');
|
||||||
|
return [
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Key string, IV string and login URL are required.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plain SSO JSON payload, as per Medi Assist sample
|
||||||
|
$payload = [
|
||||||
|
'Id' => $emp_code,
|
||||||
|
'expiryTime' => time() + (10 * 60), // 10 minutes
|
||||||
|
'CPartnerId' => $planId,
|
||||||
|
];
|
||||||
|
|
||||||
|
$plainJson = json_encode($payload, JSON_UNESCAPED_SLASHES);
|
||||||
|
|
||||||
|
// Derive a 32‑byte key (AES‑256) and 16‑byte IV from the provided strings
|
||||||
|
// $key = substr(hash('sha256', $keyString, true), 0, 32);
|
||||||
|
// $iv = substr(hash('md5', $ivString, true), 0, 16);
|
||||||
|
|
||||||
|
// Encrypt with AES‑256‑CBC + PKCS7 padding (OpenSSL default)
|
||||||
|
$cipherTextRaw = openssl_encrypt($plainJson, $cipher_algorithm, $keyString, OPENSSL_RAW_DATA, $ivString);
|
||||||
|
|
||||||
|
|
||||||
|
if ($cipherTextRaw === false) {
|
||||||
|
log_message('error', 'MEDI_ASSIST - Wellness SSO URL generation | Encryption failed while generating Medi Assist wellness token.');
|
||||||
|
return [
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Encryption failed while generating Medi Assist wellness token.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Base64 encode and URL‑encode for use as EncryptedSSO
|
||||||
|
$encryptedSSO = urlencode(base64_encode($cipherTextRaw));
|
||||||
|
// $encryptedSSO = rtrim(strtr(base64_encode($cipherTextRaw), '+/', '-_'), '=');
|
||||||
|
|
||||||
|
// Build the final login URL
|
||||||
|
$loginUrl = str_replace(['{0}', '{1}'], [$planId, $encryptedSSO], $loginUrlTemplate);
|
||||||
|
|
||||||
|
log_message('error', 'MEDI_ASSIST - Wellness SSO URL generation | Medi Assist wellness SSO URL generated successfully.');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Medi Assist wellness SSO URL generated successfully.',
|
||||||
|
'data' => $loginUrl
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -925,7 +925,6 @@ class PolicyTransactionController extends BaseController
|
|||||||
// ==========================================
|
// ==========================================
|
||||||
'client_type' => ['label' => 'Client Type', 'rules' => 'required', 'errors' => ['required' => 'Client Type must be selected.']],
|
'client_type' => ['label' => 'Client Type', 'rules' => 'required', 'errors' => ['required' => 'Client Type must be selected.']],
|
||||||
'client_id' => ['label' => 'Client', 'rules' => 'required', 'errors' => ['required' => 'Please select a Client.']],
|
'client_id' => ['label' => 'Client', 'rules' => 'required', 'errors' => ['required' => 'Please select a Client.']],
|
||||||
'client_branch_id' => ['label' => 'Client Branch', 'rules' => 'required', 'errors' => ['required' => 'Client branch is required']],
|
|
||||||
'issue_type' => ['label' => 'Business Type', 'rules' => 'required', 'errors' => ['required' => 'Please select a Business Type.']],
|
'issue_type' => ['label' => 'Business Type', 'rules' => 'required', 'errors' => ['required' => 'Please select a Business Type.']],
|
||||||
'ref' => ['label' => 'Reference', 'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 _-]+$/]', 'errors' => [
|
'ref' => ['label' => 'Reference', 'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 _-]+$/]', 'errors' => [
|
||||||
'regex_match' => 'The {field} can only contain letters, numbers, spaces, dashes, and underscores.'
|
'regex_match' => 'The {field} can only contain letters, numbers, spaces, dashes, and underscores.'
|
||||||
@ -997,14 +996,14 @@ class PolicyTransactionController extends BaseController
|
|||||||
'pos_id' => ['label' => 'POS', 'rules' => 'permit_empty', 'errors' => []],
|
'pos_id' => ['label' => 'POS', 'rules' => 'permit_empty', 'errors' => []],
|
||||||
'doc_name.*' => [
|
'doc_name.*' => [
|
||||||
'label' => 'Policy Document Name',
|
'label' => 'Policy Document Name',
|
||||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 _-]+$/]|min_length[2]|max_length[100]',
|
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]|min_length[2]|max_length[100]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'regex_match' => 'The {field} can only contain letters, numbers, spaces, dashes, and underscores.'
|
'regex_match' => 'The {field} can only contain letters, numbers, spaces, dashes, and underscores.'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'other_docs_name.*' => [
|
'other_docs_name.*' => [
|
||||||
'label' => 'Vehicle Document Name',
|
'label' => 'Vehicle Document Name',
|
||||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 _-]+$/]|min_length[2]|max_length[100]',
|
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]|min_length[2]|max_length[100]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'regex_match' => 'The {field} can only contain letters, numbers, spaces, dashes, and underscores.'
|
'regex_match' => 'The {field} can only contain letters, numbers, spaces, dashes, and underscores.'
|
||||||
]
|
]
|
||||||
@ -1052,6 +1051,9 @@ class PolicyTransactionController extends BaseController
|
|||||||
'exp_amt.*' => ['label' => 'Expected Amount', 'rules' => 'permit_empty|decimal', 'errors' => ['decimal' => 'The Expected Amount field must contain a valid number.']]
|
'exp_amt.*' => ['label' => 'Expected Amount', 'rules' => 'permit_empty|decimal', 'errors' => ['decimal' => 'The Expected Amount field must contain a valid number.']]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if(isset($post_data['client_type']) && $post_data['client_type'] == 1){
|
||||||
|
$rules['client_branch_id'] = ['label' => 'Client Branch', 'rules' => 'required', 'errors' => ['required' => 'Client branch is required']];
|
||||||
|
}
|
||||||
|
|
||||||
$isValid = $this->validate($rules);
|
$isValid = $this->validate($rules);
|
||||||
|
|
||||||
@ -2668,7 +2670,7 @@ class PolicyTransactionController extends BaseController
|
|||||||
'co_share_id.*' => ['label' => 'Record ID', 'rules' => 'permit_empty','errors' => []],
|
'co_share_id.*' => ['label' => 'Record ID', 'rules' => 'permit_empty','errors' => []],
|
||||||
'doc_name.*' => [
|
'doc_name.*' => [
|
||||||
'label' => 'Policy Document Name',
|
'label' => 'Policy Document Name',
|
||||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 _-]+$/]|min_length[2]|max_length[100]',
|
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]|min_length[2]|max_length[100]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'regex_match' => 'The {field} can only contain letters, numbers, spaces, dashes, and underscores.'
|
'regex_match' => 'The {field} can only contain letters, numbers, spaces, dashes, and underscores.'
|
||||||
]
|
]
|
||||||
@ -5618,17 +5620,18 @@ class PolicyTransactionController extends BaseController
|
|||||||
$policy_end_date = trim($row[11]);
|
$policy_end_date = trim($row[11]);
|
||||||
$revenue_type = trim($row[12]);
|
$revenue_type = trim($row[12]);
|
||||||
|
|
||||||
$base_premium = trim($row[16]);
|
$base_premium = cleanNumber($row[16]);
|
||||||
$non_commission_premium_amount = trim($row[17]);
|
$non_commission_premium_amount = cleanNumber($row[17]);
|
||||||
$tp_premium = trim($row[18]);
|
$tp_premium = cleanNumber($row[18]);
|
||||||
$igst = trim($row[19]);
|
$igst = cleanNumber($row[19]);
|
||||||
$cgst = trim($row[20]);
|
$cgst = cleanNumber($row[20]);
|
||||||
$sgst = trim($row[21]);
|
$sgst = cleanNumber($row[21]);
|
||||||
$stamp_duty = trim($row[22]);
|
$stamp_duty = cleanNumber($row[22]);
|
||||||
|
$agreed_amount = cleanNumber($row[23]);
|
||||||
|
$agreed_bp_percentage = cleanNumber($row[24]);
|
||||||
|
$agreed_tp_percentage = cleanNumber($row[25]);
|
||||||
|
|
||||||
// $total = trim($row[23]);
|
// $total = trim($row[23]);
|
||||||
$agreed_amount = trim($row[23]);
|
|
||||||
$agreed_bp_percentage = trim($row[24]);
|
|
||||||
$agreed_tp_percentage = trim($row[25]);
|
|
||||||
// $actual_bp_amount = trim($row[27]);
|
// $actual_bp_amount = trim($row[27]);
|
||||||
// $actual_tp_amount = trim($row[28]);
|
// $actual_tp_amount = trim($row[28]);
|
||||||
// $actual_bp_percentage = trim($row[29]);
|
// $actual_bp_percentage = trim($row[29]);
|
||||||
@ -5647,16 +5650,16 @@ class PolicyTransactionController extends BaseController
|
|||||||
$rewards = 0;
|
$rewards = 0;
|
||||||
|
|
||||||
$calculation = calculateMotorPolicyAmounts([
|
$calculation = calculateMotorPolicyAmounts([
|
||||||
'base_premium' => trim($row[16]),
|
'base_premium' => $base_premium,
|
||||||
'non_commission_premium_amount' => trim($row[17]),
|
'non_commission_premium_amount' => $non_commission_premium_amount,
|
||||||
'tp_premium' => trim($row[18]),
|
'tp_premium' => $tp_premium,
|
||||||
'igst' => trim($row[19]),
|
'igst' => $igst,
|
||||||
'cgst' => trim($row[20]),
|
'cgst' => $cgst,
|
||||||
'sgst' => trim($row[21]),
|
'sgst' => $sgst,
|
||||||
'stamp_duty' => trim($row[22]),
|
'stamp_duty' => $stamp_duty,
|
||||||
'agreed_amount' => trim($row[23]),
|
'agreed_amount' => $agreed_amount,
|
||||||
'agreed_bp_percentage' => trim($row[24]),
|
'agreed_bp_percentage' => $agreed_bp_percentage,
|
||||||
'agreed_tp_percentage' => trim($row[25]),
|
'agreed_tp_percentage' => $agreed_tp_percentage,
|
||||||
'standard_bp_percentage' => 15.00,
|
'standard_bp_percentage' => 15.00,
|
||||||
'standard_tp_percentage' => 2.5
|
'standard_tp_percentage' => 2.5
|
||||||
]);
|
]);
|
||||||
|
|||||||
@ -1272,7 +1272,7 @@ class RestAuthenticationController extends AdminController
|
|||||||
log_message('error', ' ');
|
log_message('error', ' ');
|
||||||
log_message('error', ' ************************************* POST END **************************************** ');
|
log_message('error', ' ************************************* POST END **************************************** ');
|
||||||
log_message('error', ' ');
|
log_message('error', ' ');
|
||||||
return $this->respond(['status' => 'success','code' => 200,'data' => "", 'message' => "Mpin - exist" , 'Mpin' =>$employeeData["mpin"], 'is_mpin_skipped' => $employeeData['is_mpin_skipped'] ?? 0, 'is_biometric_enabled' => $employeeData['is_biometric_enabled'] ?? 0],200);
|
return $this->respond(['status' => 'success','code' => 200,'data' => "", 'message' => "Mpin - exist" , 'Mpin' =>$employeeData["mpin"], 'is_mpin_skipped' => $employeeData['is_mpin_skipped'] ?? '0', 'is_biometric_enabled' => $employeeData['is_biometric_enabled'] ?? '0'],200);
|
||||||
} else {
|
} else {
|
||||||
$this->myLogger->logme("error", "REST-AUTH-CONTROLLER - checkMpin: Mpin - not found");
|
$this->myLogger->logme("error", "REST-AUTH-CONTROLLER - checkMpin: Mpin - not found");
|
||||||
log_message('error', ' ');
|
log_message('error', ' ');
|
||||||
|
|||||||
@ -9,6 +9,7 @@ use App\Models\SalesActivityModel;
|
|||||||
use App\Models\SalesLeadNoteModel;
|
use App\Models\SalesLeadNoteModel;
|
||||||
use App\Models\SalesTargetModel;
|
use App\Models\SalesTargetModel;
|
||||||
use App\Models\UserModel;
|
use App\Models\UserModel;
|
||||||
|
use App\Models\ClientModel;
|
||||||
use CodeIgniter\HTTP\ResponseInterface;
|
use CodeIgniter\HTTP\ResponseInterface;
|
||||||
use CodeIgniter\API\ResponseTrait;
|
use CodeIgniter\API\ResponseTrait;
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ class SalesController extends BaseController
|
|||||||
protected $noteModel;
|
protected $noteModel;
|
||||||
protected $userModel;
|
protected $userModel;
|
||||||
protected $targetModel;
|
protected $targetModel;
|
||||||
|
protected $clientModel;
|
||||||
|
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
@ -32,6 +34,7 @@ class SalesController extends BaseController
|
|||||||
$this->noteModel = new SalesLeadNoteModel();
|
$this->noteModel = new SalesLeadNoteModel();
|
||||||
$this->userModel = new UserModel();
|
$this->userModel = new UserModel();
|
||||||
$this->targetModel = new SalesTargetModel();
|
$this->targetModel = new SalesTargetModel();
|
||||||
|
$this->clientModel = new ClientModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -125,9 +128,13 @@ class SalesController extends BaseController
|
|||||||
->where('nhance_branch_id', $nhance_branch_id) // same branch
|
->where('nhance_branch_id', $nhance_branch_id) // same branch
|
||||||
->get()->getResultArray();
|
->get()->getResultArray();
|
||||||
|
|
||||||
// Add "(Head)" label to heads so dropdown is clear
|
// Add "(Head) (Admin)" label
|
||||||
foreach ($branch_heads as &$head) {
|
foreach ($branch_heads as &$head) {
|
||||||
$head['first_name'] = $head['first_name'] . ' (Head)';
|
if ($head['role'] == 1) {
|
||||||
|
$head['first_name'] = $head['first_name'] . ' (Admin)';
|
||||||
|
} elseif ($head['role'] == 5) {
|
||||||
|
$head['first_name'] = $head['first_name'] . ' (Head)';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
unset($head);
|
unset($head);
|
||||||
|
|
||||||
@ -155,12 +162,25 @@ class SalesController extends BaseController
|
|||||||
->whereIn('role', [1, 5]) // Sales Head roles
|
->whereIn('role', [1, 5]) // Sales Head roles
|
||||||
->get()->getResultArray();
|
->get()->getResultArray();
|
||||||
|
|
||||||
// Add "(Head)" label to all heads
|
// Add "(Head) (Admin)" label
|
||||||
foreach ($all_heads as &$head) {
|
foreach ($all_heads as &$head) {
|
||||||
$head['first_name'] = $head['first_name'] . ' (Head)';
|
if ($head['role'] == 1) {
|
||||||
|
$head['first_name'] = $head['first_name'] . ' (Admin)';
|
||||||
|
} elseif ($head['role'] == 5) {
|
||||||
|
$head['first_name'] = $head['first_name'] . ' (Head)';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
unset($head);
|
unset($head);
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// QUERY 5: Get ALL clients
|
||||||
|
// ================================================================
|
||||||
|
$all_clients = $db->table('clients')
|
||||||
|
->select('id, client_name,short_name,email,phone')
|
||||||
|
->where('is_active', 1)
|
||||||
|
->get()->getResultArray();
|
||||||
|
$data['$all_clients'] = $all_clients;
|
||||||
|
|
||||||
// ================================================================
|
// ================================================================
|
||||||
// BUILD: sales_manager_with_head = branch heads + branch managers
|
// BUILD: sales_manager_with_head = branch heads + branch managers
|
||||||
// ================================================================
|
// ================================================================
|
||||||
@ -272,6 +292,38 @@ class SalesController extends BaseController
|
|||||||
return $this->fail('Please assign this lead to a user.');
|
return $this->fail('Please assign this lead to a user.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Client logic — only runs if client_id key exists in payload ──
|
||||||
|
if (array_key_exists('client_id', $data)) {
|
||||||
|
|
||||||
|
$clientId = !empty($data['client_id']) ? (int)$data['client_id'] : null;
|
||||||
|
|
||||||
|
$clientData = array_filter([
|
||||||
|
'client_name' => $data['company_name'] ?? null,
|
||||||
|
'short_name' => $data['short_name'] ?? null,
|
||||||
|
'email' => $data['email'] ?? null,
|
||||||
|
'phone' => $data['phone'] ?? null,
|
||||||
|
], fn($v) => $v !== null && $v !== '');
|
||||||
|
|
||||||
|
if ($clientId) {
|
||||||
|
// Existing client — update name/short_name/email/phone if provided
|
||||||
|
if (!empty($clientData)) {
|
||||||
|
$clientData['updated_by'] = $this->getUserId();
|
||||||
|
$this->clientModel->update($clientId, $clientData);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// New client — insert and get ID
|
||||||
|
$clientData['created_by'] = $this->getUserId();
|
||||||
|
$clientData['is_active'] = 1;
|
||||||
|
$this->clientModel->insert($clientData);
|
||||||
|
$clientId = $this->clientModel->getInsertID();
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['client_id'] = $clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Clean up UI-only fields before inserting lead ────────────────
|
||||||
|
unset($data['short_name']);
|
||||||
|
|
||||||
if (!$this->leadModel->insert($data)) {
|
if (!$this->leadModel->insert($data)) {
|
||||||
return $this->fail($this->leadModel->errors());
|
return $this->fail($this->leadModel->errors());
|
||||||
}
|
}
|
||||||
@ -351,6 +403,38 @@ class SalesController extends BaseController
|
|||||||
$data = $this->request->getJSON(true);
|
$data = $this->request->getJSON(true);
|
||||||
$data['updated_by'] = $this->getUserId();
|
$data['updated_by'] = $this->getUserId();
|
||||||
|
|
||||||
|
// ── Client logic — only runs if client_id key exists in payload ──
|
||||||
|
if (array_key_exists('client_id', $data)) {
|
||||||
|
|
||||||
|
$clientId = !empty($data['client_id']) ? (int)$data['client_id'] : null;
|
||||||
|
|
||||||
|
$clientData = array_filter([
|
||||||
|
'client_name' => $data['company_name'] ?? null,
|
||||||
|
'short_name' => $data['short_name'] ?? null,
|
||||||
|
'email' => $data['email'] ?? null,
|
||||||
|
'phone' => $data['phone'] ?? null,
|
||||||
|
], fn($v) => $v !== null && $v !== '');
|
||||||
|
|
||||||
|
if ($clientId) {
|
||||||
|
// Existing client — update if we have data
|
||||||
|
if (!empty($clientData)) {
|
||||||
|
$clientData['updated_by'] = $this->getUserId();
|
||||||
|
$this->clientModel->update($clientId, $clientData);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// New client — insert and get ID
|
||||||
|
$clientData['created_by'] = $this->getUserId();
|
||||||
|
$clientData['is_active'] = 1;
|
||||||
|
$this->clientModel->insert($clientData);
|
||||||
|
$clientId = $this->clientModel->getInsertID();
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['client_id'] = $clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Clean up UI-only fields before inserting lead ────────────────
|
||||||
|
unset($data['short_name']);
|
||||||
|
|
||||||
if (!$this->leadModel->update($id, $data)) {
|
if (!$this->leadModel->update($id, $data)) {
|
||||||
return $this->fail($this->leadModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
|
return $this->fail($this->leadModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
@ -408,6 +492,87 @@ class SalesController extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /sales/searchClients?q=tech
|
||||||
|
*/
|
||||||
|
public function searchClients()
|
||||||
|
{
|
||||||
|
$q = trim($this->request->getGet('q') ?? '');
|
||||||
|
|
||||||
|
if (strlen($q) < 1) {
|
||||||
|
return $this->response
|
||||||
|
->setContentType('application/json')
|
||||||
|
->setBody(json_encode(['data' => []]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = \Config\Database::connect();
|
||||||
|
|
||||||
|
$results = $db->table('clients')
|
||||||
|
->select('id, client_name, short_name, email, phone')
|
||||||
|
->like('client_name', $q)
|
||||||
|
->where('client_type', 1)
|
||||||
|
->where('is_active', 1)
|
||||||
|
->limit(10)
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
|
||||||
|
return $this->response
|
||||||
|
->setContentType('application/json')
|
||||||
|
->setBody(json_encode(['data' => $results]));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* GET /sales/checkDuplicate?table=clients&field=short_name&value=TECH
|
||||||
|
*/
|
||||||
|
public function checkDuplicate()
|
||||||
|
{
|
||||||
|
if ($this->request->getMethod() !== 'get') {
|
||||||
|
return $this->response
|
||||||
|
->setStatusCode(405)
|
||||||
|
->setContentType('application/json')
|
||||||
|
->setBody(json_encode(['exists' => false]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = $this->request->getGet('table');
|
||||||
|
$field = $this->request->getGet('field');
|
||||||
|
$value = trim($this->request->getGet('value') ?? '');
|
||||||
|
$exclude_id = $this->request->getGet('exclude_id');
|
||||||
|
|
||||||
|
// ── Whitelist ─────────────────────────────────────────────
|
||||||
|
$allowed = [
|
||||||
|
'clients' => ['model' => $this->clientModel, 'pk' => 'id'],
|
||||||
|
];
|
||||||
|
|
||||||
|
// ── Allowed fields per table ──────────────────────────────
|
||||||
|
if (!array_key_exists($table, $allowed) || !in_array($field, ['short_name', 'client_name'])) {
|
||||||
|
return $this->response
|
||||||
|
->setStatusCode(400)
|
||||||
|
->setContentType('application/json')
|
||||||
|
->setBody(json_encode(['exists' => false, 'error' => 'Invalid table or field']));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($value)) {
|
||||||
|
return $this->response
|
||||||
|
->setContentType('application/json')
|
||||||
|
->setBody(json_encode(['exists' => false]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$model = $allowed[$table]['model'];
|
||||||
|
$pk = $allowed[$table]['pk'];
|
||||||
|
|
||||||
|
// ── Case-insensitive match ────────────────────────────────
|
||||||
|
$model->where("LOWER({$field})", strtolower($value));
|
||||||
|
|
||||||
|
if (!empty($exclude_id)) {
|
||||||
|
$model->where("{$pk} !=", (int)$exclude_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = $model->countAllResults();
|
||||||
|
|
||||||
|
return $this->response
|
||||||
|
->setContentType('application/json')
|
||||||
|
->setBody(json_encode(['exists' => $count > 0]));
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== CONTACT PERSON APIs ====================
|
// ==================== CONTACT PERSON APIs ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -559,6 +724,7 @@ class SalesController extends BaseController
|
|||||||
'assigned_to' => $this->request->getGet('assigned_to'),
|
'assigned_to' => $this->request->getGet('assigned_to'),
|
||||||
'date_from' => $this->request->getGet('date_from'),
|
'date_from' => $this->request->getGet('date_from'),
|
||||||
'date_to' => $this->request->getGet('date_to'),
|
'date_to' => $this->request->getGet('date_to'),
|
||||||
|
'search' => $this->request->getGet('search'), // ← ADD THIS
|
||||||
];
|
];
|
||||||
|
|
||||||
$result = $this->activityModel->getActivitiesWithFilters($filters, $limit, $offset);
|
$result = $this->activityModel->getActivitiesWithFilters($filters, $limit, $offset);
|
||||||
@ -652,12 +818,16 @@ class SalesController extends BaseController
|
|||||||
$data['additional_assigned_ids'] = json_encode($data['additional_assigned_ids']);
|
$data['additional_assigned_ids'] = json_encode($data['additional_assigned_ids']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->activityModel->insert($data)) {
|
$activityId = $this->activityModel->insert($data);
|
||||||
|
if (!$activityId) {
|
||||||
return $this->fail($this->activityModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
|
return $this->fail($this->activityModel->errors(), ResponseInterface::HTTP_BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$data['activity_id'] = $activityId;
|
||||||
|
|
||||||
// add google calender event
|
// add google calender event
|
||||||
$response = $this->addCalenderEvent($data);
|
$response = $this->addCalenderEvent($data);
|
||||||
|
log_message("error", '[GOOGLE_CALENDER] addCalenderEvent response: ' . json_encode($response));
|
||||||
|
|
||||||
$activityId = $this->activityModel->getInsertID();
|
$activityId = $this->activityModel->getInsertID();
|
||||||
$activity = $this->activityModel->find((int)$activityId);
|
$activity = $this->activityModel->find((int)$activityId);
|
||||||
@ -1073,6 +1243,7 @@ public function dashboard()
|
|||||||
$base = $this->getSalesStaffData();
|
$base = $this->getSalesStaffData();
|
||||||
$salesRole = $base['sales_role'];
|
$salesRole = $base['sales_role'];
|
||||||
$salesManagerIds = $base['sales_manager_ids'];
|
$salesManagerIds = $base['sales_manager_ids'];
|
||||||
|
$salesHeadManagerIds = $base['sales_manager_with_head'];
|
||||||
$userId = get_session_userid();
|
$userId = get_session_userid();
|
||||||
|
|
||||||
// Get branch id
|
// Get branch id
|
||||||
@ -1103,7 +1274,7 @@ public function dashboard()
|
|||||||
|
|
||||||
// Route by role
|
// Route by role
|
||||||
if ($salesRole === 'Sales Head') {
|
if ($salesRole === 'Sales Head') {
|
||||||
$this->branchLevelDashboard($nhanceBranchId, $salesManagerIds, $current_fin_year, $fin_years);
|
$this->branchLevelDashboard($nhanceBranchId, $salesHeadManagerIds, $salesManagerIds, $current_fin_year, $fin_years);
|
||||||
} elseif ($salesRole === 'Sales Manager') {
|
} elseif ($salesRole === 'Sales Manager') {
|
||||||
$this->salesManagerLevelDashboard($userId, $current_fin_year, $fin_years);
|
$this->salesManagerLevelDashboard($userId, $current_fin_year, $fin_years);
|
||||||
}
|
}
|
||||||
@ -1112,17 +1283,20 @@ public function dashboard()
|
|||||||
// ─────────────────────────────────────────────
|
// ─────────────────────────────────────────────
|
||||||
// branchLevelDashboard()
|
// branchLevelDashboard()
|
||||||
// ─────────────────────────────────────────────
|
// ─────────────────────────────────────────────
|
||||||
public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin_year, $fin_years)
|
public function branchLevelDashboard($branchId, $branchwise_all_sales_team_ids, $sales_manager_ids, $current_fin_year, $fin_years)
|
||||||
{
|
{
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$sales_manager_ids = array_values(array_filter(array_map('intval', $sales_manager_ids)));
|
$sales_manager_ids = array_values(array_filter(array_map('intval', $sales_manager_ids)));
|
||||||
|
$branchwise_all_sales_team_ids = array_column($branchwise_all_sales_team_ids, 'id');
|
||||||
|
|
||||||
|
|
||||||
$db = \Config\Database::connect();
|
$db = \Config\Database::connect();
|
||||||
$fyRange = $this->getFYDateRange($current_fin_year);
|
$fyRange = $this->getFYDateRange($current_fin_year);
|
||||||
$fyStart = $fyRange['start'];
|
$fyStart = $fyRange['start'];
|
||||||
$fyEnd = $fyRange['end'];
|
$fyEnd = $fyRange['end'];
|
||||||
|
|
||||||
if (empty($sales_manager_ids)) {
|
if (empty($branchwise_all_sales_team_ids)) {
|
||||||
// ── No team members — return empty dashboard ──
|
// ── No team members — return empty dashboard ──
|
||||||
$data = [
|
$data = [
|
||||||
'total_leads' => 0,
|
'total_leads' => 0,
|
||||||
@ -1146,21 +1320,20 @@ public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin
|
|||||||
|
|
||||||
// 1. Lead count — FY filtered by created_at
|
// 1. Lead count — FY filtered by created_at
|
||||||
$total_leads = $this->leadModel
|
$total_leads = $this->leadModel
|
||||||
->whereIn('assigned_to', $sales_manager_ids)
|
->whereIn('assigned_to', $branchwise_all_sales_team_ids)
|
||||||
->where('created_at >=', $fyStart)
|
->where('created_at >=', $fyStart)
|
||||||
->where('created_at <=', $fyEnd)
|
->where('created_at <=', $fyEnd)
|
||||||
->countAllResults();
|
->countAllResults();
|
||||||
|
|
||||||
// 2. Total activities — FY filtered by scheduled_date
|
// 2. Total activities — FY filtered by scheduled_date
|
||||||
$total_activity = $this->activityModel
|
$total_activity = $this->activityModel
|
||||||
->whereIn('assigned_to', $sales_manager_ids)
|
->whereIn('assigned_to', $branchwise_all_sales_team_ids)
|
||||||
->where('scheduled_date >=', $fyStart)
|
->where('scheduled_date >=', $fyStart)
|
||||||
->where('scheduled_date <=', $fyEnd)
|
->where('scheduled_date <=', $fyEnd)
|
||||||
->countAllResults();
|
->countAllResults();
|
||||||
|
|
||||||
// 3. Completed activities — FY filtered
|
// 3. Completed activities — FY filtered
|
||||||
$total_completed_activity = $this->activityModel
|
$total_completed_activity = $this->activityModel
|
||||||
->whereIn('assigned_to', $sales_manager_ids)
|
->whereIn('assigned_to', $branchwise_all_sales_team_ids)
|
||||||
->where('status', 'completed')
|
->where('status', 'completed')
|
||||||
->where('scheduled_date >=', $fyStart)
|
->where('scheduled_date >=', $fyStart)
|
||||||
->where('scheduled_date <=', $fyEnd)
|
->where('scheduled_date <=', $fyEnd)
|
||||||
@ -1168,7 +1341,7 @@ public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin
|
|||||||
|
|
||||||
// 4. Pending activities — FY filtered
|
// 4. Pending activities — FY filtered
|
||||||
$total_pending_activity = $this->activityModel
|
$total_pending_activity = $this->activityModel
|
||||||
->whereIn('assigned_to', $sales_manager_ids)
|
->whereIn('assigned_to', $branchwise_all_sales_team_ids)
|
||||||
->where('status', 'pending')
|
->where('status', 'pending')
|
||||||
->where('scheduled_date >=', $fyStart)
|
->where('scheduled_date >=', $fyStart)
|
||||||
->where('scheduled_date <=', $fyEnd)
|
->where('scheduled_date <=', $fyEnd)
|
||||||
@ -1187,7 +1360,7 @@ public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin
|
|||||||
AND scheduled_date <= '{$fyEnd}') as done_acts", false)
|
AND scheduled_date <= '{$fyEnd}') as done_acts", false)
|
||||||
->join('roles r', 'r.id = u.role')
|
->join('roles r', 'r.id = u.role')
|
||||||
->where('u.nhance_branch_id', $branchId)
|
->where('u.nhance_branch_id', $branchId)
|
||||||
->whereIn('u.id', $sales_manager_ids)
|
->whereIn('u.id', $branchwise_all_sales_team_ids)
|
||||||
->where('u.is_active', 1)
|
->where('u.is_active', 1)
|
||||||
->get()
|
->get()
|
||||||
->getResultArray();
|
->getResultArray();
|
||||||
@ -1199,7 +1372,7 @@ public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin
|
|||||||
up.first_name AS assigned_to_name, sa.notes')
|
up.first_name AS assigned_to_name, sa.notes')
|
||||||
->join('user_profiles up', 'up.id = sa.assigned_to', 'left')
|
->join('user_profiles up', 'up.id = sa.assigned_to', 'left')
|
||||||
->join('sales_actual_leads sal', 'sal.lead_id = sa.lead_id', 'left')
|
->join('sales_actual_leads sal', 'sal.lead_id = sa.lead_id', 'left')
|
||||||
->whereIn('sa.assigned_to', $sales_manager_ids)
|
->whereIn('sa.assigned_to', $branchwise_all_sales_team_ids)
|
||||||
->where('sa.status', 'pending')
|
->where('sa.status', 'pending')
|
||||||
->where('sa.scheduled_date >=', $fyStart)
|
->where('sa.scheduled_date >=', $fyStart)
|
||||||
->where('sa.scheduled_date <=', $fyEnd)
|
->where('sa.scheduled_date <=', $fyEnd)
|
||||||
@ -1219,7 +1392,7 @@ public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin
|
|||||||
->join('user_profiles up', 'up.id = sal.assigned_to', 'left')
|
->join('user_profiles up', 'up.id = sal.assigned_to', 'left')
|
||||||
->join('sales_activities sa', 'sa.lead_id = sal.lead_id', 'left')
|
->join('sales_activities sa', 'sa.lead_id = sal.lead_id', 'left')
|
||||||
->join('leads l', 'l.actual_lead_id = sal.lead_id', 'left')
|
->join('leads l', 'l.actual_lead_id = sal.lead_id', 'left')
|
||||||
->whereIn('sal.assigned_to', $sales_manager_ids)
|
->whereIn('sal.assigned_to', $branchwise_all_sales_team_ids)
|
||||||
->where('sal.created_at >=', $fyStart)
|
->where('sal.created_at >=', $fyStart)
|
||||||
->where('sal.created_at <=', $fyEnd)
|
->where('sal.created_at <=', $fyEnd)
|
||||||
->groupBy('sal.lead_id, sal.company_name, sal.status, up.first_name')
|
->groupBy('sal.lead_id, sal.company_name, sal.status, up.first_name')
|
||||||
@ -1234,8 +1407,9 @@ public function branchLevelDashboard($branchId, $sales_manager_ids, $current_fin
|
|||||||
$activityBreakdown = $db->table('sales_activities')
|
$activityBreakdown = $db->table('sales_activities')
|
||||||
->select("activity_type,
|
->select("activity_type,
|
||||||
COUNT(*) AS total,
|
COUNT(*) AS total,
|
||||||
|
TRUNCATE(COUNT(*) * 100.0 / {$total_activity_safe}, 2) AS percentage_accuracy,
|
||||||
ROUND(COUNT(*) * 100.0 / {$total_activity_safe}, 0) AS percentage", false)
|
ROUND(COUNT(*) * 100.0 / {$total_activity_safe}, 0) AS percentage", false)
|
||||||
->whereIn('assigned_to', $sales_manager_ids)
|
->whereIn('assigned_to', $branchwise_all_sales_team_ids)
|
||||||
->where('scheduled_date >=', $fyStart)
|
->where('scheduled_date >=', $fyStart)
|
||||||
->where('scheduled_date <=', $fyEnd)
|
->where('scheduled_date <=', $fyEnd)
|
||||||
->groupBy('activity_type')
|
->groupBy('activity_type')
|
||||||
@ -1962,6 +2136,23 @@ public function salesManagerLevelDashboard($userId, $current_fin_year = null, $f
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$activity_data = $this->activityModel->where('activity_id', $input['activity_id'])->first();
|
||||||
|
|
||||||
|
|
||||||
|
$emails[] = $user_data['email'];
|
||||||
|
if (isset($activity_data['additional_assigned_ids']) && !empty($activity_data['additional_assigned_ids'])) {
|
||||||
|
$additional_assigned_ids = json_decode($activity_data['additional_assigned_ids'], true) ?? [];
|
||||||
|
|
||||||
|
foreach ($additional_assigned_ids as $additional_assigned_id) {
|
||||||
|
$additional_user_data = $this->userModel->where('id', $additional_assigned_id)->first();
|
||||||
|
|
||||||
|
if (!empty($additional_user_data['email'])) {
|
||||||
|
$emails[] = $additional_user_data['email'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_message('error', '[GOOGLE_CALENDER] User emails: ' . json_encode($emails));
|
||||||
|
|
||||||
/* ---------------- SUMMARY ---------------- */
|
/* ---------------- SUMMARY ---------------- */
|
||||||
|
|
||||||
$activityType = ucfirst($input['activity_type']);
|
$activityType = ucfirst($input['activity_type']);
|
||||||
@ -1988,7 +2179,7 @@ public function salesManagerLevelDashboard($userId, $current_fin_year = null, $f
|
|||||||
'summary' => $summary,
|
'summary' => $summary,
|
||||||
'meeting_date' => $input['scheduled_date'],
|
'meeting_date' => $input['scheduled_date'],
|
||||||
'description' => $input['notes'] ?? '',
|
'description' => $input['notes'] ?? '',
|
||||||
'emails' => [$user_data['email']],
|
'emails' => $emails,
|
||||||
];
|
];
|
||||||
|
|
||||||
log_message('error', '[GOOGLE_CALENDER] Google Calendar Payload: ' . json_encode($eventData));
|
log_message('error', '[GOOGLE_CALENDER] Google Calendar Payload: ' . json_encode($eventData));
|
||||||
|
|||||||
@ -5,8 +5,11 @@ namespace App\Controllers;
|
|||||||
use App\Controllers\BaseController;
|
use App\Controllers\BaseController;
|
||||||
use App\Models\EmployeePolicyModel;
|
use App\Models\EmployeePolicyModel;
|
||||||
use App\Models\ClientPolicyModel;
|
use App\Models\ClientPolicyModel;
|
||||||
|
use App\Models\TpaApiDataModel;
|
||||||
|
use App\Models\BatchFileModel;
|
||||||
use App\Models\InsurerBranchModel;
|
use App\Models\InsurerBranchModel;
|
||||||
use App\Models\RFQModel;
|
use App\Models\RFQModel;
|
||||||
|
use App\Models\EmployeeModel;
|
||||||
use CodeIgniter\HTTP\ResponseInterface;
|
use CodeIgniter\HTTP\ResponseInterface;
|
||||||
use CodeIgniter\API\ResponseTrait;
|
use CodeIgniter\API\ResponseTrait;
|
||||||
use Dompdf\Dompdf;
|
use Dompdf\Dompdf;
|
||||||
@ -1120,4 +1123,255 @@ class TestingController extends BaseController
|
|||||||
'metabaseUrl' => 'https://nsights.nhanceindia.in',
|
'metabaseUrl' => 'https://nsights.nhanceindia.in',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert sample data into tpa_api_data for testing variance report (Not in NHANCE, Not in TPA, Need to Review).
|
||||||
|
* Uses client_policy (policy_status=1, is_active=1), employee_policies (active), and employees.
|
||||||
|
*
|
||||||
|
* @param int|null $client_policy_id Optional. If not provided, first eligible policy is used.
|
||||||
|
* @return \CodeIgniter\HTTP\ResponseInterface
|
||||||
|
*/
|
||||||
|
public function insertSampleTpaApiData($client_policy_id = null)
|
||||||
|
{
|
||||||
|
$db = \Config\Database::connect();
|
||||||
|
$clientPolicyModel = new ClientPolicyModel();
|
||||||
|
$employeePolicyModel = new EmployeePolicyModel();
|
||||||
|
$employeeModel = new EmployeeModel();
|
||||||
|
$tpaApiDataModel = new TpaApiDataModel();
|
||||||
|
$batchFileModel = new BatchFileModel();
|
||||||
|
|
||||||
|
// 1. Get policies: policy_status = 1, is_active = 1
|
||||||
|
$policyBuilder = $clientPolicyModel
|
||||||
|
->where('policy_status', 1)
|
||||||
|
->where('is_active', 1);
|
||||||
|
if ($client_policy_id !== null && $client_policy_id !== '') {
|
||||||
|
$policyBuilder->where('id', (int) $client_policy_id);
|
||||||
|
}
|
||||||
|
$policies = $policyBuilder->orderBy('id', 'ASC')->findAll();
|
||||||
|
if (empty($policies)) {
|
||||||
|
return $this->respond([
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'No active client policy found (policy_status=1, is_active=1).',
|
||||||
|
'data' => [],
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
$policy = $policies[0];
|
||||||
|
$client_policy_id = (int) $policy['id'];
|
||||||
|
$client_id = (int) $policy['client_id'];
|
||||||
|
$client_branch_id = !empty($policy['client_branch_id']) ? (int) $policy['client_branch_id'] : 0;
|
||||||
|
|
||||||
|
// 2. Get related employees from employee_policies (active) + employees
|
||||||
|
$empPolicies = $db->table('employee_polices ep')
|
||||||
|
->select('ep.id AS emp_policy_id, ep.employee_id, ep.tpa_id, e.emp_code, e.name, e.dob, e.gender, e.relationship')
|
||||||
|
->join('employees e', 'e.id = ep.employee_id')
|
||||||
|
->where('ep.client_policy_id', $client_policy_id)
|
||||||
|
->where('ep.is_active', 1)
|
||||||
|
->whereIn('ep.status', ['active', 'expired'])
|
||||||
|
->where('e.is_active', 1)
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
if (empty($empPolicies)) {
|
||||||
|
return $this->respond([
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'No active employee policies found for this client policy.',
|
||||||
|
'data' => ['client_policy_id' => $client_policy_id],
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Create a test batch file so we have a file_id for tpa_api_data
|
||||||
|
$createdBy = function_exists('get_session_userid') ? get_session_userid() : 1;
|
||||||
|
$batchCode = 'TPA_SAMPLE_' . date('YmdHis') . '_' . bin2hex(random_bytes(4));
|
||||||
|
$batchFileId = $batchFileModel->insert([
|
||||||
|
'client_id' => $client_id,
|
||||||
|
'client_policy_id' => $client_policy_id,
|
||||||
|
'client_branch_id' => $client_branch_id,
|
||||||
|
'batch_code' => $batchCode,
|
||||||
|
'file_name' => 'sample_tpa_data_test_' . date('Y-m-d_His') . '.xlsx',
|
||||||
|
'insurer_or_tpa' => 'tpa',
|
||||||
|
'event_type' => 'api',
|
||||||
|
'actions' => 'fetch',
|
||||||
|
'status' => 'partially success',
|
||||||
|
'count' => 0,
|
||||||
|
'created_by' => $createdBy,
|
||||||
|
'is_active' => 1,
|
||||||
|
]);
|
||||||
|
if (!$batchFileId) {
|
||||||
|
return $this->respond([
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'Failed to create test batch file.',
|
||||||
|
'data' => [],
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
$file_id = (int) $batchFileId;
|
||||||
|
|
||||||
|
$tpaApiDataModel->skipValidation(true);
|
||||||
|
$inserted = ['not_in_nhance' => 0, 'need_to_review' => 0];
|
||||||
|
$toInsert = [];
|
||||||
|
|
||||||
|
// 4. Not in NHANCE: insert TPA records with emp_codes that do NOT exist in NHANCE for this policy
|
||||||
|
$fakeEmpCodes = ['TPA_SAMPLE_NOTINNHANCE_1', 'TPA_SAMPLE_NOTINNHANCE_2'];
|
||||||
|
foreach ($fakeEmpCodes as $i => $empCode) {
|
||||||
|
$toInsert[] = [
|
||||||
|
'file_id' => $file_id,
|
||||||
|
'emp_code' => $empCode,
|
||||||
|
'name' => 'Sample TPA Only ' . ($i + 1),
|
||||||
|
'dob' => '1990-01-' . str_pad((string)(15 + $i), 2, '0', STR_PAD_LEFT),
|
||||||
|
'relation' => 'Self',
|
||||||
|
'gender' => ($i % 2 === 0) ? 'M' : 'F',
|
||||||
|
'self' => 'Sample TPA Only ' . ($i + 1),
|
||||||
|
'tpa_id' => 'TPA' . (1000 + $i),
|
||||||
|
'age' => 32 + $i,
|
||||||
|
'is_active' => 1,
|
||||||
|
'desc' => 'Sample data – Not in NHANCE',
|
||||||
|
'created_by'=> $createdBy,
|
||||||
|
];
|
||||||
|
$inserted['not_in_nhance']++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Need to Review: same employee as in NHANCE but with different name/dob/gender
|
||||||
|
$needReview = array_slice($empPolicies, 0, min(2, count($empPolicies)));
|
||||||
|
foreach ($needReview as $emp) {
|
||||||
|
$dob = $emp['dob'];
|
||||||
|
if (is_string($dob) && preg_match('/^(\d{4})-(\d{2})-(\d{2})/', $dob, $m)) {
|
||||||
|
$altDob = $m[1] . '-' . $m[2] . '-' . str_pad((string)((int)$m[3] + 1), 2, '0', STR_PAD_LEFT);
|
||||||
|
} else {
|
||||||
|
$altDob = '1995-06-15';
|
||||||
|
}
|
||||||
|
$toInsert[] = [
|
||||||
|
'file_id' => $file_id,
|
||||||
|
'emp_code' => $emp['emp_code'],
|
||||||
|
'name' => '[TPA Altered] ' . ($emp['name'] ?? 'Unknown'),
|
||||||
|
'dob' => $altDob,
|
||||||
|
'relation' => $emp['relationship'] ?? 'Self',
|
||||||
|
'gender' => (strtoupper($emp['gender'] ?? 'M') === 'M') ? 'F' : 'M',
|
||||||
|
'self' => $emp['name'] ?? 'Unknown',
|
||||||
|
'tpa_id' => $emp['tpa_id'] ?? ('T' . $emp['employee_id']),
|
||||||
|
'age' => 30,
|
||||||
|
'is_active' => 1,
|
||||||
|
'desc' => 'Sample data – Need to Review (mismatch)',
|
||||||
|
'created_by'=> $createdBy,
|
||||||
|
];
|
||||||
|
$inserted['need_to_review']++;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($toInsert as $row) {
|
||||||
|
$tpaApiDataModel->insert($row);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not in TPA: we do NOT insert those into tpa_api_data; NHANCE already has employees. So any employee
|
||||||
|
// we did not add to tpa_api_data will appear as "Not in TPA". We added only "Need to Review" and
|
||||||
|
// "Not in NHANCE" rows; the rest of NHANCE employees remain without TPA rows => they show as Not in TPA.
|
||||||
|
|
||||||
|
return $this->respond([
|
||||||
|
'status' => true,
|
||||||
|
'message' => 'Sample TPA API data inserted successfully.',
|
||||||
|
'data' => [
|
||||||
|
'file_id' => $file_id,
|
||||||
|
'client_policy_id' => $client_policy_id,
|
||||||
|
'client_id' => $client_id,
|
||||||
|
'batch_code' => $batchCode,
|
||||||
|
'inserted' => $inserted,
|
||||||
|
'not_in_tpa_note' => 'Employees in NHANCE that were not added to TPA data will appear as "Not in TPA" when you run the variance report for this file.',
|
||||||
|
],
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List employee count per client policy.
|
||||||
|
* No input parameters. Checks all client_policy records and counts linked employee_policies per policy.
|
||||||
|
*
|
||||||
|
* @return \CodeIgniter\HTTP\ResponseInterface
|
||||||
|
*/
|
||||||
|
public function listEmployeeCountByClientPolicy()
|
||||||
|
{
|
||||||
|
$db = \Config\Database::connect();
|
||||||
|
$rows = $db->table('client_policy cp')
|
||||||
|
->select('cp.id AS client_policy_id, COUNT(ep.id) AS employee_policy_count', false)
|
||||||
|
->join('employee_polices ep', 'ep.client_policy_id = cp.id', 'left')
|
||||||
|
->groupBy('cp.id')
|
||||||
|
->orderBy('cp.id', 'ASC')
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
$list = array_map(function ($row) {
|
||||||
|
return [
|
||||||
|
'client_policy_id' => (int) $row['client_policy_id'],
|
||||||
|
'employee_policy_count' => (int) $row['employee_policy_count'],
|
||||||
|
];
|
||||||
|
}, $rows);
|
||||||
|
return $this->respond([
|
||||||
|
'status' => true,
|
||||||
|
'message' => 'Employee count per client policy.',
|
||||||
|
'data' => $list,
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Wellness SSO token generation for Medi Assist (MediBuddy).
|
||||||
|
*
|
||||||
|
* This uses the token-based authentication details shared by Medi Assist:
|
||||||
|
* - Cipher: AES-256-CBC
|
||||||
|
* - Padding: PKCS7 (OpenSSL default)
|
||||||
|
* - Login URL: https://login.mediassist.in/SSOLogon.aspx?PartnerCorpId={0}&EncryptedSSO={1}
|
||||||
|
*
|
||||||
|
* Environment variables (recommended):
|
||||||
|
* - MEDIASSIST_WELLNESS_KEY
|
||||||
|
* - MEDIASSIST_WELLNESS_IV
|
||||||
|
* - MEDIASSIST_WELLNESS_PARTNER_CORP_ID
|
||||||
|
* - MEDIASSIST_WELLNESS_LOGIN_URL
|
||||||
|
*
|
||||||
|
* If env values are not present, sensible dummy defaults are used so that
|
||||||
|
* the function can still be exercised.
|
||||||
|
*/
|
||||||
|
public function testMediAssistWellness()
|
||||||
|
{
|
||||||
|
|
||||||
|
// Configuration – prefer environment variables, fall back to demo values
|
||||||
|
$keyString = env('MEDIASSIST_WELLNESS_KEY');
|
||||||
|
$ivString = env('MEDIASSIST_WELLNESS_IV');
|
||||||
|
$loginUrlTemplate = env('MEDIASSIST_WELLNESS_LOGIN_URL' );
|
||||||
|
$partnerCorpId = '15963';
|
||||||
|
$cipher_algorithm = 'AES-256-CBC';
|
||||||
|
|
||||||
|
// Plain SSO JSON payload, as per Medi Assist sample
|
||||||
|
$payload = [
|
||||||
|
'Id' => '15022',
|
||||||
|
'expiryTime' => time() + (10 * 60), // 10 minutes
|
||||||
|
'CPartnerId' => $partnerCorpId,
|
||||||
|
];
|
||||||
|
|
||||||
|
$plainJson = json_encode($payload, JSON_UNESCAPED_SLASHES);
|
||||||
|
|
||||||
|
// Derive a 32‑byte key (AES‑256) and 16‑byte IV from the provided strings
|
||||||
|
// $key = substr(hash('sha256', $keyString, true), 0, 32);
|
||||||
|
// $iv = substr(hash('md5', $ivString, true), 0, 16);
|
||||||
|
|
||||||
|
// Encrypt with AES‑256‑CBC + PKCS7 padding (OpenSSL default)
|
||||||
|
$cipherTextRaw = openssl_encrypt( $plainJson, $cipher_algorithm, $keyString, OPENSSL_RAW_DATA, $ivString);
|
||||||
|
|
||||||
|
|
||||||
|
if ($cipherTextRaw === false) {
|
||||||
|
return $this->respond([
|
||||||
|
'status' => false,
|
||||||
|
'message' => 'Encryption failed while generating Medi Assist wellness token.',
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Base64 encode and URL‑encode for use as EncryptedSSO
|
||||||
|
$encryptedSSO = urlencode(base64_encode($cipherTextRaw));
|
||||||
|
// $encryptedSSO = rtrim(strtr(base64_encode($cipherTextRaw), '+/', '-_'), '=');
|
||||||
|
|
||||||
|
// Build the final login URL
|
||||||
|
$loginUrl = str_replace(['{0}', '{1}'], [$partnerCorpId, $encryptedSSO], $loginUrlTemplate);
|
||||||
|
|
||||||
|
return $this->respond([
|
||||||
|
'status' => true,
|
||||||
|
'message' => 'Medi Assist wellness test URL generated successfully.',
|
||||||
|
'data' => [
|
||||||
|
'loginUrl' => $loginUrl,
|
||||||
|
'encryptedSSO' => $encryptedSSO,
|
||||||
|
'plainPayload' => $payload,
|
||||||
|
],
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -573,6 +573,7 @@ class TicketController extends BaseController
|
|||||||
->join('ticket_claim_status tcs', 'tcs.id = tm.claim_status_id AND tcs.is_active = 1', 'left')
|
->join('ticket_claim_status tcs', 'tcs.id = tm.claim_status_id AND tcs.is_active = 1', 'left')
|
||||||
->join("({$subquery->getCompiledSelect()}) tat_category", 'tm.id = tat_category.ticket_id', 'left')
|
->join("({$subquery->getCompiledSelect()}) tat_category", 'tm.id = tat_category.ticket_id', 'left')
|
||||||
->where('tm.is_active', 1)
|
->where('tm.is_active', 1)
|
||||||
|
->whereNotIn('claim_status_id', [11, 12, 13, 22, 24, 32, 34, 42, 44, 47, 53, 58, 65])
|
||||||
->orderBy('tm.id', 'DESC');
|
->orderBy('tm.id', 'DESC');
|
||||||
|
|
||||||
$data = $query->get()->getResultArray();
|
$data = $query->get()->getResultArray();
|
||||||
@ -628,6 +629,7 @@ class TicketController extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = $db->table('ticket_master tm')
|
$query = $db->table('ticket_master tm')
|
||||||
->select([
|
->select([
|
||||||
'tm.id',
|
'tm.id',
|
||||||
@ -1178,9 +1180,9 @@ class TicketController extends BaseController
|
|||||||
],
|
],
|
||||||
'tpa_no' => [
|
'tpa_no' => [
|
||||||
'label' => 'TPA ID',
|
'label' => 'TPA ID',
|
||||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/]+$/]',
|
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/\-_]+$/]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'regex_match' => 'TPA ID can only contain letters, numbers, and /.'
|
'regex_match' => 'TPA ID can only contain letters, numbers, /, -, and _.'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'emp_mobile' => ['label' => 'Employee Mobile No','rules' => 'required|numeric|exact_length[10]',
|
'emp_mobile' => ['label' => 'Employee Mobile No','rules' => 'required|numeric|exact_length[10]',
|
||||||
@ -1233,9 +1235,9 @@ class TicketController extends BaseController
|
|||||||
],
|
],
|
||||||
'hospital_address' => [
|
'hospital_address' => [
|
||||||
'label' => 'Hospital Address',
|
'label' => 'Hospital Address',
|
||||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\s\-_.,#\/]+$/]',
|
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\s.,#\/_-]+$/]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'regex_match' => 'The {field} contains invalid characters (Allowed: letters, numbers, spaces, dashes, commas, dots, # and /).'
|
'regex_match' => 'The {field} contains invalid characters (Allowed: letters, numbers, spaces, -, _, ., ,, # and /).'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'hospital_state' => [
|
'hospital_state' => [
|
||||||
@ -1280,10 +1282,21 @@ class TicketController extends BaseController
|
|||||||
'rules' => 'permit_empty|numeric',
|
'rules' => 'permit_empty|numeric',
|
||||||
'errors' => ['numeric' => 'Claim Amount must contain only numbers.']
|
'errors' => ['numeric' => 'Claim Amount must contain only numbers.']
|
||||||
],
|
],
|
||||||
'pod_no' => ['label' => 'POD No','rules' => 'permit_empty','errors' => []],
|
'pod_no' => [
|
||||||
|
'label' => 'POD No',
|
||||||
|
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]',
|
||||||
|
'errors' => [
|
||||||
|
'regex_match' => 'The {field} may only contain letters, numbers, spaces, slashes, underscores, and hyphens.',
|
||||||
|
]
|
||||||
|
],
|
||||||
// --- ADDITIONAL / CONDITIONAL FIELDS ---
|
// --- ADDITIONAL / CONDITIONAL FIELDS ---
|
||||||
'claim_number' => ['label' => 'Claim Number','rules' => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'Invalid Claim Number.']],
|
'claim_number' => [
|
||||||
|
'label' => 'Claim Number',
|
||||||
|
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/_-]+$/]',
|
||||||
|
'errors' => [
|
||||||
|
'regex_match' => 'Claim Number only allows letters, numbers, slashes (/), hyphens (-), and underscores (_).'
|
||||||
|
]
|
||||||
|
],
|
||||||
'denial_date' => ['label' => 'Denial Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Denial Date must be dd/mm/yyyy.']],
|
'denial_date' => ['label' => 'Denial Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Denial Date must be dd/mm/yyyy.']],
|
||||||
'approved_amount' => ['label' => 'Approved Amount','rules' => 'permit_empty|numeric',
|
'approved_amount' => ['label' => 'Approved Amount','rules' => 'permit_empty|numeric',
|
||||||
'errors' => ['numeric' => 'Approved amount must be numeric']
|
'errors' => ['numeric' => 'Approved amount must be numeric']
|
||||||
@ -1531,9 +1544,9 @@ class TicketController extends BaseController
|
|||||||
],
|
],
|
||||||
'tpa_no' => [
|
'tpa_no' => [
|
||||||
'label' => 'TPA ID',
|
'label' => 'TPA ID',
|
||||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/]+$/]',
|
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/\-_]+$/]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'regex_match' => 'TPA ID can only contain letters, numbers, and /.'
|
'regex_match' => 'TPA ID can only contain letters, numbers, /, -, and _.'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'emp_mobile' => ['label' => 'Employee Mobile No','rules' => 'required|numeric|exact_length[10]',
|
'emp_mobile' => ['label' => 'Employee Mobile No','rules' => 'required|numeric|exact_length[10]',
|
||||||
@ -1586,9 +1599,9 @@ class TicketController extends BaseController
|
|||||||
],
|
],
|
||||||
'hospital_address' => [
|
'hospital_address' => [
|
||||||
'label' => 'Hospital Address',
|
'label' => 'Hospital Address',
|
||||||
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\s\-_.,#\/]+$/]',
|
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\s.,#\/_-]+$/]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'regex_match' => 'The {field} contains invalid characters (Allowed: letters, numbers, spaces, dashes, commas, dots, # and /).'
|
'regex_match' => 'The {field} contains invalid characters (Allowed: letters, numbers, spaces, -, _, ., ,, # and /).'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'hospital_state' => [
|
'hospital_state' => [
|
||||||
@ -1633,10 +1646,22 @@ class TicketController extends BaseController
|
|||||||
'rules' => 'permit_empty|numeric',
|
'rules' => 'permit_empty|numeric',
|
||||||
'errors' => ['numeric' => 'Claim Amount must contain only numbers.']
|
'errors' => ['numeric' => 'Claim Amount must contain only numbers.']
|
||||||
],
|
],
|
||||||
'pod_no' => ['label' => 'POD No','rules' => 'permit_empty','errors' => []],
|
'pod_no' => [
|
||||||
|
'label' => 'POD No',
|
||||||
|
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]',
|
||||||
|
'errors' => [
|
||||||
|
'regex_match' => 'The {field} may only contain letters, numbers, spaces, slashes, underscores, and hyphens.',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
// --- ADDITIONAL / CONDITIONAL FIELDS ---
|
// --- ADDITIONAL / CONDITIONAL FIELDS ---
|
||||||
'claim_number' => ['label' => 'Claim Number','rules' => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'Invalid Claim Number.']],
|
'claim_number' => [
|
||||||
|
'label' => 'Claim Number',
|
||||||
|
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/_-]+$/]',
|
||||||
|
'errors' => [
|
||||||
|
'regex_match' => 'Claim Number only allows letters, numbers, slashes (/), hyphens (-), and underscores (_).'
|
||||||
|
]
|
||||||
|
],
|
||||||
'denial_date' => ['label' => 'Denial Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Denial Date must be dd/mm/yyyy.']],
|
'denial_date' => ['label' => 'Denial Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Denial Date must be dd/mm/yyyy.']],
|
||||||
'approved_amount' => ['label' => 'Approved Amount','rules' => 'permit_empty|numeric',
|
'approved_amount' => ['label' => 'Approved Amount','rules' => 'permit_empty|numeric',
|
||||||
'errors' => ['numeric' => 'Approved amount must be numeric']
|
'errors' => ['numeric' => 'Approved amount must be numeric']
|
||||||
@ -1777,8 +1802,12 @@ class TicketController extends BaseController
|
|||||||
$ticket_id = $this->request->getPost('ticket_master_id');
|
$ticket_id = $this->request->getPost('ticket_master_id');
|
||||||
$old_ticket_data = $this->ticketMasterModel->where('id', $ticket_id)->where('is_active', 1)->first();
|
$old_ticket_data = $this->ticketMasterModel->where('id', $ticket_id)->where('is_active', 1)->first();
|
||||||
$ticket_data['claim_status_id'] = $this->getLastMatchedStatus($ticket_data, $old_ticket_data);
|
$ticket_data['claim_status_id'] = $this->getLastMatchedStatus($ticket_data, $old_ticket_data);
|
||||||
|
$ticket_data['last_updated_by'] = 'USER';
|
||||||
// print_rr($ticket_data); die;
|
// print_rr($ticket_data); die;
|
||||||
|
|
||||||
|
$this->myLogger->logme('error', "[UPDATE_CLAIM] Ticket Master ID: {data}", ['data' => $ticket_id]);
|
||||||
|
$this->myLogger->logme('error', "[UPDATE_CLAIM] Old Ticket Data: {data}", ['data' => json_encode($old_ticket_data, JSON_PRETTY_PRINT)]);
|
||||||
|
$this->myLogger->logme('error', "[UPDATE_CLAIM] New Ticket Data: {data}", ['data' => json_encode($ticket_data, JSON_PRETTY_PRINT)]);
|
||||||
|
|
||||||
if ($ticket_data) {
|
if ($ticket_data) {
|
||||||
$return_value = $this->ticketMasterModel->where('id', $ticket_id)->set($ticket_data)->update();
|
$return_value = $this->ticketMasterModel->where('id', $ticket_id)->set($ticket_data)->update();
|
||||||
@ -2400,6 +2429,8 @@ class TicketController extends BaseController
|
|||||||
th.old_value,
|
th.old_value,
|
||||||
th.new_value,
|
th.new_value,
|
||||||
th.created_at,
|
th.created_at,
|
||||||
|
th.updated_by,
|
||||||
|
|
||||||
CONCAT_WS(' ', creator.first_name, creator.last_name) AS modified_by,
|
CONCAT_WS(' ', creator.first_name, creator.last_name) AS modified_by,
|
||||||
-- Claim Status
|
-- Claim Status
|
||||||
old_status.claim_status as old_status_value,
|
old_status.claim_status as old_status_value,
|
||||||
@ -2414,7 +2445,7 @@ class TicketController extends BaseController
|
|||||||
new_insured_emp.name as new_insured_id_name,
|
new_insured_emp.name as new_insured_id_name,
|
||||||
|
|
||||||
ticket_master.ticket_type_id
|
ticket_master.ticket_type_id
|
||||||
|
|
||||||
FROM
|
FROM
|
||||||
ticket_history th
|
ticket_history th
|
||||||
|
|
||||||
@ -3543,9 +3574,13 @@ class TicketController extends BaseController
|
|||||||
claim_dump_files.file_name,
|
claim_dump_files.file_name,
|
||||||
claim_dump_files.status,
|
claim_dump_files.status,
|
||||||
claim_dump_files.created_at,
|
claim_dump_files.created_at,
|
||||||
up.first_name as user_name
|
up.first_name as user_name,
|
||||||
|
c.client_name,
|
||||||
|
cp.policy_no
|
||||||
')
|
')
|
||||||
->join('user_profiles as up', 'claim_dump_files.created_by = up.id', 'left')
|
->join('user_profiles as up', 'claim_dump_files.created_by = up.id', 'left')
|
||||||
|
->join('client_policy as cp', 'claim_dump_files.client_policy_id = cp.id', 'left')
|
||||||
|
->join('clients as c', 'cp.client_id = c.id', 'left')
|
||||||
->where('claim_dump_files.is_active', 1)
|
->where('claim_dump_files.is_active', 1)
|
||||||
->orderBy('claim_dump_files.id', 'desc')
|
->orderBy('claim_dump_files.id', 'desc')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|||||||
@ -115,12 +115,13 @@ class UserController extends AdminController
|
|||||||
// ======================
|
// ======================
|
||||||
'emp_code' => [
|
'emp_code' => [
|
||||||
'label' => 'Employee Code',
|
'label' => 'Employee Code',
|
||||||
'rules' => 'required|min_length[3]|max_length[15]|regex_match[/^[a-zA-Z0-9_\-\/]+$/]',
|
'rules' => 'required|min_length[3]|max_length[15]|regex_match[/^[a-zA-Z0-9_\-\/]+$/]|is_unique[user_profiles.emp_code]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Employee Code is required',
|
'required' => 'Employee Code is required',
|
||||||
'min_length' => 'Employee Code must be at least 3 characters',
|
'min_length' => 'Employee Code must be at least 3 characters',
|
||||||
'max_length' => 'Employee Code cannot exceed 15 characters',
|
'max_length' => 'Employee Code cannot exceed 15 characters',
|
||||||
'regex_match' => 'Employee Code can only contain letters, numbers, underscores, hyphens, and forward slashes (/).'
|
'regex_match' => 'Employee Code can only contain letters, numbers, underscores, hyphens, and forward slashes (/).',
|
||||||
|
'is_unique' => 'This Employee Code already exists.'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -141,10 +142,11 @@ class UserController extends AdminController
|
|||||||
// Email
|
// Email
|
||||||
// ======================
|
// ======================
|
||||||
'email' => [
|
'email' => [
|
||||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
|
'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]|is_unique[user_profiles.email]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Email address is required.',
|
'required' => 'Email address is required.',
|
||||||
'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
|
'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).',
|
||||||
|
'is_unique' => 'This Email address already exists.'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -152,10 +154,11 @@ class UserController extends AdminController
|
|||||||
// Mobile
|
// Mobile
|
||||||
// ======================
|
// ======================
|
||||||
'mobile' => [
|
'mobile' => [
|
||||||
'rules' => 'required|regex_match[/^[6-9][0-9]{9}$/]',
|
'rules' => 'required|regex_match[/^[6-9][0-9]{9}$/]|is_unique[user_profiles.mobile]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Mobile number is required',
|
'required' => 'Mobile number is required',
|
||||||
'regex_match' => 'Enter a valid 10-digit mobile number starting with 6, 7, 8, or 9',
|
'regex_match' => 'Enter a valid 10-digit mobile number starting with 6, 7, 8, or 9',
|
||||||
|
'is_unique' => 'This Mobile number already exists.'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -192,14 +195,15 @@ class UserController extends AdminController
|
|||||||
|
|
||||||
$insert = $this->userModel->insert($userData);
|
$insert = $this->userModel->insert($userData);
|
||||||
|
|
||||||
$bookStackData = [
|
// $bookStackData = [
|
||||||
'name' => $userData['first_name'],
|
// 'name' => $userData['first_name'],
|
||||||
'email' => $userData['email'],
|
// 'email' => $userData['email'],
|
||||||
|
|
||||||
];
|
// ];
|
||||||
$this->bookStack->createEditUser($bookStackData);
|
// $this->bookStack->createEditUser($bookStackData);
|
||||||
|
|
||||||
if ($insert) {
|
if ($insert) {
|
||||||
|
|
||||||
$teamData['user_id'] = $insert;
|
$teamData['user_id'] = $insert;
|
||||||
foreach ($teams as $value) {
|
foreach ($teams as $value) {
|
||||||
$teamData['team_id'] = $value;
|
$teamData['team_id'] = $value;
|
||||||
@ -207,42 +211,42 @@ class UserController extends AdminController
|
|||||||
$this->userTeamsModel->insert($teamData);
|
$this->userTeamsModel->insert($teamData);
|
||||||
}
|
}
|
||||||
|
|
||||||
$db = \Config\Database::connect();
|
// $db = \Config\Database::connect();
|
||||||
$tableName = 'hdz_staff';
|
// $tableName = 'hdz_staff';
|
||||||
|
|
||||||
// Set default values
|
// // Set default values
|
||||||
$admin = 0;
|
// $admin = 0;
|
||||||
$acm = 0;
|
// $acm = 0;
|
||||||
$acm_id = null;
|
// $acm_id = null;
|
||||||
|
|
||||||
if ($userData['role'] == 3) {
|
// if ($userData['role'] == 3) {
|
||||||
$admin = 0;
|
// $admin = 0;
|
||||||
$acm = 1;
|
// $acm = 1;
|
||||||
$acm_id = $insert;
|
// $acm_id = $insert;
|
||||||
} else if (in_array($userData['role'], [1, 5])) {
|
// } else if (in_array($userData['role'], [1, 5])) {
|
||||||
$admin = 1;
|
// $admin = 1;
|
||||||
$acm = 0;
|
// $acm = 0;
|
||||||
$acm_id = null;
|
// $acm_id = null;
|
||||||
}
|
// }
|
||||||
|
|
||||||
$password = '12345678'; // Default password
|
// $password = '12345678'; // Default password
|
||||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
// $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
$hdz_staff = [
|
// $hdz_staff = [
|
||||||
'emp_code' => $userData['emp_code'],
|
// 'emp_code' => $userData['emp_code'],
|
||||||
'fullname' => $userData['first_name'],
|
// 'fullname' => $userData['first_name'],
|
||||||
'username' => strtolower($userData['first_name']),
|
// 'username' => strtolower($userData['first_name']),
|
||||||
'email' => $userData['email'],
|
// 'email' => $userData['email'],
|
||||||
'admin' => $admin,
|
// 'admin' => $admin,
|
||||||
'acm' => $acm,
|
// 'acm' => $acm,
|
||||||
'acm_id' => $acm_id,
|
// 'acm_id' => $acm_id,
|
||||||
'registration' => time(),
|
// 'registration' => time(),
|
||||||
'password' => $hashedPassword,
|
// 'password' => $hashedPassword,
|
||||||
'active' => 1,
|
// 'active' => 1,
|
||||||
'department' => 'a:7:{i:0;s:1:"1";i:1;s:1:"2";i:2;s:1:"5";i:3;s:1:"3";i:4;s:1:"9";i:5;s:1:"4";i:6;s:2:"10";}',
|
// 'department' => 'a:7:{i:0;s:1:"1";i:1;s:1:"2";i:2;s:1:"5";i:3;s:1:"3";i:4;s:1:"9";i:5;s:1:"4";i:6;s:2:"10";}',
|
||||||
];
|
// ];
|
||||||
|
|
||||||
$db->table($tableName)->insert($hdz_staff);
|
// $db->table($tableName)->insert($hdz_staff);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,7 +272,10 @@ class UserController extends AdminController
|
|||||||
if (!$this->request->getPost()) {
|
if (!$this->request->getPost()) {
|
||||||
return redirect()->to(base_url('/user/list'));
|
return redirect()->to(base_url('/user/list'));
|
||||||
} else {
|
} else {
|
||||||
// echo ":/ in 163";
|
|
||||||
|
$data = $this->request->getPost();
|
||||||
|
$userData = sanitizeInputArrayAdvanced($data);
|
||||||
|
$id = $userData['PrimaryKey'];
|
||||||
$rules = [
|
$rules = [
|
||||||
|
|
||||||
// ======================
|
// ======================
|
||||||
@ -297,13 +304,13 @@ class UserController extends AdminController
|
|||||||
// Employee Code
|
// Employee Code
|
||||||
// ======================
|
// ======================
|
||||||
'emp_code' => [
|
'emp_code' => [
|
||||||
'label' => 'Employee Code',
|
'rules' => "required|min_length[3]|max_length[15]|regex_match[/^[a-zA-Z0-9_\-\/]+$/]|is_unique[user_profiles.emp_code,id,{$id}]",
|
||||||
'rules' => 'required|min_length[3]|max_length[15]|regex_match[/^[a-zA-Z0-9_\-\/]+$/]',
|
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Employee Code is required',
|
'required' => 'Employee Code is required',
|
||||||
'min_length' => 'Employee Code must be at least 3 characters',
|
'min_length' => 'Employee Code must be at least 3 characters',
|
||||||
'max_length' => 'Employee Code cannot exceed 15 characters',
|
'max_length' => 'Employee Code cannot exceed 15 characters',
|
||||||
'regex_match' => 'Employee Code can only contain letters, numbers, underscores, hyphens, and forward slashes (/).'
|
'regex_match' => 'Employee Code can only contain letters, numbers, underscores, hyphens, and forward slashes (/).',
|
||||||
|
'is_unique' => 'This Employee Code already exists.'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -324,10 +331,11 @@ class UserController extends AdminController
|
|||||||
// Email
|
// Email
|
||||||
// ======================
|
// ======================
|
||||||
'email' => [
|
'email' => [
|
||||||
'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]',
|
'rules' => "required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]|is_unique[user_profiles.email,id,{$id}]",
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Email address is required.',
|
'required' => 'Email address is required.',
|
||||||
'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).'
|
'regex_match' => 'Please enter a valid email format (e.g., name@domain.com).',
|
||||||
|
'is_unique' => 'This Email address already exists.'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -335,10 +343,11 @@ class UserController extends AdminController
|
|||||||
// Mobile
|
// Mobile
|
||||||
// ======================
|
// ======================
|
||||||
'mobile' => [
|
'mobile' => [
|
||||||
'rules' => 'required|regex_match[/^[6-9][0-9]{9}$/]',
|
'rules' => "required|regex_match[/^[6-9][0-9]{9}$/]|is_unique[user_profiles.mobile,id,{$id}]",
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Mobile number is required',
|
'required' => 'Mobile number is required',
|
||||||
'regex_match' => 'Enter a valid 10-digit mobile number starting with 6, 7, 8, or 9',
|
'regex_match' => 'Enter a valid 10-digit mobile number starting with 6, 7, 8, or 9',
|
||||||
|
'is_unique' => 'This Mobile number already exists.'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -384,12 +393,12 @@ class UserController extends AdminController
|
|||||||
$userData['updated_by'] = get_session_userid();
|
$userData['updated_by'] = get_session_userid();
|
||||||
$update = $this->userModel->where('id', $id)->set($userData)->update();
|
$update = $this->userModel->where('id', $id)->set($userData)->update();
|
||||||
|
|
||||||
$bookStackData = [
|
// $bookStackData = [
|
||||||
'name' => $userData['first_name'],
|
// 'name' => $userData['first_name'],
|
||||||
'email' => $userData['email'],
|
// 'email' => $userData['email'],
|
||||||
];
|
// ];
|
||||||
|
|
||||||
$this->bookStack->createEditUser($bookStackData,$existingData);
|
// $this->bookStack->createEditUser($bookStackData,$existingData);
|
||||||
|
|
||||||
|
|
||||||
if ($update) {
|
if ($update) {
|
||||||
@ -404,54 +413,54 @@ class UserController extends AdminController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$db = \Config\Database::connect();
|
// $db = \Config\Database::connect();
|
||||||
$tableName = 'hdz_staff';
|
// $tableName = 'hdz_staff';
|
||||||
|
|
||||||
$hdz_staff = [
|
// $hdz_staff = [
|
||||||
'emp_code' => $userData['emp_code'],
|
// 'emp_code' => $userData['emp_code'],
|
||||||
'fullname' => $userData['first_name'],
|
// 'fullname' => $userData['first_name'],
|
||||||
'username' => strtolower($userData['first_name']),
|
// 'username' => strtolower($userData['first_name']),
|
||||||
'email' => $userData['email'],
|
// 'email' => $userData['email'],
|
||||||
'registration' => time(),
|
// 'registration' => time(),
|
||||||
'active' => 1,
|
// 'active' => 1,
|
||||||
'department' => 'a:7:{i:0;s:1:"1";i:1;s:1:"2";i:2;s:1:"5";i:3;s:1:"3";i:4;s:1:"9";i:5;s:1:"4";i:6;s:2:"10";}',
|
// 'department' => 'a:7:{i:0;s:1:"1";i:1;s:1:"2";i:2;s:1:"5";i:3;s:1:"3";i:4;s:1:"9";i:5;s:1:"4";i:6;s:2:"10";}',
|
||||||
];
|
// ];
|
||||||
|
|
||||||
if ($userData['role'] == 3) {
|
// if ($userData['role'] == 3) {
|
||||||
$hdz_staff['admin'] = 0;
|
// $hdz_staff['admin'] = 0;
|
||||||
$hdz_staff['acm'] = 1;
|
// $hdz_staff['acm'] = 1;
|
||||||
$hdz_staff['acm_id'] = $id;
|
// $hdz_staff['acm_id'] = $id;
|
||||||
} elseif (in_array($userData['role'], [1, 5])) {
|
// } elseif (in_array($userData['role'], [1, 5])) {
|
||||||
$hdz_staff['admin'] = 1;
|
// $hdz_staff['admin'] = 1;
|
||||||
$hdz_staff['acm'] = 0;
|
// $hdz_staff['acm'] = 0;
|
||||||
$hdz_staff['acm_id'] = null;
|
// $hdz_staff['acm_id'] = null;
|
||||||
} else {
|
// } else {
|
||||||
|
|
||||||
return redirect()->to(base_url('/user/list'));
|
// return redirect()->to(base_url('/user/list'));
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Check if staff data exists
|
// // Check if staff data exists
|
||||||
$staffData = $db->table($tableName)
|
// $staffData = $db->table($tableName)
|
||||||
// ->where('emp_code', $userData['emp_code'])
|
// // ->where('emp_code', $userData['emp_code'])
|
||||||
->where('email', $userData['email'])
|
// ->where('email', $userData['email'])
|
||||||
->get()->getResult();
|
// ->get()->getResult();
|
||||||
|
|
||||||
if (!empty($staffData)) {
|
// if (!empty($staffData)) {
|
||||||
|
|
||||||
// Update existing record
|
// // Update existing record
|
||||||
$db->table($tableName)
|
// $db->table($tableName)
|
||||||
// ->where('emp_code', $userData['emp_code'])
|
// // ->where('emp_code', $userData['emp_code'])
|
||||||
->where('email', $userData['email'])
|
// ->where('email', $userData['email'])
|
||||||
->set($hdz_staff)->update();
|
// ->set($hdz_staff)->update();
|
||||||
|
|
||||||
} else {
|
// } else {
|
||||||
|
|
||||||
$password = '12345678'; // Default password
|
// $password = '12345678'; // Default password
|
||||||
$hdz_staff['password'] = password_hash($password, PASSWORD_DEFAULT);
|
// $hdz_staff['password'] = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
// Insert new record
|
// // Insert new record
|
||||||
$db->table($tableName)->insert($hdz_staff);
|
// $db->table($tableName)->insert($hdz_staff);
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,7 +475,29 @@ class UserController extends AdminController
|
|||||||
$emailToDelete = $model->where('id', $id)->first();
|
$emailToDelete = $model->where('id', $id)->first();
|
||||||
$deactive = $model->where('id', $id)->set(['is_active' => 0])->update();
|
$deactive = $model->where('id', $id)->set(['is_active' => 0])->update();
|
||||||
|
|
||||||
$this->bookStack->deleteUser($emailToDelete);
|
// $this->bookStack->deleteUser($emailToDelete);
|
||||||
|
if($deactive)
|
||||||
|
{
|
||||||
|
// $db = \Config\Database::connect();
|
||||||
|
// $tableName = 'hdz_staff';
|
||||||
|
|
||||||
|
// $db->table($tableName)->insert($hdz_staff);
|
||||||
|
|
||||||
|
|
||||||
|
echo json_encode(array("status" => true));
|
||||||
|
}else{
|
||||||
|
echo json_encode(array("status" => false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function activateUser($id = null)
|
||||||
|
{
|
||||||
|
$model = new UserModel();
|
||||||
|
|
||||||
|
$emailToDelete = $model->where('id', $id)->first();
|
||||||
|
$deactive = $model->where('id', $id)->set(['is_active' => 1])->update();
|
||||||
|
|
||||||
|
// $this->bookStack->deleteUser($emailToDelete);
|
||||||
if($deactive)
|
if($deactive)
|
||||||
{
|
{
|
||||||
// $db = \Config\Database::connect();
|
// $db = \Config\Database::connect();
|
||||||
|
|||||||
@ -495,40 +495,52 @@ class VidalApiController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract claim status
|
// Extract claim status
|
||||||
$claimData = $response['data']['data']['claims'][0];
|
// $claimData = $response['data']['data']['claims'][0];
|
||||||
$tpa_claim_no = $claimData['claimNumber'] ?? '';
|
$allClaimData = $response['data']['data']['claims'];
|
||||||
$currentStatus = $claimData['status'] ?? '';
|
|
||||||
$tpa_claim_type = $claimData['claimType'] ?? '';
|
$currentStatus = "";
|
||||||
|
foreach ($allClaimData as $claimData) {
|
||||||
|
|
||||||
|
$tpa_claim_no = $claimData['claimNumber'] ?? '';
|
||||||
|
$currentStatus = $claimData['status'] ?? '';
|
||||||
|
$tpa_claim_type = $claimData['claimType'] ?? '';
|
||||||
|
$doa = !empty($claimData['doa'] ?? null) ? date('Y-m-d', strtotime(str_replace('/', '-', $claimData['doa']))) : null;
|
||||||
|
|
||||||
|
|
||||||
// VALID STATUS LIST
|
|
||||||
$validStatuses = [
|
|
||||||
"In-Progress" => 5,
|
|
||||||
"Required Information" => 4,
|
|
||||||
"Paid" => 11,
|
|
||||||
"Rejected" => 8,
|
|
||||||
"Approved" => 8,
|
|
||||||
];
|
|
||||||
|
|
||||||
$updateArray = [
|
// VALID STATUS LIST
|
||||||
'tpa_claim_status' => $currentStatus,
|
$validStatuses = [
|
||||||
'tpa_claim_id' => $tpa_claim_no,
|
"In-Progress" => 5,
|
||||||
// 'claim_number' => $tpa_claim_no, // already updated
|
"Required Information" => 4,
|
||||||
'updated_at' => date('Y-m-d H:i:s'),
|
"Paid" => 11,
|
||||||
];
|
"Rejected" => 8,
|
||||||
if (isset($validStatuses[$currentStatus])) {
|
"Approved" => 8,
|
||||||
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
];
|
||||||
|
|
||||||
|
$updateArray = [
|
||||||
|
'tpa_claim_status' => $currentStatus,
|
||||||
|
'tpa_claim_id' => $tpa_claim_no,
|
||||||
|
// 'claim_number' => $tpa_claim_no, // already updated
|
||||||
|
'updated_at' => date('Y-m-d H:i:s'),
|
||||||
|
'last_updated_by' => 'API',
|
||||||
|
];
|
||||||
|
if (isset($validStatuses[$currentStatus])) {
|
||||||
|
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
||||||
|
}
|
||||||
|
if (!empty($tpa_claim_type)) {
|
||||||
|
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($ticket['doa'] == $doa || $ticket['claim_number'] == $tpa_claim_no){
|
||||||
|
// UPDATE ticket_master
|
||||||
|
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
||||||
|
|
||||||
|
// LOG UPDATE
|
||||||
|
log_message('error', "VIDAL - Claim Status SUCCESS | Updated ticket ID $claimId with claim status: $currentStatus");
|
||||||
|
}else{
|
||||||
|
log_message('error', "VIDAL - Claim Status NOT UPDATED | TicketID={$claimId} | TicketDOA={$ticket['doa']} | ClaimDOA={$doa} | Status={$currentStatus}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!empty($tpa_claim_type)) {
|
|
||||||
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// UPDATE ticket_master
|
|
||||||
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
|
||||||
|
|
||||||
// LOG UPDATE
|
|
||||||
log_message('error', "VIDAL - Claim Status SUCCESS | Updated ticket ID $claimId with claim status: $currentStatus");
|
|
||||||
|
|
||||||
return ['status' => true,'message' => 'Claim status updated.','updated_status' => $currentStatus,'api_response' => $response];
|
return ['status' => true,'message' => 'Claim status updated.','updated_status' => $currentStatus,'api_response' => $response];
|
||||||
}
|
}
|
||||||
@ -611,44 +623,62 @@ class VidalApiController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract claim status
|
// Extract claim status
|
||||||
$claimData = $response['data']['data']['claims'][0];
|
// $claimData = $response['data']['data']['claims'][0];
|
||||||
$tpa_claim_no = $claimData['claimNumber'] ?? '';
|
$allClaimData = $response['data']['data']['claims'];
|
||||||
$currentStatus = $claimData['status'] ?? '';
|
|
||||||
$tpa_claim_type = $claimData['claimType'] ?? '';
|
foreach ($allClaimData as $claimData) {
|
||||||
|
|
||||||
|
$tpa_claim_no = $claimData['claimNumber'] ?? '';
|
||||||
|
$currentStatus = $claimData['status'] ?? '';
|
||||||
|
$tpa_claim_type = $claimData['claimType'] ?? '';
|
||||||
|
$doa = !empty($claimData['doa'] ?? null) ? date('Y-m-d', strtotime(str_replace('/', '-', $claimData['doa']))) : null;
|
||||||
|
|
||||||
|
|
||||||
// VALID STATUS LIST
|
// VALID STATUS LIST
|
||||||
$validStatuses = [
|
$validStatuses = [
|
||||||
"In-Progress" => 5,
|
"In-Progress" => 5,
|
||||||
"Required Information" => 4,
|
"Required Information" => 4,
|
||||||
"Paid" => 11,
|
"Paid" => 11,
|
||||||
"Rejected" => 8,
|
"Rejected" => 8,
|
||||||
"Approved" => 8,
|
"Approved" => 8,
|
||||||
];
|
];
|
||||||
|
|
||||||
$updateArray = [
|
$updateArray = [
|
||||||
'tpa_claim_status' => $currentStatus,
|
'tpa_claim_status' => $currentStatus,
|
||||||
'tpa_claim_id' => $tpa_claim_no,
|
// 'claim_number' => $tpa_claim_no, // already updated
|
||||||
// 'claim_number' => $tpa_claim_no, // already updated
|
'updated_at' => date('Y-m-d H:i:s'),
|
||||||
'updated_at' => date('Y-m-d H:i:s'),
|
'last_updated_by' => 'API',
|
||||||
];
|
];
|
||||||
if (isset($validStatuses[$currentStatus])) {
|
|
||||||
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
if (isset($validStatuses[$currentStatus])) {
|
||||||
|
$updateArray['claim_status_id'] = $validStatuses[$currentStatus];
|
||||||
|
}
|
||||||
|
if (!empty($tpa_claim_type)) {
|
||||||
|
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
||||||
|
}
|
||||||
|
if (empty($ticket['tpa_claim_id']) || $ticket['tpa_claim_id'] === null) {
|
||||||
|
$updateArray['tpa_claim_id'] = $tpa_claim_no;
|
||||||
|
}
|
||||||
|
if (empty($ticket['claim_number']) || $ticket['claim_number'] === null) {
|
||||||
|
$updateArray['claim_number'] = $tpa_claim_no;
|
||||||
|
}
|
||||||
|
|
||||||
|
// UPDATE ticket_master
|
||||||
|
if($ticket['doa'] == $doa || $ticket['claim_number'] == $tpa_claim_no){
|
||||||
|
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
||||||
|
|
||||||
|
// LOG UPDATE
|
||||||
|
log_message('error', "VIDAL - Claim Status SUCCESS | Updated ticket ID $claimId with claim status: $currentStatus");
|
||||||
|
$status_updated_count ++;
|
||||||
|
}else{
|
||||||
|
log_message('error', "VIDAL - Claim Status NOT UPDATED | TicketID={$claimId} | TicketDOA={$ticket['doa']} | ClaimDOA={$doa} | Status={$currentStatus}");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!empty($tpa_claim_type)) {
|
|
||||||
$updateArray['tpa_claim_type'] = $tpa_claim_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// UPDATE ticket_master
|
|
||||||
$this->db->table('ticket_master')->where('id', $claimId)->update($updateArray);
|
|
||||||
|
|
||||||
// LOG UPDATE
|
|
||||||
log_message('error', "VIDAL - Claim Status SUCCESS | Updated ticket ID $claimId with claim status: $currentStatus");
|
|
||||||
|
|
||||||
$status_updated_count ++;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_message('error', "VIDAL - Claim Status ENDED | Total Tickets={".count($TicketData)."} | Status Updated={$status_updated_count} | Errors={".count($error_data)."}");
|
||||||
|
|
||||||
return $this->response->setJSON([
|
return $this->response->setJSON([
|
||||||
'status' => true,
|
'status' => true,
|
||||||
'message' => 'Claim status updated.',
|
'message' => 'Claim status updated.',
|
||||||
|
|||||||
@ -3072,6 +3072,8 @@ if (!function_exists('validate_mobile_value')) {
|
|||||||
if (!function_exists('validate_positive_number_value')) {
|
if (!function_exists('validate_positive_number_value')) {
|
||||||
function validate_positive_number_value($value)
|
function validate_positive_number_value($value)
|
||||||
{
|
{
|
||||||
|
$value = cleanNumber($value);
|
||||||
|
|
||||||
if ($value === "" || $value === null) {
|
if ($value === "" || $value === null) {
|
||||||
return ['status' => true, 'error' => null];
|
return ['status' => true, 'error' => null];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1382,3 +1382,16 @@ if (! function_exists('add_google_calender_event')) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! function_exists('cleanNumber')) {
|
||||||
|
|
||||||
|
function cleanNumber($value){
|
||||||
|
if (empty($value)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = str_replace(',', '', trim($value));
|
||||||
|
return is_numeric($value) ? (float)$value : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -368,7 +368,8 @@ abstract class BaseTpaClaimImportService
|
|||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
return 61;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -374,7 +374,8 @@ class EmployeePolicyModel extends Model
|
|||||||
public function getEmployeePolicyForEcard($policy_id = 0)
|
public function getEmployeePolicyForEcard($policy_id = 0)
|
||||||
{
|
{
|
||||||
$result = $this->select([
|
$result = $this->select([
|
||||||
'employee_polices.id'
|
'employee_polices.id',
|
||||||
|
'emp.relationship',
|
||||||
])
|
])
|
||||||
->join('employees emp', 'employee_polices.employee_id = emp.id');
|
->join('employees emp', 'employee_polices.employee_id = emp.id');
|
||||||
if ($policy_id !=0 && !empty($policy_id)) {
|
if ($policy_id !=0 && !empty($policy_id)) {
|
||||||
@ -515,11 +516,11 @@ class EmployeePolicyModel extends Model
|
|||||||
";
|
";
|
||||||
|
|
||||||
if($insurer_or_tpa == 'insurer'){
|
if($insurer_or_tpa == 'insurer'){
|
||||||
$sql .= "AND (employee_polices.uhid IS NULL OR employee_polices.uhid <> '')";
|
$sql .= "AND employee_polices.uhid IS NULL";
|
||||||
}
|
}
|
||||||
|
|
||||||
if($insurer_or_tpa == 'tpa'){
|
if($insurer_or_tpa == 'tpa'){
|
||||||
$sql .= "AND (employee_polices.tpa_id IS NULL OR employee_polices.tpa_id <> '')";
|
$sql .= "AND employee_polices.tpa_id IS NULL";
|
||||||
}
|
}
|
||||||
|
|
||||||
$binds = ["datas" => $datas,"event" => $event
|
$binds = ["datas" => $datas,"event" => $event
|
||||||
|
|||||||
@ -3785,7 +3785,7 @@
|
|||||||
$totalBilled[$key] = ($totalBilled[$key] ?? 0) + (float) ($row['billed_amt'] ?? 0);
|
$totalBilled[$key] = ($totalBilled[$key] ?? 0) + (float) ($row['billed_amt'] ?? 0);
|
||||||
|
|
||||||
// Store total_irda_amt once
|
// Store total_irda_amt once
|
||||||
if (!isset($totalIrdaMap[$key]) && ($row['total_irda_amt'] ?? 0) > 0) {
|
if (!isset($totalIrdaMap[$key])) {
|
||||||
$totalIrdaMap[$key] = (float) $row['total_irda_amt'];
|
$totalIrdaMap[$key] = (float) $row['total_irda_amt'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3802,11 +3802,24 @@
|
|||||||
foreach ($result as $row) {
|
foreach ($result as $row) {
|
||||||
$ptId = $row['pt_id'].'-'.$row['insurer_id'];
|
$ptId = $row['pt_id'].'-'.$row['insurer_id'];
|
||||||
if (!isset($ptSeen[$ptId])) {
|
if (!isset($ptSeen[$ptId])) {
|
||||||
|
|
||||||
|
$totalIrdaVal = $totalIrdaMap[$ptId] ?? 0;
|
||||||
|
$totalBilledVal = $totalBilled[$ptId] ?? 0;
|
||||||
|
$addMinus = false;
|
||||||
|
|
||||||
|
if($totalIrdaVal < 0){
|
||||||
|
$totalIrdaVal = abs($totalIrdaVal);
|
||||||
|
$totalBilledVal = abs($totalBilledVal);
|
||||||
|
$addMinus = true;
|
||||||
|
}
|
||||||
|
|
||||||
// First entry → set unbilled amount
|
// First entry → set unbilled amount
|
||||||
$row['unbilled_amount'] = round(
|
$row['unbilled_amount'] = round((float) ($totalIrdaVal - $totalBilledVal),2 );
|
||||||
(float) (($totalIrdaMap[$ptId] ?? 0) - ($totalBilled[$ptId] ?? 0)),
|
|
||||||
2
|
if($addMinus){
|
||||||
);
|
$row['unbilled_amount'] = ($row['unbilled_amount'] * -1);
|
||||||
|
}
|
||||||
|
|
||||||
$ptSeen[$ptId] = true;
|
$ptSeen[$ptId] = true;
|
||||||
} else {
|
} else {
|
||||||
// Other entries → zero
|
// Other entries → zero
|
||||||
|
|||||||
@ -65,13 +65,25 @@ class SalesActivityModel extends Model
|
|||||||
*/
|
*/
|
||||||
public function getActivitiesByLead($leadId, $status = null)
|
public function getActivitiesByLead($leadId, $status = null)
|
||||||
{
|
{
|
||||||
// Convert the INT id to a string, then wrap it in JSON quotes to match ["5", "11"]
|
$subQuery = "(
|
||||||
$subQuery = "(SELECT GROUP_CONCAT(up2.first_name SEPARATOR ', ')
|
SELECT GROUP_CONCAT(up2.first_name SEPARATOR ', ')
|
||||||
FROM user_profiles up2
|
FROM user_profiles up2
|
||||||
WHERE JSON_CONTAINS(sales_activities.additional_assigned_ids, JSON_QUOTE(CAST(up2.id AS CHAR)))
|
WHERE sales_activities.additional_assigned_ids IS NOT NULL
|
||||||
) as additional_assigned_names";
|
AND sales_activities.additional_assigned_ids != ''
|
||||||
|
AND sales_activities.additional_assigned_ids != '[]'
|
||||||
|
AND JSON_VALID(sales_activities.additional_assigned_ids)
|
||||||
|
AND JSON_CONTAINS(
|
||||||
|
sales_activities.additional_assigned_ids,
|
||||||
|
JSON_QUOTE(CAST(up2.id AS CHAR))
|
||||||
|
)
|
||||||
|
) as additional_assigned_names";
|
||||||
|
|
||||||
$builder = $this->select("sales_activities.*, up1.first_name as assigned_to_name, $subQuery")
|
$builder = $this->db->table('sales_activities')
|
||||||
|
->select("
|
||||||
|
sales_activities.*,
|
||||||
|
up1.first_name as assigned_to_name,
|
||||||
|
{$subQuery}
|
||||||
|
")
|
||||||
->join('user_profiles as up1', 'up1.id = sales_activities.assigned_to', 'left')
|
->join('user_profiles as up1', 'up1.id = sales_activities.assigned_to', 'left')
|
||||||
->where('sales_activities.lead_id', $leadId);
|
->where('sales_activities.lead_id', $leadId);
|
||||||
|
|
||||||
@ -79,7 +91,10 @@ class SalesActivityModel extends Model
|
|||||||
$builder->where('sales_activities.status', $status);
|
$builder->where('sales_activities.status', $status);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $builder->orderBy('sales_activities.scheduled_date', 'DESC')->findAll();
|
return $builder
|
||||||
|
->orderBy('sales_activities.scheduled_date', 'DESC')
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -182,10 +197,14 @@ class SalesActivityModel extends Model
|
|||||||
$builder->whereIn('sales_activities.assigned_to', $assignedToIds);
|
$builder->whereIn('sales_activities.assigned_to', $assignedToIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($filters['search'])) {
|
if (!empty($filters['search'])) {
|
||||||
$builder->groupStart()
|
// $builder->groupStart()
|
||||||
|
$this->groupStart()
|
||||||
->like('sales_actual_leads.company_name', $filters['search'])
|
->like('sales_actual_leads.company_name', $filters['search'])
|
||||||
|
->orLike('sales_activities.status', $filters['search'])
|
||||||
|
->orLike('sales_activities.activity_type', $filters['search'])
|
||||||
->orLike('up1.first_name', $filters['search'])
|
->orLike('up1.first_name', $filters['search'])
|
||||||
|
->orLike('up2.first_name', $filters['search'])
|
||||||
->groupEnd();
|
->groupEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,7 @@ class SalesActualLeadModel extends Model
|
|||||||
protected $protectFields = true;
|
protected $protectFields = true;
|
||||||
protected $allowedFields = [
|
protected $allowedFields = [
|
||||||
'company_name',
|
'company_name',
|
||||||
|
'client_id',
|
||||||
'email',
|
'email',
|
||||||
'phone',
|
'phone',
|
||||||
'address',
|
'address',
|
||||||
@ -37,7 +38,7 @@ class SalesActualLeadModel extends Model
|
|||||||
|
|
||||||
// Validation
|
// Validation
|
||||||
protected $validationRules = [
|
protected $validationRules = [
|
||||||
'company_name' => 'required|alpha_space|min_length[2]|max_length[255]',
|
'company_name' => 'required|regex_match[/^[a-zA-Z0-9\s_-]+$/]|min_length[2]|max_length[255]',
|
||||||
'email' => 'required_without[phone]|permit_empty|valid_email|max_length[255]',
|
'email' => 'required_without[phone]|permit_empty|valid_email|max_length[255]',
|
||||||
'phone' => 'required_without[email]|permit_empty|regex_match[/^[0-9+\s]+$/]|min_length[10]|max_length[20]',
|
'phone' => 'required_without[email]|permit_empty|regex_match[/^[0-9+\s]+$/]|min_length[10]|max_length[20]',
|
||||||
'status' => 'in_list[New,Potential,Prospects,Not a Prospects]',
|
'status' => 'in_list[New,Potential,Prospects,Not a Prospects]',
|
||||||
@ -48,7 +49,8 @@ class SalesActualLeadModel extends Model
|
|||||||
protected $validationMessages = [
|
protected $validationMessages = [
|
||||||
'company_name' => [
|
'company_name' => [
|
||||||
'required' => 'Company Name is Missing',
|
'required' => 'Company Name is Missing',
|
||||||
'alpha_space' => 'Company Name must contain only letters and spaces',
|
// 'alpha_space' => 'Company Name must contain only letters and spaces',
|
||||||
|
'regex_match' => 'Company Name can only contain letters, numbers, spaces, hyphens and underscores.',
|
||||||
'min_length' => 'Company Name must be at least 2 characters long',
|
'min_length' => 'Company Name must be at least 2 characters long',
|
||||||
'max_length' => 'Company Name must be at most 255 characters long'
|
'max_length' => 'Company Name must be at most 255 characters long'
|
||||||
],
|
],
|
||||||
@ -59,7 +61,7 @@ class SalesActualLeadModel extends Model
|
|||||||
],
|
],
|
||||||
'phone' => [
|
'phone' => [
|
||||||
'required_without' => 'Either Email or Mobile Number is Needed.',
|
'required_without' => 'Either Email or Mobile Number is Needed.',
|
||||||
'regex_match' => 'Phone number can contain only digits, + and spaces.',
|
'regex_match' => 'Mobile number can contain only digits, + and spaces.',
|
||||||
'min_length' => 'Mobile Number must be at least 10 characters long',
|
'min_length' => 'Mobile Number must be at least 10 characters long',
|
||||||
'max_length' => 'Mobile Number must be at most 20 characters long',
|
'max_length' => 'Mobile Number must be at most 20 characters long',
|
||||||
],
|
],
|
||||||
@ -77,8 +79,9 @@ class SalesActualLeadModel extends Model
|
|||||||
*/
|
*/
|
||||||
public function getLeadWithUser($leadId)
|
public function getLeadWithUser($leadId)
|
||||||
{
|
{
|
||||||
return $this->select('sales_actual_leads.*, user_profiles.first_name as assigned_to_name, user_profiles.email as assigned_to_email')
|
return $this->select('sales_actual_leads.*, clients.short_name, user_profiles.first_name as assigned_to_name, user_profiles.email as assigned_to_email')
|
||||||
->join('user_profiles', 'user_profiles.id = sales_actual_leads.assigned_to', 'left')
|
->join('user_profiles', 'user_profiles.id = sales_actual_leads.assigned_to', 'left')
|
||||||
|
->join('clients', 'clients.id = sales_actual_leads.client_id', 'left')
|
||||||
->where('sales_actual_leads.lead_id', $leadId)
|
->where('sales_actual_leads.lead_id', $leadId)
|
||||||
->first();
|
->first();
|
||||||
}
|
}
|
||||||
@ -88,8 +91,9 @@ class SalesActualLeadModel extends Model
|
|||||||
*/
|
*/
|
||||||
public function getLeadsWithFilters($filters = [], $limit = 10, $offset = 0)
|
public function getLeadsWithFilters($filters = [], $limit = 10, $offset = 0)
|
||||||
{
|
{
|
||||||
$this->select('sales_actual_leads.*, user_profiles.first_name as assigned_to_name, user_profiles.email as assigned_to_email')
|
$this->select('sales_actual_leads.*,clients.short_name, user_profiles.first_name as assigned_to_name, user_profiles.email as assigned_to_email')
|
||||||
->join('user_profiles', 'user_profiles.id = sales_actual_leads.assigned_to', 'left');
|
->join('user_profiles', 'user_profiles.id = sales_actual_leads.assigned_to', 'left')
|
||||||
|
->join('clients', 'clients.id = sales_actual_leads.client_id', 'left');
|
||||||
|
|
||||||
if (!empty($filters['status'])) {
|
if (!empty($filters['status'])) {
|
||||||
$this->where('sales_actual_leads.status', $filters['status']);
|
$this->where('sales_actual_leads.status', $filters['status']);
|
||||||
|
|||||||
@ -94,6 +94,7 @@ class TicketMasterModel extends Model
|
|||||||
'tpa_claim_type',
|
'tpa_claim_type',
|
||||||
'tpa_ailments',
|
'tpa_ailments',
|
||||||
'claim_dump_ref_id',
|
'claim_dump_ref_id',
|
||||||
|
'last_updated_by',
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,9 @@ class TpaApiDataModel extends Model
|
|||||||
'age',
|
'age',
|
||||||
'is_active',
|
'is_active',
|
||||||
'desc',
|
'desc',
|
||||||
'created_by'
|
'created_by',
|
||||||
|
'si',
|
||||||
|
'doj'
|
||||||
];
|
];
|
||||||
|
|
||||||
// protected $useTimestamps = true;
|
// protected $useTimestamps = true;
|
||||||
|
|||||||
@ -388,8 +388,18 @@ table.dataTable tbody td {
|
|||||||
<div class="btn-group dropdown" style="position: relative !important;left:0px !important;top:0px !important;">
|
<div class="btn-group dropdown" style="position: relative !important;left:0px !important;top:0px !important;">
|
||||||
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
|
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
|
||||||
<div class="dropdown-menu dropdown-menu-right" style="cursor: pointer;">
|
<div class="dropdown-menu dropdown-menu-right" style="cursor: pointer;">
|
||||||
<a data-toggle="modal" data-target="#con-close-modal" class="dropdown-item btnEdit" data-id="<?php echo $row->id; ?>"><i data-id="<?php echo $row->id; ?>" class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle btnEdit"></i>Edit</a>
|
<a class="dropdown-item btnEdit" data-id="<?php echo $row->id; ?>"><i data-id="<?php echo $row->id; ?>" class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle btnEdit"></i>Edit</a>
|
||||||
<a class="dropdown-item btnDelete" data-id="<?php echo $row->id; ?>"><i data-id="<?php echo $row->id; ?>" class="mdi mdi-delete mr-2 text-muted font-18 vertical-middle btnDelete"></i>Delete</a>
|
<?php if (in_array(get_role_id(), [1, 5])): ?>
|
||||||
|
<?php if ($row->is_active == 1): ?>
|
||||||
|
<a class="dropdown-item btnDelete" data-id="<?php echo $row->id; ?>">
|
||||||
|
<i class="mdi mdi-delete mr-2 text-muted font-18 vertical-middle"></i>Delete
|
||||||
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a class="dropdown-item btnActivate" data-id="<?php echo $row->id; ?>">
|
||||||
|
<i class="mdi mdi-account-check mr-2 text-muted font-18 vertical-middle"></i>Activate
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -493,7 +503,7 @@ table.dataTable tbody td {
|
|||||||
<select class="form-control" id="nhance_branch_id" name="nhance_branch_id" required>
|
<select class="form-control" id="nhance_branch_id" name="nhance_branch_id" required>
|
||||||
<option value="">Select Nhance Branch</option>
|
<option value="">Select Nhance Branch</option>
|
||||||
<?php foreach($NHanceBranchData as $value) { ?>
|
<?php foreach($NHanceBranchData as $value) { ?>
|
||||||
<option value="<?= $value['id'] ?>"><?= $value['branch_name'] ?></option>
|
<option value="<?= $value['id'] ?>" <?= old('nhance_branch_id') == $value['id'] ? 'selected' : '' ?>><?= $value['branch_name'] ?></option>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -502,7 +512,7 @@ table.dataTable tbody td {
|
|||||||
<select class="form-control" id="rm_id" name="rm_id" required>
|
<select class="form-control" id="rm_id" name="rm_id" required>
|
||||||
<option value="">Select Reporting Manager</option>
|
<option value="">Select Reporting Manager</option>
|
||||||
<?php foreach($user_data as $value) { ?>
|
<?php foreach($user_data as $value) { ?>
|
||||||
<option value="<?= $value['id'] ?>"><?= $value['first_name'] ?></option>
|
<option value="<?= $value['id'] ?>" <?= old('rm_id') == $value['id'] ? 'selected' : '' ?>><?= $value['first_name'] ?></option>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -511,11 +521,11 @@ table.dataTable tbody td {
|
|||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label for="emp_code">Employee Code<span class="text-danger">*</span></label>
|
<label for="emp_code">Employee Code<span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="emp_code" placeholder="Enter Code" name="emp_code" onchange="validateInput(this, 'user_profiles', 'emp_code', 'btnSubmit')" required>
|
<input type="text" class="form-control" id="emp_code" placeholder="Enter Code" name="emp_code" onchange="validateInput(this, 'user_profiles', 'emp_code', 'btnSubmit')" required value="<?= old('emp_code') ?>">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label for="first_name">Name<span class="text-danger">*</span></label>
|
<label for="first_name">Name<span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="first_name" placeholder="Enter Name" name="first_name" required>
|
<input type="text" class="form-control" id="first_name" placeholder="Enter Name" name="first_name" required value="<?= old('first_name') ?>">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -523,19 +533,19 @@ table.dataTable tbody td {
|
|||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label for="email">Email<span class="text-danger">*</span></label>
|
<label for="email">Email<span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="email" placeholder="Enter Email" name="email" data-parsley-trigger="change" data-parsley-type="email" onchange="validateInput(this, 'user_profiles', 'email', 'btnSubmit')" required>
|
<input type="text" class="form-control" id="email" placeholder="Enter Email" name="email" data-parsley-trigger="change" data-parsley-type="email" onchange="validateInput(this, 'user_profiles', 'email', 'btnSubmit')" required value="<?= old('email') ?>">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label for="mobile">Mobile Number<span class="text-danger">*</span></label>
|
<label for="mobile">Mobile Number<span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="mobile" placeholder="Enter Mobile Number ( starting with 6, 7, 8, or 9 only. )" name="mobile" onkeypress = "return onlyNumbers(event)" maxlength="10" minlength="10" pattern="^[6-9]\d{9}$" data-parsley-type-message="Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9." data-parsley-required-message="Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9." required>
|
<input type="text" class="form-control" id="mobile" placeholder="Enter Mobile Number ( starting with 6, 7, 8, or 9 only. )" name="mobile" onkeypress="return onlyNumbers(event)" onchange="validateInput(this, 'user_profiles', 'mobile', 'btnSubmit')" maxlength="10" minlength="10" pattern="^[6-9]\d{9}$" data-parsley-type-message="Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9." data-parsley-required-message="Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9." required value="<?= old('mobile') ?>">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label for="emp_code">User Role<span class="text-danger">*</span>
|
<label for="role">User Role<span class="text-danger">*</span>
|
||||||
<span class="tooltip-trigger mdi mdi-information-outline" id="roleAccessMatrixTrigger"></span>
|
<span class="tooltip-trigger mdi mdi-information-outline" id="roleAccessMatrixTrigger"></span>
|
||||||
</label>
|
</label>
|
||||||
<select class="form-control" id="role" name="role" required>
|
<select class="form-control" id="role" name="role" required>
|
||||||
@ -557,7 +567,7 @@ table.dataTable tbody td {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label for="profile">User Team<span class="text-danger">*</span>
|
<label for="team">User Team<span class="text-danger">*</span>
|
||||||
<span class="tooltip-trigger mdi mdi-information-outline" id="accessMatrixTrigger"></span>
|
<span class="tooltip-trigger mdi mdi-information-outline" id="accessMatrixTrigger"></span>
|
||||||
</label>
|
</label>
|
||||||
<select hidden class="form-control" id="team" name="team[]" multiple required>
|
<select hidden class="form-control" id="team" name="team[]" multiple required>
|
||||||
@ -891,6 +901,21 @@ table.dataTable tbody td {
|
|||||||
<?php foreach (session()->getFlashdata('errors') as $error): ?>
|
<?php foreach (session()->getFlashdata('errors') as $error): ?>
|
||||||
toastr.error("<?= addslashes($error) ?>", "Validation Error");
|
toastr.error("<?= addslashes($error) ?>", "Validation Error");
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
var errorModal = new bootstrap.Modal(document.getElementById('con-close-modal'));
|
||||||
|
errorModal.show();
|
||||||
|
|
||||||
|
<?php if(old('PrimaryKey')): ?>
|
||||||
|
$('#UserForm').attr('action', '<?= base_url('/user/edit') ?>');
|
||||||
|
$('#UserId').val('<?= old('PrimaryKey') ?>');
|
||||||
|
$('#btnSubmit').html('Update');
|
||||||
|
$('.modal-title').text('Update User');
|
||||||
|
$('#email').attr('data-original', '<?= old('email') ?>');
|
||||||
|
$('#mobile').attr('data-original', '<?= old('mobile') ?>');
|
||||||
|
$('#emp_code').attr('data-original', '<?= old('emp_code') ?>');
|
||||||
|
<?php else: ?>
|
||||||
|
$('#UserForm').attr('action', '<?= base_url('/user/create') ?>');
|
||||||
|
<?php endif; ?>
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@ -1086,10 +1111,11 @@ table.dataTable tbody td {
|
|||||||
$('.close').click(function(){
|
$('.close').click(function(){
|
||||||
$('#UserId').val('');
|
$('#UserId').val('');
|
||||||
$('#first_name').val('');
|
$('#first_name').val('');
|
||||||
$('#email').val('');
|
$('#email').val('').removeAttr('data-original');
|
||||||
$('#mobile').val('');
|
$('#mobile').val('').removeAttr('data-original');
|
||||||
$('#emp_code').val('');
|
$('#emp_code').val('').removeAttr('data-original');
|
||||||
$('#role').val('')
|
$('#role').val('')
|
||||||
|
$('#btnSubmit').prop('disabled', false);
|
||||||
})
|
})
|
||||||
|
|
||||||
// $('body').on('click', '#btnUsers', function () {
|
// $('body').on('click', '#btnUsers', function () {
|
||||||
@ -1103,83 +1129,74 @@ table.dataTable tbody td {
|
|||||||
// $(this).addClass('active');
|
// $(this).addClass('active');
|
||||||
// loadPartner();
|
// loadPartner();
|
||||||
// });
|
// });
|
||||||
|
$('body').on('click', '.btnEdit', function () {
|
||||||
|
|
||||||
$('body').on('click', '.btnEdit', function () {
|
$('#UserForm').parsley().reset();
|
||||||
var user_id = $(this).attr('data-id');
|
|
||||||
$.ajax({
|
|
||||||
url: '<?php echo base_url('user/getuser/');?>'+user_id,
|
|
||||||
type: "GET",
|
|
||||||
dataType: 'json',
|
|
||||||
success: function (res) {
|
|
||||||
console.log(":():",res);
|
|
||||||
console.log(":():",res.data.nhance_branch_id);
|
|
||||||
$('#updateModal').modal('show');
|
|
||||||
$('#role').val('')
|
|
||||||
$('#UserForm').attr('action', '<?php echo base_url('user/edit');?>');
|
|
||||||
$('#UserId').val(res.data.id);
|
|
||||||
$('#first_name').val(res.data.first_name);
|
|
||||||
$('#last_name').val(res.data.last_name);
|
|
||||||
$('#email').val(res.data.email);
|
|
||||||
$('#mobile').val(res.data.mobile);
|
|
||||||
$('#emp_code').val(res.data.emp_code);
|
|
||||||
$('#role option[value="' + res.data.role + '"]').prop('selected', true);
|
|
||||||
$('#rm_id').val(res.data.rm_id).select2(); // ✅ correct trigger
|
|
||||||
if (res.data.nhance_branch_id !== null &&
|
|
||||||
res.data.nhance_branch_id !== "" &&
|
|
||||||
res.data.nhance_branch_id !== 0) {
|
|
||||||
$('#nhance_branch_id')
|
|
||||||
.val(res.data.nhance_branch_id)
|
|
||||||
.trigger('change.select2');
|
|
||||||
} else {
|
|
||||||
$('#nhance_branch_id')
|
|
||||||
.val(null) // ✅ MUST be null
|
|
||||||
.trigger('change.select2'); // ✅ correct trigger
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#btnSubmit').html('Update');
|
var user_id = $(this).attr('data-id');
|
||||||
|
|
||||||
$.each(res.userTeamData, function(index, item) {
|
$.ajax({
|
||||||
$('#team option[value="' + item.team_id + '"]').prop('selected', true);
|
url: '<?php echo base_url('user/getuser/');?>' + user_id,
|
||||||
$('#team').multiselect('refresh');
|
type: "GET",
|
||||||
});
|
dataType: 'json',
|
||||||
|
success: function (res) {
|
||||||
|
// Set form action to edit
|
||||||
|
$('#UserForm').attr('action', '<?php echo base_url('user/edit');?>');
|
||||||
|
$('#UserId').val(res.data.id);
|
||||||
|
$('#first_name').val(res.data.first_name);
|
||||||
|
$('#email').val(res.data.email).attr('data-original', res.data.email);
|
||||||
|
$('#mobile').val(res.data.mobile).attr('data-original', res.data.mobile);
|
||||||
|
$('#emp_code').val(res.data.emp_code).attr('data-original', res.data.emp_code);
|
||||||
|
$('#role').val(res.data.role).trigger('change');
|
||||||
|
$('#rm_id').val(res.data.rm_id).trigger('change.select2');
|
||||||
|
|
||||||
},
|
if (res.data.nhance_branch_id) {
|
||||||
error: function (xhr, status, error) {
|
$('#nhance_branch_id').val(res.data.nhance_branch_id).trigger('change.select2');
|
||||||
console.error("Error Details:");
|
} else {
|
||||||
console.error("Status Code:", xhr.status);
|
$('#nhance_branch_id').val(null).trigger('change.select2');
|
||||||
console.error("Status Text:", xhr.statusText);
|
|
||||||
console.error("Response Text:", xhr.responseText);
|
|
||||||
console.error("Ready State:", xhr.readyState);
|
|
||||||
console.error("Response Headers:", xhr.getAllResponseHeaders());
|
|
||||||
console.error("Error Thrown:", error);
|
|
||||||
console.error("Status:", status);
|
|
||||||
if (xhr.status === 400) {
|
|
||||||
let response = JSON.parse(xhr.responseText);
|
|
||||||
let errorMessages = "";
|
|
||||||
let seenMessages = []; // Array to store unique messages
|
|
||||||
if (response.errors) {
|
|
||||||
$.each(response.errors, function (field, message) {
|
|
||||||
if (!seenMessages.includes(message)) {
|
|
||||||
errorMessages += `• ${message}<br>`;
|
|
||||||
seenMessages.push(message); // Mark this message as "seen"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
toastr.error(errorMessages, 'Validation Error', { "allowHtml": true });
|
|
||||||
} else {
|
|
||||||
toastr.warning(response.message || 'Validation failed', 'Warning');
|
|
||||||
}
|
|
||||||
} else if (xhr.status === 403) {
|
|
||||||
let response = JSON.parse(xhr.responseText);
|
|
||||||
toastr.error(response.message, 'Security Policy');
|
|
||||||
} else if (xhr.status === 500) {
|
|
||||||
toastr.error('Something went wrong . Please try again later.', 'Server Error');
|
|
||||||
} else {
|
|
||||||
toastr.error('An unexpected error occurred. Please try again later.', 'Error');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
// Reset team first, then set selected
|
||||||
|
$('#team').multiselect('deselectAll', false);
|
||||||
|
$.each(res.userTeamData, function(index, item) {
|
||||||
|
$('#team option[value="' + item.team_id + '"]').prop('selected', true);
|
||||||
|
});
|
||||||
|
$('#team').multiselect('refresh');
|
||||||
|
|
||||||
|
$('.modal-title').text('Update User');
|
||||||
|
$('#btnSubmit').html('Update');
|
||||||
|
|
||||||
|
// ✅ Open modal AFTER data is loaded
|
||||||
|
var myModal = new bootstrap.Modal(document.getElementById('con-close-modal'));
|
||||||
|
myModal.show();
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
if (xhr.status === 400) {
|
||||||
|
let response = JSON.parse(xhr.responseText);
|
||||||
|
let errorMessages = "";
|
||||||
|
let seenMessages = [];
|
||||||
|
if (response.errors) {
|
||||||
|
$.each(response.errors, function (field, message) {
|
||||||
|
if (!seenMessages.includes(message)) {
|
||||||
|
errorMessages += `• ${message}<br>`;
|
||||||
|
seenMessages.push(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toastr.error(errorMessages, 'Validation Error', { "allowHtml": true });
|
||||||
|
} else {
|
||||||
|
toastr.warning(response.message || 'Validation failed', 'Warning');
|
||||||
|
}
|
||||||
|
} else if (xhr.status === 403) {
|
||||||
|
let response = JSON.parse(xhr.responseText);
|
||||||
|
toastr.error(response.message, 'Security Policy');
|
||||||
|
} else if (xhr.status === 500) {
|
||||||
|
toastr.error('Something went wrong. Please try again later.', 'Server Error');
|
||||||
|
} else {
|
||||||
|
toastr.error('An unexpected error occurred.', 'Error');
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$('body').on('click', '.btnDelete', function () {
|
$('body').on('click', '.btnDelete', function () {
|
||||||
|
|
||||||
@ -1201,7 +1218,29 @@ table.dataTable tbody td {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('body').on('click', '.btnActivate', function () {
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: "Are you sure?",
|
||||||
|
text: "You need to active this user",
|
||||||
|
icon: "info",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
confirmButtonText: "Yes",
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
|
||||||
|
var student_id = $(this).attr('data-id');
|
||||||
|
$.get('<?php echo base_url('user/activateUser/');?>'+student_id, function (data) {
|
||||||
|
console.log(data);
|
||||||
|
toastr.success('User actived successfully', 'Success');
|
||||||
|
window.location.reload()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$('body').on('click', '.btnPartnerEdit', function () {
|
$('body').on('click', '.btnPartnerEdit', function () {
|
||||||
let user = JSON.parse($(this).attr('data-obj')); // ✅ convert back to object
|
let user = JSON.parse($(this).attr('data-obj')); // ✅ convert back to object
|
||||||
@ -1296,13 +1335,96 @@ table.dataTable tbody td {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var form = document.getElementById("UserForm");
|
var form = document.getElementById("UserForm");
|
||||||
|
|
||||||
// Add submit event listener to the form
|
form.addEventListener("submit", function(event) {
|
||||||
form.addEventListener("submit", function(event) {
|
event.preventDefault();
|
||||||
// Disable the submit button to avoid multiple submissions
|
|
||||||
// document.getElementById("btnSubmit").disabled = true;
|
var btn = document.getElementById('btnSubmit');
|
||||||
|
var currentUserId = $('#UserId').val(); // capture BEFORE ajax
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.innerText = "Saving...";
|
||||||
|
|
||||||
|
var actionUrl = $('#UserForm').attr('action');
|
||||||
|
var formData = new FormData(form);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: actionUrl,
|
||||||
|
type: "POST",
|
||||||
|
data: formData,
|
||||||
|
processData: false,
|
||||||
|
contentType: false,
|
||||||
|
success: function(responseText) {
|
||||||
|
|
||||||
|
// Parse returned HTML and look for toastr.error calls (validation errors injected by CI flashdata)
|
||||||
|
var tempDiv = document.createElement('div');
|
||||||
|
tempDiv.innerHTML = responseText;
|
||||||
|
|
||||||
|
var errorMessages = [];
|
||||||
|
tempDiv.querySelectorAll('script').forEach(function(script) {
|
||||||
|
var content = script.innerText || script.textContent;
|
||||||
|
var matches = content.match(/toastr\.error\("([^"]+)"/g);
|
||||||
|
if (matches) {
|
||||||
|
matches.forEach(function(m) {
|
||||||
|
var msg = m.replace(/toastr\.error\("/, '').replace(/"$/, '');
|
||||||
|
if (!errorMessages.includes(msg)) {
|
||||||
|
errorMessages.push(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (errorMessages.length > 0) {
|
||||||
|
// ❌ VALIDATION FAILED — keep modal OPEN, show all errors in one toast
|
||||||
|
var errorList = '<ul style="margin:4px 0 0 0; padding-left:18px; text-align:left;">';
|
||||||
|
errorMessages.forEach(function(msg) {
|
||||||
|
errorList += '<li>' + msg + '</li>';
|
||||||
|
});
|
||||||
|
errorList += '</ul>';
|
||||||
|
|
||||||
|
toastr.error(errorList, 'Validation Errors', {
|
||||||
|
"allowHtml": true,
|
||||||
|
"timeOut": 8000,
|
||||||
|
"closeButton": true
|
||||||
|
});
|
||||||
|
|
||||||
|
// ✅ Modal stays OPEN — do NOT hide it
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// ✅ SUCCESS
|
||||||
|
toastr.success('User saved successfully!', 'Success');
|
||||||
|
|
||||||
|
// ✅ Bootstrap 4 way to close modal
|
||||||
|
$('#con-close-modal').modal('hide');
|
||||||
|
|
||||||
|
// Full reset
|
||||||
|
form.reset();
|
||||||
|
$('#UserForm').parsley().reset();
|
||||||
|
$('#UserId').val('');
|
||||||
|
$('#nhance_branch_id').val(null).trigger('change.select2');
|
||||||
|
$('#rm_id').val(null).trigger('change.select2');
|
||||||
|
$('#role').val('').trigger('change');
|
||||||
|
$('#team').multiselect('deselectAll', false);
|
||||||
|
$('#team').multiselect('refresh');
|
||||||
|
$('#email').removeAttr('data-original');
|
||||||
|
$('#mobile').removeAttr('data-original');
|
||||||
|
$('#emp_code').removeAttr('data-original');
|
||||||
|
$('.modal-title').text('Add User');
|
||||||
|
$('#btnSubmit').text('Submit').prop('disabled', false);
|
||||||
|
window.location.reload();
|
||||||
|
|
||||||
|
loadUser();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
|
toastr.error('An unexpected error occurred. Please try again.', 'Error');
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.innerText = currentUserId ? 'Update' : 'Submit';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
function submitPartner(event) {
|
function submitPartner(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -1516,7 +1638,7 @@ table.dataTable tbody td {
|
|||||||
<i class="mdi mdi-dots-horizontal"></i>
|
<i class="mdi mdi-dots-horizontal"></i>
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
<a data-toggle="modal" data-target="#con-close-modal" class="dropdown-item btnEdit" data-id="${row.id}">
|
<a class="dropdown-item btnEdit" data-id="${row.id}" style="cursor:pointer;">
|
||||||
<i data-id="${row.id}" class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle btnEdit"></i>Edit
|
<i data-id="${row.id}" class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle btnEdit"></i>Edit
|
||||||
</a>
|
</a>
|
||||||
<a class="dropdown-item btnDelete" data-id="${row.id}">
|
<a class="dropdown-item btnDelete" data-id="${row.id}">
|
||||||
@ -1962,22 +2084,30 @@ table.dataTable tbody td {
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).on('click', '#btnAdd', function() {
|
$(document).on('click', '#btnAdd', function() {
|
||||||
// Reset the form
|
|
||||||
$('#UserForm')[0].reset();
|
$('#UserForm')[0].reset(); // 1️⃣ reset form values first
|
||||||
$('#UserId').val('');
|
$('#UserForm').parsley().reset(); // 2️⃣ then reset parsley validation
|
||||||
|
|
||||||
// Specifically reset Select2 if you are using it
|
$('#btnSubmit').prop('disabled', false); // 3️⃣ re-enable submit button
|
||||||
$('#nhance_branch_id').val('').trigger('change');
|
|
||||||
$('#rm_id').val('').trigger('change');
|
$('#nhance_branch_id').val(null).trigger('change.select2');
|
||||||
|
$('#rm_id').val(null).trigger('change.select2');
|
||||||
// Update Modal Title and Action
|
|
||||||
$('.modal-title').text('Add User');
|
$('#team').multiselect('deselectAll', false);
|
||||||
$('#btnSubmit').text('Submit');
|
$('#team').multiselect('refresh');
|
||||||
$('#UserForm').attr('action', '<?php echo base_url('/user/create');?>');
|
|
||||||
|
// remove data-original attributes
|
||||||
var myModal = new bootstrap.Modal(document.getElementById('con-close-modal'));
|
$('#email').removeAttr('data-original');
|
||||||
myModal.show();
|
$('#mobile').removeAttr('data-original');
|
||||||
});
|
$('#emp_code').removeAttr('data-original');
|
||||||
|
|
||||||
|
$('.modal-title').text('Add User');
|
||||||
|
$('#btnSubmit').text('Submit');
|
||||||
|
$('#UserForm').attr('action', '<?php echo base_url('/user/create');?>');
|
||||||
|
|
||||||
|
var myModal = new bootstrap.Modal(document.getElementById('con-close-modal'));
|
||||||
|
myModal.show();
|
||||||
|
});
|
||||||
|
|
||||||
// $(document).on('click', '#btnAdd', function(){
|
// $(document).on('click', '#btnAdd', function(){
|
||||||
// $('#first_name').val('');
|
// $('#first_name').val('');
|
||||||
@ -2009,6 +2139,13 @@ table.dataTable tbody td {
|
|||||||
function validateInput(input, table, field, submitButId){
|
function validateInput(input, table, field, submitButId){
|
||||||
|
|
||||||
let value = $(input).val();
|
let value = $(input).val();
|
||||||
|
let originalValue = $(input).attr('data-original');
|
||||||
|
|
||||||
|
if (originalValue && value.trim().toLowerCase() === originalValue.trim().toLowerCase()) {
|
||||||
|
$('#' + submitButId).prop('disabled', false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let label = $(input).closest('.form-group').find('label').text().replace('*', '').trim();
|
let label = $(input).closest('.form-group').find('label').text().replace('*', '').trim();
|
||||||
|
|
||||||
let message = "Value is duplicate!";
|
let message = "Value is duplicate!";
|
||||||
@ -2018,7 +2155,8 @@ table.dataTable tbody td {
|
|||||||
|
|
||||||
checkDuplicateTableFieldValue(table, field, value, function(isDuplicate) {
|
checkDuplicateTableFieldValue(table, field, value, function(isDuplicate) {
|
||||||
if (isDuplicate) {
|
if (isDuplicate) {
|
||||||
toastr.warning(message, 'Warning');
|
toastr.warning(message, 'Warning');
|
||||||
|
$(input).focus();
|
||||||
// $(input).val('')
|
// $(input).val('')
|
||||||
$('#' + submitButId).prop('disabled', true);
|
$('#' + submitButId).prop('disabled', true);
|
||||||
} else{
|
} else{
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
<style>
|
<style>
|
||||||
.table-responsive {
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reload:hover {
|
.reload:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -13,7 +10,88 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
.dataTables_length label {height: 21px !important;}
|
|
||||||
|
/* TPA variation modal layout */
|
||||||
|
#tpa_variation_modal .modal-dialog {
|
||||||
|
max-width: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tpa_variation_modal .modal-content {
|
||||||
|
max-height: 90vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tpa_variation_modal .modal-body {
|
||||||
|
max-height: calc(85vh - 50px);
|
||||||
|
overflow-y: auto;
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tpa_variation_modal .table-responsive {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tpa_variation_modal table {
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tpa_variation_modal table thead th,
|
||||||
|
#tpa_variation_modal table tbody td {
|
||||||
|
padding: 2px 6px;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tabs spacing & styling */
|
||||||
|
#tpa_variation_modal .nav-tabs {
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tpa_variation_modal .nav-tabs .nav-item {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tpa_variation_modal .nav-tabs .nav-link {
|
||||||
|
padding: 6px 14px;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tpa_variation_modal .nav-tabs .nav-link.active {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-color: #dee2e6 #dee2e6 transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal footer buttons size */
|
||||||
|
#tpa_variation_modal .modal-footer .btn {
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
/* Compact table + buttons (NHance) */
|
||||||
|
#datatable-buttons thead th,
|
||||||
|
#datatable-buttons tbody td {
|
||||||
|
padding: 5px 11px !important;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action dropdown toggle in rows */
|
||||||
|
#datatable-buttons .dropdown-toggle.btn-sm {
|
||||||
|
padding: 4px 9px !important;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DataTables toolbar buttons (Export/Filter/Clear Filter) */
|
||||||
|
#datatable-buttons_wrapper .dt-buttons .btn {
|
||||||
|
padding: 6px 13px !important;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#datatable-buttons_wrapper .dt-buttons .btn .btn-custom {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="col-12" id="second_page">
|
<div class="col-12" id="second_page">
|
||||||
@ -72,6 +150,7 @@
|
|||||||
<?php echo change_date_format($file['created_at'],'Y-m-d H:i:s', 'd M Y h:i a') ?> by <?php echo get_username($file['created_by']) ?>
|
<?php echo change_date_format($file['created_at'],'Y-m-d H:i:s', 'd M Y h:i a') ?> by <?php echo get_username($file['created_by']) ?>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<?php if ($file['status'] == 'failed') { ?>
|
<?php if ($file['status'] == 'failed') { ?>
|
||||||
<?php echo $file['status']; ?>
|
<?php echo $file['status']; ?>
|
||||||
@ -157,36 +236,39 @@
|
|||||||
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm"
|
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm"
|
||||||
data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
|
data-toggle="dropdown" aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
|
||||||
|
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
<?php if($file['actions'] != 'export') { ?>
|
||||||
|
|
||||||
<?php if (str_starts_with($file['status'], 'failed')) { ?>
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
<a href="#"
|
|
||||||
data-id="<?= $file['id'] ?>"
|
|
||||||
data-client_id="<?= $file['client_id'] ?>"
|
|
||||||
data-client_policy_id="<?= $file['client_policy_id'] ?>"
|
|
||||||
data-insurer_or_tpa="<?= $file['insurer_or_tpa'] ?>"
|
|
||||||
data-event_type="<?= $file['event_type'] ?>"
|
|
||||||
data-actions="<?= $file['actions'] ?>"
|
|
||||||
data-client_branch_id="<?= $file['client_branch_id'] ?>"
|
|
||||||
data-issue_date="<?= $file['policy_issue_date'] ?>"
|
|
||||||
|
|
||||||
onclick="getBatchFileData(this)" class="dropdown-item upload_button" ><i class="mdi mdi-upload mr-2 text-muted font-18 vertical-middle"></i>Re-Upload</a>
|
<?php if (str_starts_with($file['status'], 'failed')) { ?>
|
||||||
<?php } ?>
|
<a href="#"
|
||||||
|
data-id="<?= $file['id'] ?>"
|
||||||
|
data-client_id="<?= $file['client_id'] ?>"
|
||||||
|
data-client_policy_id="<?= $file['client_policy_id'] ?>"
|
||||||
|
data-insurer_or_tpa="<?= $file['insurer_or_tpa'] ?>"
|
||||||
|
data-event_type="<?= $file['event_type'] ?>"
|
||||||
|
data-actions="<?= $file['actions'] ?>"
|
||||||
|
data-client_branch_id="<?= $file['client_branch_id'] ?>"
|
||||||
|
data-issue_date="<?= $file['policy_issue_date'] ?>"
|
||||||
|
|
||||||
<?php if ($file['actions'] == "import") { ?>
|
onclick="getBatchFileData(this)" class="dropdown-item upload_button" ><i class="mdi mdi-upload mr-2 text-muted font-18 vertical-middle"></i>Re-Upload</a>
|
||||||
<a href="<?= base_url('/util/download_import_file/') . $file['id'] ?>" class="dropdown-item" style="color: #000;" aria-hidden="true" target="_blank" ><i class="mdi mdi-download mr-2 text-muted font-18 vertical-middle"></i>Download</a>
|
<?php } ?>
|
||||||
<?php } ?>
|
|
||||||
|
|
||||||
<?php if ($file['actions'] == "fetch" && $file['status'] == 'partially success') { ?>
|
<?php if ($file['actions'] == "import") { ?>
|
||||||
<a href="<?= base_url('employee/getTPADataVariationReport/') . $file['id'] ?>" class="dropdown-item" style="color: #000;" aria-hidden="true" target="_blank" ><i class="mdi mdi-download mr-2 text-muted font-18 vertical-middle"></i>Download TPA Variation report</a>
|
<a href="<?= base_url('/util/download_import_file/') . $file['id'] ?>" class="dropdown-item" style="color: #000;" aria-hidden="true" target="_blank" ><i class="mdi mdi-download mr-2 text-muted font-18 vertical-middle"></i>Download</a>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<?php if ($file['actions'] == "fetch" && $file['status'] == 'partially success') { ?>
|
||||||
<?php } ?>
|
<a href="<?= base_url('employee/getTPADataVariationReport/') . $file['id'] ?>" class="dropdown-item" style="color: #000;" aria-hidden="true" target="_blank" ><i class="mdi mdi-download mr-2 text-muted font-18 vertical-middle"></i>Download TPA Variation report</a>
|
||||||
|
<a href="#" class="dropdown-item" style="color: #000;" aria-hidden="true" onclick="getTPADataVariationReport(<?= $file['id'] ?>)"><i class="mdi mdi-eye mr-2 text-muted font-18 vertical-middle"></i>View TPA Variation report</a>
|
||||||
|
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?php } ?>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
<?php }
|
<?php }
|
||||||
} ?>
|
} ?>
|
||||||
@ -228,44 +310,285 @@
|
|||||||
</div><!-- /.modal-dialog -->
|
</div><!-- /.modal-dialog -->
|
||||||
</div><!-- /.modal -->
|
</div><!-- /.modal -->
|
||||||
|
|
||||||
|
<!-- TPA Data Variation Modal -->
|
||||||
|
<div class="modal fade" id="tpa_variation_modal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">TPA Data Variation Report</h4>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<ul class="nav nav-tabs" id="tpaVariationTabs" role="tablist">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" id="not-in-nhance-tab" data-toggle="tab" href="#not_in_nhance_tab" role="tab" aria-controls="not_in_nhance_tab" aria-selected="true">Not in Nhance</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" id="not-in-tpa-tab" data-toggle="tab" href="#not_in_tpa_tab" role="tab" aria-controls="not_in_tpa_tab" aria-selected="false">Not in TPA</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" id="need-to-review-tab" data-toggle="tab" href="#need_to_review_tab" role="tab" aria-controls="need_to_review_tab" aria-selected="false">Need to Review</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content pt-3" id="tpaVariationTabContent">
|
||||||
|
<div class="tab-pane fade show active" id="not_in_nhance_tab" role="tabpanel" aria-labelledby="not-in-nhance-tab">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-sm" id="not_in_nhance_table">
|
||||||
|
<thead></thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="not_in_tpa_tab" role="tabpanel" aria-labelledby="not-in-tpa-tab">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-sm" id="not_in_tpa_table">
|
||||||
|
<thead></thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="need_to_review_tab" role="tabpanel" aria-labelledby="need-to-review-tab">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-sm" id="need_to_review_table">
|
||||||
|
<thead></thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" id="tpaProceedButton" class="btn btn-primary btn-sm" onclick="handleTPAProceed()">Proceed</button>
|
||||||
|
</div>
|
||||||
|
</div><!-- /.modal-content -->
|
||||||
|
</div><!-- /.modal-dialog -->
|
||||||
|
</div><!-- /.modal -->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
let currentTPAVariationFileId = null;
|
||||||
|
|
||||||
|
const tpaVariationColumns = {
|
||||||
|
not_in_nhance: [
|
||||||
|
{ key: 'emp_code', label: 'Employee Code' },
|
||||||
|
{ key: 'name', label: 'Employee Name' },
|
||||||
|
{ key: 'relation', label: 'Relation' },
|
||||||
|
{ key: 'dob', label: 'Date of Birth' },
|
||||||
|
{ key: 'gender', label: 'Gender' },
|
||||||
|
{ key: 'age', label: 'Age' },
|
||||||
|
{ key: 'tpa_id', label: 'TPA Member ID' },
|
||||||
|
],
|
||||||
|
not_in_tpa: [
|
||||||
|
{ key: 'emp_code', label: 'Employee Code' },
|
||||||
|
{ key: 'name', label: 'Employee Name' },
|
||||||
|
{ key: 'relationship', label: 'Relation' },
|
||||||
|
{ key: 'dob', label: 'Date of Birth' },
|
||||||
|
{ key: 'gender', label: 'Gender' },
|
||||||
|
{ key: 'mobile', label: 'Mobile No' },
|
||||||
|
{ key: 'email_corporate', label: 'Corporate Email' },
|
||||||
|
{ key: 'policy_no', label: 'Policy No' },
|
||||||
|
{ key: 'tpa_name', label: 'TPA Name' },
|
||||||
|
{ key: 'change_event', label: 'Change Event' },
|
||||||
|
],
|
||||||
|
need_to_review: [
|
||||||
|
// DB side
|
||||||
|
{ key: 'emp_code', label: 'Employee Code' },
|
||||||
|
{ key: 'name', label: 'Employee Name' },
|
||||||
|
{ key: 'relationship', label: 'Relation' },
|
||||||
|
{ key: 'dob', label: 'Date of Birth' },
|
||||||
|
{ key: 'gender', label: 'Gender' },
|
||||||
|
{ key: 'policy_no', label: 'Policy No' },
|
||||||
|
{ key: 'uhid', label: 'UHID' },
|
||||||
|
{ key: 'change_event', label: 'Change Event' },
|
||||||
|
// TPA side
|
||||||
|
{ key: 'tpa_emp_code', label: 'TPA Employee Code' },
|
||||||
|
{ key: 'tpa_name', label: 'TPA Name' },
|
||||||
|
{ key: 'tpa_relation', label: 'TPA Relation' },
|
||||||
|
{ key: 'tpa_dob', label: 'TPA Date of Birth' },
|
||||||
|
{ key: 'tpa_gender', label: 'TPA Gender' },
|
||||||
|
{ key: 'tpa_tpa_id', label: 'TPA Member ID' },
|
||||||
|
{ key: 'tpa_age', label: 'TPA Age' },
|
||||||
|
// Mismatch info
|
||||||
|
{ key: 'mismatch_fields', label: 'Mismatch Fields' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
function renderVariationTable(tableSelector, rows, columns) {
|
||||||
|
const $table = $(tableSelector);
|
||||||
|
const $thead = $table.find('thead');
|
||||||
|
const $tbody = $table.find('tbody');
|
||||||
|
|
||||||
|
$thead.empty();
|
||||||
|
$tbody.empty();
|
||||||
|
|
||||||
|
const headerRow = $('<tr></tr>');
|
||||||
|
columns.forEach(col => {
|
||||||
|
headerRow.append($('<th></th>').text(col.label));
|
||||||
|
});
|
||||||
|
$thead.append(headerRow);
|
||||||
|
|
||||||
|
if (!rows || !rows.length) {
|
||||||
|
const emptyRow = $('<tr></tr>');
|
||||||
|
columns.forEach((col, index) => {
|
||||||
|
const td = $('<td></td>');
|
||||||
|
if (index === 0) {
|
||||||
|
td.addClass('text-center text-muted').text('No data found');
|
||||||
|
}
|
||||||
|
emptyRow.append(td);
|
||||||
|
});
|
||||||
|
$tbody.append(emptyRow);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.forEach(row => {
|
||||||
|
const tr = $('<tr></tr>');
|
||||||
|
columns.forEach(col => {
|
||||||
|
let value = row[col.key];
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
value = '';
|
||||||
|
}
|
||||||
|
tr.append($('<td></td>').text(value));
|
||||||
|
});
|
||||||
|
$tbody.append(tr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateTPAVariationModal(data) {
|
||||||
|
const notInNhance = data.not_in_nhance || [];
|
||||||
|
const notInTpa = data.not_in_tpa || [];
|
||||||
|
const reviewData = (data.mismatch_data || []).map(row => {
|
||||||
|
let mismatchFields = '';
|
||||||
|
const match = row.match || {};
|
||||||
|
|
||||||
|
const notMatching = match.not_matching || [];
|
||||||
|
if (Array.isArray(notMatching) && notMatching.length) {
|
||||||
|
mismatchFields = notMatching.join(', ');
|
||||||
|
} else if (match.status && match.status !== 'matched') {
|
||||||
|
mismatchFields = match.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tpaRecord = match.tpa_record || {};
|
||||||
|
|
||||||
|
return Object.assign({}, row, {
|
||||||
|
mismatch_fields: mismatchFields,
|
||||||
|
tpa_emp_code: tpaRecord.emp_code || '',
|
||||||
|
tpa_name: tpaRecord.name || '',
|
||||||
|
tpa_relation: tpaRecord.relation || '',
|
||||||
|
tpa_dob: tpaRecord.dob || '',
|
||||||
|
tpa_gender: tpaRecord.gender || '',
|
||||||
|
tpa_tpa_id: tpaRecord.tpa_id || '',
|
||||||
|
tpa_age: tpaRecord.age || '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
destroyVariationDataTable('#not_in_nhance_table');
|
||||||
|
destroyVariationDataTable('#not_in_tpa_table');
|
||||||
|
destroyVariationDataTable('#need_to_review_table');
|
||||||
|
|
||||||
|
renderVariationTable('#not_in_nhance_table', notInNhance, tpaVariationColumns.not_in_nhance);
|
||||||
|
renderVariationTable('#not_in_tpa_table', notInTpa, tpaVariationColumns.not_in_tpa);
|
||||||
|
renderVariationTable('#need_to_review_table', reviewData, tpaVariationColumns.need_to_review);
|
||||||
|
|
||||||
|
initVariationDataTable('#not_in_nhance_table');
|
||||||
|
initVariationDataTable('#not_in_tpa_table');
|
||||||
|
initVariationDataTable('#need_to_review_table');
|
||||||
|
}
|
||||||
|
|
||||||
|
function showTPAVariationModal() {
|
||||||
|
const modalElement = document.getElementById('tpa_variation_modal');
|
||||||
|
if (!modalElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const modal = new bootstrap.Modal(modalElement);
|
||||||
|
modal.show();
|
||||||
|
// Ensure proceed button reflects the currently active tab when modal opens
|
||||||
|
const $activeTab = $('#tpaVariationTabs .nav-link.active');
|
||||||
|
updateTPAProceedButton($activeTab.attr('id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function initVariationDataTable(tableSelector) {
|
||||||
|
const $table = $(tableSelector);
|
||||||
|
|
||||||
|
$table.DataTable({
|
||||||
|
pageLength: 20,
|
||||||
|
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||||
|
searching: true,
|
||||||
|
paging: true,
|
||||||
|
ordering: false,
|
||||||
|
info: true,
|
||||||
|
autoWidth: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroyVariationDataTable(tableSelector) {
|
||||||
|
const $table = $(tableSelector);
|
||||||
|
if ($.fn.DataTable.isDataTable($table)) {
|
||||||
|
$table.DataTable().clear().destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTPAProceedButton(activeId) {
|
||||||
|
const $button = $('#tpaProceedButton');
|
||||||
|
if (!$button.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default: visible
|
||||||
|
$button.removeClass('d-none');
|
||||||
|
|
||||||
|
if (activeId === 'not-in-nhance-tab') {
|
||||||
|
$button.text('Proceed - Not in Nhance');
|
||||||
|
} else if (activeId === 'need-to-review-tab') {
|
||||||
|
$button.text('Proceed - Need to Review');
|
||||||
|
} else if (activeId === 'not-in-tpa-tab') {
|
||||||
|
// Hide button for "Not in TPA" section
|
||||||
|
$button.addClass('d-none');
|
||||||
|
} else {
|
||||||
|
$button.text('Proceed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
// Update proceed button label/visibility on tab change
|
||||||
|
$('#tpaVariationTabs a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||||
|
const activeId = $(e.target).attr('id');
|
||||||
|
updateTPAProceedButton(activeId);
|
||||||
|
});
|
||||||
|
|
||||||
$('#datatable-buttons').DataTable({
|
$('#datatable-buttons').DataTable({
|
||||||
// dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right
|
||||||
// "<'row'<'col-sm-12'tr>>" +
|
|
||||||
// "<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
|
||||||
// buttons: [{
|
|
||||||
// extend: 'csv',
|
|
||||||
// text: 'CSV',
|
|
||||||
// title: 'Batch List',
|
|
||||||
// className: 'my_class',
|
|
||||||
// }],
|
|
||||||
// initComplete: function(settings, json) {
|
|
||||||
// $('.my_class').css({
|
|
||||||
// position: "relative",
|
|
||||||
// left: "50px"
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
|
||||||
"<'row'<'col-sm-12'tr>>" +
|
"<'row'<'col-sm-12'tr>>" +
|
||||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>", lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
buttons: [
|
||||||
buttons: [{
|
{
|
||||||
extend: 'collection',
|
extend: 'collection',
|
||||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||||
className: 'btn app-btn-secondary ',
|
className: 'btn app-btn-secondary mr-2',
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
extend: 'csv',
|
extend: 'csv',
|
||||||
text: '<i class="mdi mdi-file-delimited " ></i><span class=" btn-custom"> CSV </span>',
|
text: '<i class="mdi mdi-file-delimited " ></i><span class=" btn-custom"> CSV </span>',
|
||||||
title: 'Batch List',
|
title: 'Batch List',
|
||||||
className: 'app-btn-primary ',
|
className: 'app-btn-primary ',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '<i class="mdi mdi-filter" ></i><span class=" btn-custom"> Filter </span>',
|
||||||
|
className: 'btn app-btn-primary mr-2',
|
||||||
|
action: function(e, dt, node, config) {
|
||||||
|
openPolicyFilterNav(2);
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
|
{
|
||||||
|
text: '<i class="mdi mdi-filter-remove"></i><span class="btn-custom"> Clear Filter </span>',
|
||||||
|
className: 'btn app-btn-info mr-2 hide-clear-filter',
|
||||||
|
action: function(e, dt, node, config) {
|
||||||
|
clearFilterData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
language: {
|
language: {
|
||||||
search: `
|
search: `
|
||||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
|
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
|
||||||
@ -281,7 +604,6 @@
|
|||||||
paging: true
|
paging: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Function to format a number in Indian Rupees format
|
// Function to format a number in Indian Rupees format
|
||||||
@ -393,5 +715,109 @@
|
|||||||
// });
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTPADataVariationReport(file_id) {
|
||||||
|
|
||||||
|
currentTPAVariationFileId = file_id;
|
||||||
|
|
||||||
|
$('.loader').fadeIn();
|
||||||
|
$('.loader-mask').fadeIn();
|
||||||
|
|
||||||
|
let url = '<?= base_url('employee/getTPADataVariationReportView') ?>/' + file_id;
|
||||||
|
|
||||||
|
sendAjaxRequestForGlobal(url, 'GET', {}, function (response) {
|
||||||
|
|
||||||
|
$('.loader').fadeOut();
|
||||||
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
|
|
||||||
|
if (response && response.status && response.data) {
|
||||||
|
populateTPAVariationModal(response.data);
|
||||||
|
showTPAVariationModal();
|
||||||
|
} else {
|
||||||
|
toastr.warning((response && response.message) || 'No data found for variation report.', 'WARNING');
|
||||||
|
}
|
||||||
|
|
||||||
|
}, function (xhr, status, error) {
|
||||||
|
$('.loader').fadeOut();
|
||||||
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
|
console.error('Error fetching TPA variation data:', error);
|
||||||
|
toastr.error('An error occurred while fetching the variation report.', 'ERROR');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function proceedTPADataVariationNextStep(tabKey) {
|
||||||
|
if (!currentTPAVariationFileId) {
|
||||||
|
toastr.warning('Unable to identify the selected file.', 'WARNING');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.loader').fadeIn();
|
||||||
|
$('.loader-mask').fadeIn();
|
||||||
|
|
||||||
|
const url = '<?= base_url('employee/proceedTPADataVariationNextStep') ?>/' + currentTPAVariationFileId + '?tab=' + encodeURIComponent(tabKey);
|
||||||
|
|
||||||
|
sendAjaxRequestForGlobal(url, 'GET', {}, function (response) {
|
||||||
|
$('.loader').fadeOut();
|
||||||
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
|
|
||||||
|
if (response && response.status) {
|
||||||
|
toastr.success(response.message || 'Proceed to next step initiated successfully.', 'SUCCESS');
|
||||||
|
} else {
|
||||||
|
toastr.warning(response.message || 'Unable to proceed to next step.', 'WARNING');
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.reload();
|
||||||
|
|
||||||
|
}, function (xhr, status, error) {
|
||||||
|
$('.loader').fadeOut();
|
||||||
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
|
toastr.error('An error occurred while proceeding to the next step.', 'ERROR');
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function proceedNotInNhance() {
|
||||||
|
proceedTPADataVariationNextStep('not_in_nhance');
|
||||||
|
}
|
||||||
|
|
||||||
|
function proceedNotInTPA() {
|
||||||
|
proceedTPADataVariationNextStep('not_in_tpa');
|
||||||
|
}
|
||||||
|
|
||||||
|
function proceedNeedToReview() {
|
||||||
|
proceedTPADataVariationNextStep('need_to_review');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTPAProceed() {
|
||||||
|
const $activeTab = $('#tpaVariationTabs .nav-link.active');
|
||||||
|
const activeId = $activeTab.attr('id');
|
||||||
|
|
||||||
|
if (!activeId) {
|
||||||
|
toastr.warning('No active tab selected.', 'WARNING');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabLabel = ($activeTab.text() || '').trim() || 'this tab';
|
||||||
|
|
||||||
|
confirmActionSweertAlert(
|
||||||
|
"Are you sure you want to proceed with the data in this tab?",
|
||||||
|
"Yes, Proceed",
|
||||||
|
"Cancel",
|
||||||
|
"warning"
|
||||||
|
).then(function(isConfirmed) {
|
||||||
|
if (!isConfirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeId === 'not-in-nhance-tab') {
|
||||||
|
proceedNotInNhance();
|
||||||
|
} else if (activeId === 'not-in-tpa-tab') {
|
||||||
|
proceedNotInTPA();
|
||||||
|
} else if (activeId === 'need-to-review-tab') {
|
||||||
|
proceedNeedToReview();
|
||||||
|
} else {
|
||||||
|
toastr.warning('Unknown tab selected.', 'WARNING');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -67,8 +67,7 @@
|
|||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label for="mobile">CD Account Number<span class="text-danger">*</span></label>
|
<label for="mobile">CD Account Number<span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="cd_ac_no_for_cd_master" name="cd_ac_no" onkeypress="return onlyNumbers(event)" required data-parsley-type="digits">
|
<input type="text" class="form-control" id="cd_ac_no_for_cd_master" name="cd_ac_no" pattern="[a-zA-Z0-9\/_-]+" title="Only letters, numbers, /, -, and _ are allowed" required> <small style="margin-left: 10px;font-size: 10px;"><span class="text-danger" id="cd_ac_no_for_cd_master_errorr"></span></small>
|
||||||
<small style="margin-left: 10px;font-size: 10px;"><span class="text-danger" id="cd_ac_no_for_cd_master_errorr"></span></small>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
@ -129,7 +128,7 @@
|
|||||||
if(res.status == true){
|
if(res.status == true){
|
||||||
|
|
||||||
$('#cd_ac_no_for_cd_master_errorr').text(res.message);
|
$('#cd_ac_no_for_cd_master_errorr').text(res.message);
|
||||||
// toastr.warning(res.message, 'warning');
|
// toastr.warning(res.message, 'Warning');
|
||||||
// $('#cd_ac_no').val('');
|
// $('#cd_ac_no').val('');
|
||||||
$('#cd_master_btn_Submit').prop('disabled',true);
|
$('#cd_master_btn_Submit').prop('disabled',true);
|
||||||
return;
|
return;
|
||||||
@ -366,4 +365,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
// Target the input by its ID
|
||||||
|
$('#cd_ac_no_for_cd_master').on('keypress', function(e) {
|
||||||
|
// Get the character code
|
||||||
|
var keyCode = e.which || e.keyCode;
|
||||||
|
var char = String.fromCharCode(keyCode);
|
||||||
|
|
||||||
|
// Regular Expression: allow a-z, A-Z, 0-9, /, -, _
|
||||||
|
// If the character doesn't match, prevent it from being typed
|
||||||
|
var regex = /^[a-zA-Z0-9\/_-]+$/;
|
||||||
|
|
||||||
|
if (!regex.test(char)) {
|
||||||
|
e.preventDefault();
|
||||||
|
// Optional: Show a quick error message in your span
|
||||||
|
$('#cd_ac_no_for_cd_master_errorr').text('Character not allowed').fadeOut(2000, function() {
|
||||||
|
$(this).text('').show();
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also handle "Paste" to ensure invalid data isn't pasted in
|
||||||
|
$('#cd_ac_no_for_cd_master').on('input', function() {
|
||||||
|
var value = $(this).val();
|
||||||
|
var sanitized = value.replace(/[^a-zA-Z0-9\/_-]/g, '');
|
||||||
|
$(this).val(sanitized);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -135,6 +135,8 @@
|
|||||||
<thead class="bg-light">
|
<thead class="bg-light">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="font-weight-medium">S.No </th>
|
<th class="font-weight-medium">S.No </th>
|
||||||
|
<th class="font-weight-medium">Client</th>
|
||||||
|
<th class="font-weight-medium">Policy</th>
|
||||||
<th class="font-weight-medium">File name</th>
|
<th class="font-weight-medium">File name</th>
|
||||||
<th class="font-weight-medium">User/Time</th>
|
<th class="font-weight-medium">User/Time</th>
|
||||||
<th class="font-weight-medium">Status</th>
|
<th class="font-weight-medium">Status</th>
|
||||||
@ -150,6 +152,8 @@
|
|||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center"><b><?php echo ($key + 1) ?></b></td>
|
<td class="text-center"><b><?php echo ($key + 1) ?></b></td>
|
||||||
|
<td><?php echo $file['client_name'] ?></td>
|
||||||
|
<td><?php echo $file['policy_no'] ?></td>
|
||||||
<td style="overflow: hidden;" class="reload truncate" data-toggle="tooltip" data-placement="top" title="<?php echo $file['file_name'] ?>">
|
<td style="overflow: hidden;" class="reload truncate" data-toggle="tooltip" data-placement="top" title="<?php echo $file['file_name'] ?>">
|
||||||
<?php echo $file['file_name'] ?>
|
<?php echo $file['file_name'] ?>
|
||||||
</td>
|
</td>
|
||||||
@ -740,13 +744,18 @@
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
$.each(data, function(index, item) {
|
$.each(data, function(index, item) {
|
||||||
var option = $('<option>', {
|
|
||||||
value: item.id,
|
|
||||||
text: item.policy_type + '-' + item.policy_no,
|
|
||||||
'data-tpaid': item.tpa_id,
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#client_policy_id').append(option);
|
if(item.policy_type_id == 2 || item.policy_type_id == 3 || item.policy_type_id == 4 || item.policy_type_id == 5) {
|
||||||
|
|
||||||
|
var option = $('<option>', {
|
||||||
|
value: item.id,
|
||||||
|
text: item.policy_type + '-' + item.policy_no,
|
||||||
|
'data-tpaid': item.tpa_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#client_policy_id').append(option);
|
||||||
|
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -124,8 +124,8 @@ input:checked + .slider:before {
|
|||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group col-md-4">
|
<div class="form-group col-md-4">
|
||||||
<label for="pan">PAN<span class="text-danger">*</span></label>
|
<label for="pan">PAN<span class="text-danger"></span></label>
|
||||||
<input type="text" class="form-control" id="pan" placeholder="Enter PAN Number" data-parsley-error-message="Invalid PAN Number. Example: ABCDE1234F" value="<?= isset($client['pan']) ? $client['pan'] : '' ?>" name="pan" data-parsley-trigger="change" data-parsley-pattern="^[A-Z]{5}[0-9]{4}[A-Z]$" required>
|
<input type="text" class="form-control" id="pan" placeholder="Enter PAN Number" data-parsley-error-message="Invalid PAN Number. Example: ABCDE1234F" value="<?= isset($client['pan']) ? $client['pan'] : '' ?>" name="pan" data-parsley-trigger="change" data-parsley-pattern="^[A-Z]{5}[0-9]{4}[A-Z]$">
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="form-group col-md-4">
|
<!-- <div class="form-group col-md-4">
|
||||||
<label for="gst">GST<span class="text-danger">*</span></label>
|
<label for="gst">GST<span class="text-danger">*</span></label>
|
||||||
|
|||||||
@ -254,7 +254,10 @@ input:checked + .slider-branch-contact:before {
|
|||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label for="designation">Designation<span class="text-danger">*</span></label>
|
<label for="designation">Designation<span class="text-danger">*</span></label>
|
||||||
<input value="" type="text" class="form-control" placeholder="Enter Designation Name"
|
<input value="" type="text" class="form-control" placeholder="Enter Designation Name"
|
||||||
name="designation[]" id="designation" required>
|
name="designation[]" id="designation" required
|
||||||
|
pattern="[A-Za-z0-9\-_/ ]+"
|
||||||
|
oninput="this.value = this.value.replace(/[^A-Za-z0-9\-_\/ ]/g, '')"
|
||||||
|
title="Designation may only contain letters, numbers, /, -, _ and spaces only allowed">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
@ -697,7 +700,7 @@ $('body').on('click', '.btnBranchEdit', function() {
|
|||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
$('.loader').fadeOut();
|
$('.loader').fadeOut();
|
||||||
$('.loader-mask').delay(350).fadeOut('slow');
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
toastr.warning('Something Wrong!', 'warning');
|
toastr.warning('Something Wrong!', 'Warning');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -736,7 +739,10 @@ function appendContactHtml(contact = false, reset = false) {
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label for="${uniqueId}_designation">Designation<span class="text-danger">*</span></label>
|
<label for="${uniqueId}_designation">Designation<span class="text-danger">*</span></label>
|
||||||
<input value="${contact !== undefined && contact !== false ? contact.designation : ''}" type="text" class="form-control" placeholder="Enter Designation Name" name="designation[]" id="${uniqueId}_designation" required>
|
<input value="${contact !== undefined && contact !== false ? contact.designation : ''}" type="text" class="form-control" placeholder="Enter Designation Name" name="designation[]" id="${uniqueId}_designation" required
|
||||||
|
pattern="[A-Za-z0-9\-_/ ]+"
|
||||||
|
oninput="this.value = this.value.replace(/[^A-Za-z0-9\-_\/ ]/g, '')"
|
||||||
|
title="Designation may only contain letters, numbers, /, -, _ and spaces only allowed">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
@ -916,10 +922,10 @@ function removeClientBranch(element) {
|
|||||||
// console.log(res.status == true);
|
// console.log(res.status == true);
|
||||||
if (res) {
|
if (res) {
|
||||||
if (res.status == true) {
|
if (res.status == true) {
|
||||||
toastr.success(res.message, 'success');
|
toastr.success(res.message, 'Success');
|
||||||
location.reload();
|
location.reload();
|
||||||
} else {
|
} else {
|
||||||
toastr.warning(res.message, 'warning');
|
toastr.warning(res.message, 'Warning');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -377,7 +377,7 @@
|
|||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
$('.loader').fadeOut();
|
$('.loader').fadeOut();
|
||||||
$('.loader-mask').delay(350).fadeOut('slow');
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
toastr.warning('Something Wrong!', 'warning');
|
toastr.warning('Something Wrong!', 'Warning');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -149,15 +149,15 @@ table.dataTable thead th {
|
|||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h4 class="modal-title" id="myCenterModalLabel">Lead List</h4>
|
<h4 class="modal-title" id="myCenterModalLabel">Opportunity List</h4>
|
||||||
<button type="button" id="close_btn" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<button type="button" id="close_btn" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|
||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
<label for="lead_id"> Lead List <span class="text-danger">*</span></label>
|
<label for="lead_id"> Opportunity List <span class="text-danger">*</span></label>
|
||||||
<select class="form-control" id="lead_id" name="lead_id" required>
|
<select class="form-control" id="lead_id" name="lead_id" required>
|
||||||
<option value="">Select Lead</option>
|
<option value="">Select Opportunity</option>
|
||||||
<?php if(isset($lead_data)) { ?>
|
<?php if(isset($lead_data)) { ?>
|
||||||
<?php foreach ($lead_data as $value) { ?>
|
<?php foreach ($lead_data as $value) { ?>
|
||||||
<option value="<?= $value['id']?>"><?= $value['client_name'] ?> - <?= $value['branch_name'] ?> - <?= $value['user_name'] ?></option>
|
<option value="<?= $value['id']?>"><?= $value['client_name'] ?> - <?= $value['branch_name'] ?> - <?= $value['user_name'] ?></option>
|
||||||
@ -312,7 +312,7 @@ $(document).ready(function()
|
|||||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
text: 'Add From Lead',
|
text: 'Add From Opportunities',
|
||||||
className: 'btn-filter add-from-lead', // custom class
|
className: 'btn-filter add-from-lead', // custom class
|
||||||
action: function(e, dt, node, config) {
|
action: function(e, dt, node, config) {
|
||||||
showModal();
|
showModal();
|
||||||
|
|||||||
@ -273,7 +273,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<script>
|
<script>
|
||||||
var pageSubTitle = 'Client Onboarding <span id="client_heading"><?php if (isset($client)) { echo ' - ' . addslashes($client['client_name']); } ?></span>';
|
var pageSubTitle = 'Client Onboarding <span id="client_heading"><?php if (isset($client)) { echo ' - ' . addslashes($client['client_name']); } ?></span>';
|
||||||
var pageBackButton = '<a href="<?= base_url("ticket/list"); ?>" aria-label="Back to list"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
var pageBackButton = '<a href="<?= base_url("client/list"); ?>" aria-label="Back to list"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
||||||
</script>
|
</script>
|
||||||
<!-- <div class="row" style="padding-bottom: 10px;">
|
<!-- <div class="row" style="padding-bottom: 10px;">
|
||||||
<div class="col-6" style="align-self: center;">
|
<div class="col-6" style="align-self: center;">
|
||||||
@ -647,57 +647,58 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function toggleAccordion(header) {
|
|
||||||
if (event.target.classList.contains('fa-info-circle') ||
|
// function toggleAccordion(header) {
|
||||||
event.target.parentElement.classList.contains('fa-info-circle')) {
|
// if (event.target.classList.contains('fa-info-circle') ||
|
||||||
getHrActivityHistory();
|
// event.target.parentElement.classList.contains('fa-info-circle')) {
|
||||||
return; // Do nothing if click was on the info icon
|
// getHrActivityHistory();
|
||||||
}
|
// return; // Do nothing if click was on the info icon
|
||||||
const card = header.parentElement;
|
// }
|
||||||
const content = card.querySelector('.card-content');
|
// const card = header.parentElement;
|
||||||
const icon = header.querySelector('.accordion-icon i');
|
// const content = card.querySelector('.card-content');
|
||||||
|
// const icon = header.querySelector('.accordion-icon i');
|
||||||
|
|
||||||
// Close all other accordions
|
// // Close all other accordions
|
||||||
document.querySelectorAll('.user-card').forEach(otherCard => {
|
// document.querySelectorAll('.user-card').forEach(otherCard => {
|
||||||
if (otherCard !== card) {
|
// if (otherCard !== card) {
|
||||||
const otherContent = otherCard.querySelector('.card-content');
|
// const otherContent = otherCard.querySelector('.card-content');
|
||||||
const otherIcon = otherCard.querySelector('.accordion-icon i');
|
// const otherIcon = otherCard.querySelector('.accordion-icon i');
|
||||||
otherIcon.setAttribute('class', 'mdi mdi-chevron-down');
|
// otherIcon.setAttribute('class', 'mdi mdi-chevron-down');
|
||||||
|
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
// Toggle current accordion
|
// // Toggle current accordion
|
||||||
content.classList.toggle('active');
|
// content.classList.toggle('active');
|
||||||
if (content.classList.contains('active')) {
|
// if (content.classList.contains('active')) {
|
||||||
icon.classList.setAttribute('class', 'mdi mdi-chevron-up');
|
// icon.classList.setAttribute('class', 'mdi mdi-chevron-up');
|
||||||
} else {
|
// } else {
|
||||||
icon.classList.setAttribute('class', 'mdi mdi-chevron-down');
|
// icon.classList.setAttribute('class', 'mdi mdi-chevron-down');
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
function toggleSection(checkbox, sectionId) {
|
// function toggleSection(checkbox, sectionId) {
|
||||||
const section = document.getElementById(sectionId);
|
// const section = document.getElementById(sectionId);
|
||||||
const checkboxes = section.querySelectorAll('input[type="checkbox"]');
|
// const checkboxes = section.querySelectorAll('input[type="checkbox"]');
|
||||||
|
|
||||||
if (checkbox.checked) {
|
// if (checkbox.checked) {
|
||||||
section.classList.remove('hidden');
|
// section.classList.remove('hidden');
|
||||||
checkboxes.forEach(cb => {
|
// checkboxes.forEach(cb => {
|
||||||
cb.disabled = false;
|
// cb.disabled = false;
|
||||||
});
|
// });
|
||||||
} else {
|
// } else {
|
||||||
section.classList.add('hidden');
|
// section.classList.add('hidden');
|
||||||
checkboxes.forEach(cb => {
|
// checkboxes.forEach(cb => {
|
||||||
cb.checked = false;
|
// cb.checked = false;
|
||||||
cb.disabled = true;
|
// cb.disabled = true;
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
function getCheckedValues(name) {
|
// function getCheckedValues(name) {
|
||||||
const checkboxes = document.querySelectorAll(`input[name="${name}"]:checked`);
|
// const checkboxes = document.querySelectorAll(`input[name="${name}"]:checked`);
|
||||||
return Array.from(checkboxes).map(cb => parseInt(cb.value));
|
// return Array.from(checkboxes).map(cb => parseInt(cb.value));
|
||||||
}
|
// }
|
||||||
|
|
||||||
function getHrActivityHistory() {
|
function getHrActivityHistory() {
|
||||||
console.log(event.target.dataset.id); //return;
|
console.log(event.target.dataset.id); //return;
|
||||||
@ -827,12 +828,6 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to get checked checkbox values (if not already defined)
|
|
||||||
function getCheckedValues(namePrefix) {
|
|
||||||
const checkboxes = document.querySelectorAll(`input[name^="${namePrefix}"]:checked`);
|
|
||||||
return Array.from(checkboxes).map(cb => cb.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).on('change', '.select-all', function() {
|
$(document).on('change', '.select-all', function() {
|
||||||
let $this = $(this);
|
let $this = $(this);
|
||||||
let type = $this.data('type');
|
let type = $this.data('type');
|
||||||
|
|||||||
@ -104,7 +104,7 @@ input:checked + .slider_blue::before {
|
|||||||
|
|
||||||
<div class="row" style="padding-bottom: 10px; position: relative;right: 13px; justify-content: end;">
|
<div class="row" style="padding-bottom: 10px; position: relative;right: 13px; justify-content: end;">
|
||||||
<button type="button" id="BtnAdd" class="btn btn-primary waves-effect waves-light btnAdd btn-sm" style="position: relative;right: 10px;"><span class="mdi mdi-plus-box-outline" aria-hidden="true" style="padding: 5px 10px;"></span>Add Policy</button>
|
<button type="button" id="BtnAdd" class="btn btn-primary waves-effect waves-light btnAdd btn-sm" style="position: relative;right: 10px;"><span class="mdi mdi-plus-box-outline" aria-hidden="true" style="padding: 5px 10px;"></span>Add Policy</button>
|
||||||
<button type="button" id="BtnAddSuccess" class="btn btn-success waves-effect waves-light BtnAddSuccess btn-sm" onclick="showModal()"><span class="mdi mdi-plus-box-outline" aria-hidden="true" style="padding: 5px 10px;"></span>Add Policy From Lead</button>
|
<button type="button" id="BtnAddSuccess" class="btn btn-success waves-effect waves-light BtnAddSuccess btn-sm" onclick="showModal()"><span class="mdi mdi-plus-box-outline" aria-hidden="true" style="padding: 5px 10px;"></span>Add Policy From Opportunities</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
@ -339,15 +339,15 @@ input:checked + .slider_blue::before {
|
|||||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h4 class="modal-title" id="myCenterModalLabel">Lead List</h4>
|
<h4 class="modal-title" id="myCenterModalLabel">Opportunity List</h4>
|
||||||
<button type="button" id="close_btn" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<button type="button" id="close_btn" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|
||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
<label for="lead_id"> Lead List <span class="text-danger">*</span></label>
|
<label for="lead_id"> Opportunity List <span class="text-danger">*</span></label>
|
||||||
<select class="form-control" id="lead_id" name="lead_id" required>
|
<select class="form-control" id="lead_id" name="lead_id" required>
|
||||||
<option value="">Select Lead</option>
|
<option value="">Select Opportunity</option>
|
||||||
<?php if(isset($lead_data)) { ?>
|
<?php if(isset($lead_data)) { ?>
|
||||||
<?php foreach ($lead_data as $value) { ?>
|
<?php foreach ($lead_data as $value) { ?>
|
||||||
<option value="<?= $value['id']?>" data-clientid="<?= $value['client_id']?>", data-branchid="<?= $value['client_branch_id']?>"><?= $value['client_name'] ?> - <?= $value['branch_name'] ?> - <?= $value['user_name'] ?></option>
|
<option value="<?= $value['id']?>" data-clientid="<?= $value['client_id']?>", data-branchid="<?= $value['client_branch_id']?>"><?= $value['client_name'] ?> - <?= $value['branch_name'] ?> - <?= $value['user_name'] ?></option>
|
||||||
|
|||||||
@ -13,7 +13,7 @@ option:disabled {
|
|||||||
<div class="card mb-1">
|
<div class="card mb-1">
|
||||||
|
|
||||||
<h4 class="m-1">
|
<h4 class="m-1">
|
||||||
<span>Filters</span>
|
<!-- <span>Filters</span> -->
|
||||||
<a id="toggleIcon1" class="text-dark float-right" data-toggle="collapse" href="#collapseTwo"
|
<a id="toggleIcon1" class="text-dark float-right" data-toggle="collapse" href="#collapseTwo"
|
||||||
aria-expanded="true">
|
aria-expanded="true">
|
||||||
<i id="icon1" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
<i id="icon1" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||||
@ -658,6 +658,7 @@ function appendClients(data) {
|
|||||||
option.attr('selected', true);
|
option.attr('selected', true);
|
||||||
}
|
}
|
||||||
$('#client_id').append(option);
|
$('#client_id').append(option);
|
||||||
|
$('#client_id_for_filter').append(option.clone());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,35 @@
|
|||||||
|
<style>
|
||||||
|
|
||||||
|
.truncate {
|
||||||
|
max-width: 80px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#expense-desc-popup {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 9999;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
max-width: 320px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
<div class="row" id="expense_module">
|
<div class="row" id="expense_module">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@ -80,7 +112,18 @@
|
|||||||
|
|
||||||
<div class="form-group col-md-4">
|
<div class="form-group col-md-4">
|
||||||
<label for="amount">Amount <span class="text-danger">*</span></label>
|
<label for="amount">Amount <span class="text-danger">*</span></label>
|
||||||
<input type="number" step="0.01" min="0" class="form-control" id="amount" name="amount" placeholder="Enter amount" value="<?= isset($filters['amount']) ? esc($filters['amount']) : ''; ?>" required>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
min="0"
|
||||||
|
max="100000000"
|
||||||
|
class="form-control"
|
||||||
|
id="amount"
|
||||||
|
name="amount"
|
||||||
|
placeholder="Enter amount"
|
||||||
|
value="<?= isset($filters['amount']) ? esc($filters['amount']) : ''; ?>"
|
||||||
|
required
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-4">
|
<div class="form-group col-md-4">
|
||||||
@ -126,7 +169,16 @@
|
|||||||
<?= ! empty($row['short_name']) ? ' (' . esc($row['short_name']) . ')' : ''; ?>
|
<?= ! empty($row['short_name']) ? ' (' . esc($row['short_name']) . ')' : ''; ?>
|
||||||
</td>
|
</td>
|
||||||
<td><?= esc($row['policy_no'] ?? ''); ?></td>
|
<td><?= esc($row['policy_no'] ?? ''); ?></td>
|
||||||
<td><?= esc($row['description'] ?? ''); ?></td>
|
<td class="desc-cell">
|
||||||
|
<i
|
||||||
|
class="mdi mdi-information-outline text-muted expense-desc-icon"
|
||||||
|
style="cursor: pointer;"
|
||||||
|
data-description="<?= esc($row['description'] ?? ''); ?>"
|
||||||
|
></i>
|
||||||
|
<span class="truncate">
|
||||||
|
<?= esc($row['description'] ?? ''); ?>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
<td><?= esc($row['approved_by_name'] ?? ''); ?></td>
|
<td><?= esc($row['approved_by_name'] ?? ''); ?></td>
|
||||||
<td><?= number_format((float) ($row['amount'] ?? 0), 2); ?></td>
|
<td><?= number_format((float) ($row['amount'] ?? 0), 2); ?></td>
|
||||||
<td>
|
<td>
|
||||||
@ -213,8 +265,13 @@
|
|||||||
|
|
||||||
if (!amount) {
|
if (!amount) {
|
||||||
errors.push('Amount is required.');
|
errors.push('Amount is required.');
|
||||||
} else if (isNaN(amount) || Number(amount) < 0) {
|
} else {
|
||||||
errors.push('Amount must be a non-negative number.');
|
const amountNum = Number(amount);
|
||||||
|
if (isNaN(amountNum) || amountNum < 0) {
|
||||||
|
errors.push('Amount must be a non-negative number.');
|
||||||
|
} else if (amountNum > 100000000) {
|
||||||
|
errors.push('Amount cannot be greater than 100 Cr.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!expenseDate) {
|
if (!expenseDate) {
|
||||||
@ -261,8 +318,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (amount) {
|
if (amount) {
|
||||||
if (isNaN(amount) || Number(amount) < 0) {
|
const amountNum = Number(amount);
|
||||||
|
if (isNaN(amountNum) || amountNum < 0) {
|
||||||
errors.push('Amount filter must be a non-negative number.');
|
errors.push('Amount filter must be a non-negative number.');
|
||||||
|
} else if (amountNum > 100000000) {
|
||||||
|
errors.push('Amount filter cannot be greater than 100 Cr.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,10 +490,12 @@
|
|||||||
|
|
||||||
window.expenseDatePicker = flatpickr('#expense_date', {
|
window.expenseDatePicker = flatpickr('#expense_date', {
|
||||||
dateFormat: 'd-m-Y',
|
dateFormat: 'd-m-Y',
|
||||||
allowInput: true
|
allowInput: false,
|
||||||
|
maxDate: new Date(),
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#expense-table').DataTable({
|
$('#expense-table').DataTable({
|
||||||
|
scrollX: true,
|
||||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||||
"<'row'<'col-sm-12'tr>>" +
|
"<'row'<'col-sm-12'tr>>" +
|
||||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||||
@ -450,7 +512,18 @@
|
|||||||
title: 'Expense List',
|
title: 'Expense List',
|
||||||
className: 'app-btn-primary ',
|
className: 'app-btn-primary ',
|
||||||
exportOptions: {
|
exportOptions: {
|
||||||
columns: ':not(:last-child)'
|
columns: ':not(:last-child)',
|
||||||
|
format: {
|
||||||
|
body: function (data, row, column, node) {
|
||||||
|
// Column index 2 is "Policy No" (S.No=0, Client=1, Policy No=2)
|
||||||
|
if (column === 2) {
|
||||||
|
// Prefix with apostrophe to force Excel treat as text and preserve long numbers
|
||||||
|
var text = $(node).text ? $(node).text() : data;
|
||||||
|
return "'" + text;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -461,7 +534,16 @@
|
|||||||
className: 'app-btn-primary ',
|
className: 'app-btn-primary ',
|
||||||
exportOptions: {
|
exportOptions: {
|
||||||
orthogonal: 'sort',
|
orthogonal: 'sort',
|
||||||
columns: ':not(:last-child)'
|
columns: ':not(:last-child)',
|
||||||
|
format: {
|
||||||
|
body: function (data, row, column, node) {
|
||||||
|
if (column === 2) {
|
||||||
|
var text = $(node).text ? $(node).text() : data;
|
||||||
|
return "'" + text;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -581,6 +663,34 @@
|
|||||||
$('#btnResetExpense, #btnCancelExpense').on('click', function () {
|
$('#btnResetExpense, #btnCancelExpense').on('click', function () {
|
||||||
resetExpenseForm();
|
resetExpenseForm();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Show small popup near the info icon on hover (no tooltip)
|
||||||
|
let $popup = $('#expense-desc-popup');
|
||||||
|
if (!$popup.length) {
|
||||||
|
$popup = $('<div id="expense-desc-popup"></div>').appendTo('body');
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).on('mouseenter', '.expense-desc-icon', function () {
|
||||||
|
const desc = $(this).data('description') || '';
|
||||||
|
if (!desc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$popup.text(desc);
|
||||||
|
|
||||||
|
const offset = $(this).offset();
|
||||||
|
const iconHeight = $(this).outerHeight() || 16;
|
||||||
|
|
||||||
|
$popup.css({
|
||||||
|
top: offset.top + iconHeight + 4,
|
||||||
|
left: offset.left,
|
||||||
|
display: 'block'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('mouseleave', '.expense-desc-icon', function () {
|
||||||
|
$popup.hide();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -60,10 +60,11 @@
|
|||||||
? htmlspecialchars(substr($category, 0, 50)) . "..."
|
? htmlspecialchars(substr($category, 0, 50)) . "..."
|
||||||
: htmlspecialchars($category);
|
: htmlspecialchars($category);
|
||||||
?></td>
|
?></td>
|
||||||
<td><?php $question = $row['question'] ? $row['question'] : 'N/A';
|
<td><?php
|
||||||
echo (strlen($question) > 50)
|
$question_text = $row['question'] ? strip_tags($row['question']) : 'N/A';
|
||||||
? htmlspecialchars(substr($question, 0, 50)) . "..."
|
echo (strlen($question_text) > 50)
|
||||||
: htmlspecialchars($question);
|
? htmlspecialchars(substr($question_text, 0, 50)) . "..."
|
||||||
|
: htmlspecialchars($question_text);
|
||||||
?></td>
|
?></td>
|
||||||
<!-- <td><?php $answer = $row['answer'] ? $row['answer'] : 'N/A';
|
<!-- <td><?php $answer = $row['answer'] ? $row['answer'] : 'N/A';
|
||||||
echo (strlen($answer) > 50)
|
echo (strlen($answer) > 50)
|
||||||
@ -324,7 +325,7 @@
|
|||||||
}
|
}
|
||||||
$('.loader, .loader-mask').fadeOut();
|
$('.loader, .loader-mask').fadeOut();
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function (xhr, status, error) {
|
||||||
$('.loader, .loader-mask').fadeOut();
|
$('.loader, .loader-mask').fadeOut();
|
||||||
if (xhr.status === 400) {
|
if (xhr.status === 400) {
|
||||||
let response = JSON.parse(xhr.responseText);
|
let response = JSON.parse(xhr.responseText);
|
||||||
@ -366,7 +367,8 @@
|
|||||||
$('#FAQForm')[0].reset();
|
$('#FAQForm')[0].reset();
|
||||||
$('#faq_id').val(data.id);
|
$('#faq_id').val(data.id);
|
||||||
$('#category').val(data.category);
|
$('#category').val(data.category);
|
||||||
$('#question').val(data.question);
|
// Safely set the question value by stripping any HTML tags
|
||||||
|
$('#question').val($('<div/>').html(data.question || '').text());
|
||||||
if (editor) {
|
if (editor) {
|
||||||
editor.value = data.answer || '';
|
editor.value = data.answer || '';
|
||||||
}
|
}
|
||||||
@ -374,6 +376,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Simple client-side XSS pattern check to give instant feedback
|
||||||
|
function hasUnsafeHtml(html) {
|
||||||
|
if (!html) return false;
|
||||||
|
|
||||||
|
const decoded = $('<textarea/>').html(html).text().toLowerCase();
|
||||||
|
|
||||||
|
const patterns = [
|
||||||
|
/<\s*script/i,
|
||||||
|
/on\w+\s*=/i,
|
||||||
|
/javascript\s*:/i,
|
||||||
|
/vbscript\s*:/i,
|
||||||
|
/data\s*:\s*text\/html/i,
|
||||||
|
/expression\s*\(/i
|
||||||
|
];
|
||||||
|
|
||||||
|
return patterns.some(p => p.test(decoded));
|
||||||
|
}
|
||||||
|
|
||||||
$('#FAQForm').on('submit', function(e) {
|
$('#FAQForm').on('submit', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const form = this;
|
const form = this;
|
||||||
@ -393,6 +413,12 @@
|
|||||||
toastr.warning("Answer is required", 'Warning');
|
toastr.warning("Answer is required", 'Warning');
|
||||||
return; // Stop the function here
|
return; // Stop the function here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Block unsafe HTML/JS patterns
|
||||||
|
if (hasUnsafeHtml(editor.value)) {
|
||||||
|
toastr.error("Answer contains restricted tags or attributes. Script tags, iframes and event handlers (like onclick, onerror, onmouseover) are not allowed.", "Validation Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If valid, submit via AJAX
|
// If valid, submit via AJAX
|
||||||
|
|||||||
@ -8,10 +8,36 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dataTables_length label {height: 21px !important;}
|
|
||||||
|
|
||||||
.column-header { margin-right: 10px; /* Adjust this value as needed */ }
|
.column-header { margin-right: 10px; /* Adjust this value as needed */ }
|
||||||
|
|
||||||
|
/* Compact table + buttons (NHance) */
|
||||||
|
#tickets-table thead th,
|
||||||
|
#tickets-table tbody td {
|
||||||
|
padding: 6px 12px !important;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tickets-table .font-12 {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action dropdown toggle in rows */
|
||||||
|
#tickets-table .dropdown-toggle.btn-sm {
|
||||||
|
padding: 4px 9px !important;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DataTables toolbar buttons (Export/Filter/Clear Filter) */
|
||||||
|
#tickets-table_wrapper .dt-buttons .btn {
|
||||||
|
padding: 6px 13px !important;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tickets-table_wrapper .dt-buttons .btn .btn-custom {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@ -170,177 +196,110 @@
|
|||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$('body').on('click', '.view_emp_list', function() {
|
|
||||||
|
|
||||||
console.log('file_id', 'file_id');
|
$('body').on('click', '.view_emp_list', function() {
|
||||||
$('#emp_data_success').empty();
|
|
||||||
$('#title').html(' ');
|
|
||||||
var file_id = $(this).attr('data-id');
|
|
||||||
console.log(file_id);
|
|
||||||
|
|
||||||
var queryParams = {
|
console.log('file_id', 'file_id');
|
||||||
file_id: file_id,
|
$('#emp_data_success').empty();
|
||||||
};
|
$('#title').html(' ');
|
||||||
|
var file_id = $(this).attr('data-id');
|
||||||
|
console.log(file_id);
|
||||||
|
|
||||||
console.log('queryParams', queryParams)
|
var queryParams = {
|
||||||
const queryString = objectToQueryString(queryParams);
|
|
||||||
console.log('queryString', queryString)
|
|
||||||
|
|
||||||
$('.loader').fadeIn();
|
|
||||||
$('.loader-mask').fadeIn();
|
|
||||||
var uri = '<?= base_url('util/view-success-emp-list') ?>?' + queryString
|
|
||||||
console.log(uri)
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: uri,
|
|
||||||
data: {
|
|
||||||
file_id: file_id,
|
file_id: file_id,
|
||||||
},
|
};
|
||||||
type: "GET",
|
|
||||||
dataType: 'json',
|
|
||||||
processData: false,
|
|
||||||
contentType: false,
|
|
||||||
success: function(res) {
|
|
||||||
|
|
||||||
console.log(res);
|
console.log('queryParams', queryParams)
|
||||||
|
const queryString = objectToQueryString(queryParams);
|
||||||
|
console.log('queryString', queryString)
|
||||||
|
|
||||||
if (res) {
|
$('.loader').fadeIn();
|
||||||
|
$('.loader-mask').fadeIn();
|
||||||
|
var uri = '<?= base_url('util/view-success-emp-list') ?>?' + queryString
|
||||||
|
console.log(uri)
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: uri,
|
||||||
|
data: {
|
||||||
|
file_id: file_id,
|
||||||
|
},
|
||||||
|
type: "GET",
|
||||||
|
dataType: 'json',
|
||||||
|
processData: false,
|
||||||
|
contentType: false,
|
||||||
|
success: function(res) {
|
||||||
|
|
||||||
|
console.log(res);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
setTimeout(function() {
|
||||||
|
$('.loader').fadeOut();
|
||||||
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
var title = " - ";
|
||||||
|
|
||||||
|
if (res.file_data) {
|
||||||
|
title += (res.file_data.file_name || "") + " - ";
|
||||||
|
title += (res.file_data.short_name || "") + " - ";
|
||||||
|
title += (res.file_data.policy_name || "") + " - ";
|
||||||
|
title += (res.file_data.action || "") + " - ";
|
||||||
|
title += (res.file_data.status || "");
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#title_header_name').html(title);
|
||||||
|
$('#emp_data_success').html(res.data);
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
console.error(xhr.responseText);
|
||||||
|
console.error(status, error);
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
$('.loader').fadeOut();
|
$('.loader').fadeOut();
|
||||||
$('.loader-mask').delay(350).fadeOut('slow');
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
|
toastr.warning('Something went wrong', 'Warning');
|
||||||
|
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var title = " - ";
|
})
|
||||||
|
|
||||||
if (res.file_data) {
|
$('body').on('click', '.upload_button', function() {
|
||||||
title += (res.file_data.file_name || "") + " - ";
|
|
||||||
title += (res.file_data.short_name || "") + " - ";
|
|
||||||
title += (res.file_data.policy_name || "") + " - ";
|
|
||||||
title += (res.file_data.action || "") + " - ";
|
|
||||||
title += (res.file_data.status || "");
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#title_header_name').html(title);
|
$('#uploadForm')[0].reset();
|
||||||
$('#emp_data_success').html(res.data);
|
console.log('file_id');
|
||||||
},
|
var fileId = JSON.parse(this.getAttribute('data-id'));
|
||||||
error: function(xhr, status, error) {
|
console.log(fileId); // Use fileId as needed
|
||||||
console.error(xhr.responseText);
|
$('#file_client_id').val(fileId.client_id)
|
||||||
console.error(status, error);
|
$('#file_policy_id').val(fileId.client_policy_id)
|
||||||
setTimeout(function() {
|
$('#file_branch_id').val(fileId.client_branch_id)
|
||||||
$('.loader').fadeOut();
|
$('#file_upload_actions').val(fileId.action)
|
||||||
$('.loader-mask').delay(350).fadeOut('slow');
|
})
|
||||||
toastr.warning('Something went wrong', 'Warning');
|
|
||||||
|
|
||||||
}, 1000);
|
$('body').on('click', '.truncate2', function(event) {
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
})
|
event.preventDefault();
|
||||||
|
// console.log(event);
|
||||||
|
var fileId = JSON.parse(this.getAttribute('data-id'));
|
||||||
|
console.log('Truncate File ID' + fileId);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$('body').on('click', '.upload_button', function() {
|
|
||||||
|
|
||||||
$('#uploadForm')[0].reset();
|
|
||||||
console.log('file_id');
|
|
||||||
var fileId = JSON.parse(this.getAttribute('data-id'));
|
|
||||||
console.log(fileId); // Use fileId as needed
|
|
||||||
$('#file_client_id').val(fileId.client_id)
|
|
||||||
$('#file_policy_id').val(fileId.client_policy_id)
|
|
||||||
$('#file_branch_id').val(fileId.client_branch_id)
|
|
||||||
$('#file_upload_actions').val(fileId.action)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
$('body').on('click', '.truncate2', function(event) {
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
// console.log(event);
|
|
||||||
var fileId = JSON.parse(this.getAttribute('data-id'));
|
|
||||||
console.log('Truncate File ID' + fileId);
|
|
||||||
|
|
||||||
Swal.fire({
|
|
||||||
title: "Do you want to truncate data from uploaded file?",
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonText: "Delete",
|
|
||||||
confirmButtonColor: "#ff3333",
|
|
||||||
}).then((result) => {
|
|
||||||
|
|
||||||
console.log(result);
|
|
||||||
|
|
||||||
if (result.isConfirmed) {
|
|
||||||
|
|
||||||
$('.loader').fadeIn();
|
|
||||||
$('.loader-mask').fadeIn();
|
|
||||||
var apiURL = '<?php echo base_url(); ?>employee/truncate/' + fileId;
|
|
||||||
// console.log('Truncate API URL : ', apiURL);
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: apiURL,
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
"X-Requested-With": "XMLHttpRequest"
|
|
||||||
},
|
|
||||||
success: function(response) {
|
|
||||||
|
|
||||||
// console.log('Truncate Response', response)
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
$('.loader').fadeOut();
|
|
||||||
$('.loader-mask').delay(350).fadeOut('slow');
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
if (response.code === 200 && response.dataStatus === true && response.data !== "") {
|
|
||||||
Swal.fire({
|
|
||||||
title: "Deleted!",
|
|
||||||
icon: "success"
|
|
||||||
});
|
|
||||||
} else if (response.code === 404 && response.dataStatus === false) {
|
|
||||||
console.error('No data found', response);
|
|
||||||
handleNoDataFound(response, fileId);
|
|
||||||
} else {
|
|
||||||
Swal.fire({
|
|
||||||
title: "Failed!",
|
|
||||||
text: 'Something went wrong! Try later',
|
|
||||||
icon: "error"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function(xhr, status, error) {
|
|
||||||
|
|
||||||
console.error(xhr.responseText);
|
|
||||||
console.error(status, error);
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
$('.loader').fadeOut();
|
|
||||||
$('.loader-mask').delay(350).fadeOut('slow');
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
console.error('Error fetching data from API:', error);
|
|
||||||
toastr.error('Something went wrong! Try later', 'Error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
function handleNoDataFound(response, fileId) {
|
|
||||||
if (response.role == 1 || response.role == 5) {
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: response.message,
|
title: "Do you want to truncate data from uploaded file?",
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
confirmButtonText: "Delete",
|
confirmButtonText: "Delete",
|
||||||
confirmButtonColor: "#ff3333",
|
confirmButtonColor: "#ff3333",
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
|
|
||||||
console.log(result);
|
console.log(result);
|
||||||
|
|
||||||
if (result.isConfirmed) {
|
if (result.isConfirmed) {
|
||||||
|
|
||||||
$('.loader').fadeIn();
|
$('.loader').fadeIn();
|
||||||
$('.loader-mask').fadeIn();
|
$('.loader-mask').fadeIn();
|
||||||
var apiURL = '<?php echo base_url(); ?>employee/truncate/' + fileId + '/' + <?= get_role_id(); ?>;
|
var apiURL = '<?php echo base_url(); ?>employee/truncate/' + fileId;
|
||||||
|
// console.log('Truncate API URL : ', apiURL);
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: apiURL,
|
url: apiURL,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@ -348,6 +307,9 @@ function handleNoDataFound(response, fileId) {
|
|||||||
"X-Requested-With": "XMLHttpRequest"
|
"X-Requested-With": "XMLHttpRequest"
|
||||||
},
|
},
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
|
|
||||||
|
// console.log('Truncate Response', response)
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
$('.loader').fadeOut();
|
$('.loader').fadeOut();
|
||||||
$('.loader-mask').delay(350).fadeOut('slow');
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
@ -360,11 +322,7 @@ function handleNoDataFound(response, fileId) {
|
|||||||
});
|
});
|
||||||
} else if (response.code === 404 && response.dataStatus === false) {
|
} else if (response.code === 404 && response.dataStatus === false) {
|
||||||
console.error('No data found', response);
|
console.error('No data found', response);
|
||||||
Swal.fire({
|
handleNoDataFound(response, fileId);
|
||||||
title: "Failed!",
|
|
||||||
text: response.message,
|
|
||||||
icon: "error"
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Failed!",
|
title: "Failed!",
|
||||||
@ -374,127 +332,201 @@ function handleNoDataFound(response, fileId) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(xhr, status, error) {
|
error: function(xhr, status, error) {
|
||||||
|
|
||||||
|
console.error(xhr.responseText);
|
||||||
|
console.error(status, error);
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
$('.loader').fadeOut();
|
$('.loader').fadeOut();
|
||||||
$('.loader-mask').delay(350).fadeOut('slow');
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
console.error('Error fetching data from API:', error);
|
console.error('Error fetching data from API:', error);
|
||||||
console.error('Something went wrong! Try later', 'Error');
|
toastr.error('Something went wrong! Try later', 'Error');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
} else {
|
})
|
||||||
Swal.fire({
|
|
||||||
title: "Failed!",
|
|
||||||
text: response.message,
|
|
||||||
icon: "error"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#uploadForm').submit(function() {
|
function handleNoDataFound(response, fileId) {
|
||||||
|
if (response.role == 1 || response.role == 5) {
|
||||||
|
Swal.fire({
|
||||||
|
title: response.message,
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: "Delete",
|
||||||
|
confirmButtonColor: "#ff3333",
|
||||||
|
}).then((result) => {
|
||||||
|
|
||||||
var isValid = $('#uploadForm').parsley().validate();
|
console.log(result);
|
||||||
if (!isValid) {
|
if (result.isConfirmed) {
|
||||||
console.log('Form is Empty', 'Warning');
|
$('.loader').fadeIn();
|
||||||
return;
|
$('.loader-mask').fadeIn();
|
||||||
|
var apiURL = '<?php echo base_url(); ?>employee/truncate/' + fileId + '/' + <?= get_role_id(); ?>;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: apiURL,
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
"X-Requested-With": "XMLHttpRequest"
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
setTimeout(function() {
|
||||||
|
$('.loader').fadeOut();
|
||||||
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
if (response.code === 200 && response.dataStatus === true && response.data !== "") {
|
||||||
|
Swal.fire({
|
||||||
|
title: "Deleted!",
|
||||||
|
icon: "success"
|
||||||
|
});
|
||||||
|
} else if (response.code === 404 && response.dataStatus === false) {
|
||||||
|
console.error('No data found', response);
|
||||||
|
Swal.fire({
|
||||||
|
title: "Failed!",
|
||||||
|
text: response.message,
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: "Failed!",
|
||||||
|
text: 'Something went wrong! Try later',
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
setTimeout(function() {
|
||||||
|
$('.loader').fadeOut();
|
||||||
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
console.error('Error fetching data from API:', error);
|
||||||
|
console.error('Something went wrong! Try later', 'Error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: "Failed!",
|
||||||
|
text: response.message,
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create FormData object
|
$('#uploadForm').submit(function() {
|
||||||
var formData = new FormData($(this)[0]);
|
|
||||||
for (var pair of formData.entries()) {
|
|
||||||
console.log(pair[0] + ', ' + pair[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// return false;
|
var isValid = $('#uploadForm').parsley().validate();
|
||||||
$.ajax({
|
if (!isValid) {
|
||||||
url: $(this).attr("action"),
|
console.log('Form is Empty', 'Warning');
|
||||||
type: "POST",
|
return;
|
||||||
data: formData,
|
}
|
||||||
processData: false, // Prevent jQuery from automatically processing the data
|
|
||||||
contentType: false, // Let jQuery handle the content type
|
|
||||||
headers: {
|
|
||||||
// "Content-Type":"multipart/form-data",
|
|
||||||
"X-Requested-With": "XMLHttpRequest"
|
|
||||||
},
|
|
||||||
success: function(response) {
|
|
||||||
|
|
||||||
console.log(response);
|
// Create FormData object
|
||||||
$('#uploadForm')[0].reset();
|
var formData = new FormData($(this)[0]);
|
||||||
|
for (var pair of formData.entries()) {
|
||||||
|
console.log(pair[0] + ', ' + pair[1]);
|
||||||
|
}
|
||||||
|
|
||||||
if (response.code === 200 && response.dataStatus === true && response
|
// return false;
|
||||||
.data !== "") {
|
$.ajax({
|
||||||
toastr.success(
|
url: $(this).attr("action"),
|
||||||
'File upload successs, Data validation is in-progress',
|
type: "POST",
|
||||||
'Success');
|
data: formData,
|
||||||
$('.close').click()
|
processData: false, // Prevent jQuery from automatically processing the data
|
||||||
window.location.reload(true);
|
contentType: false, // Let jQuery handle the content type
|
||||||
} else if (response.code === 404 && response.dataStatus === false) {
|
headers: {
|
||||||
console.error('no data found', response);
|
// "Content-Type":"multipart/form-data",
|
||||||
// alert(response.message);
|
"X-Requested-With": "XMLHttpRequest"
|
||||||
toastr.error(response.message, 'Failed');
|
},
|
||||||
window.location.reload(true);
|
success: function(response) {
|
||||||
} else {
|
|
||||||
console.error('Something went wrong!');
|
console.log(response);
|
||||||
// alert('Something went wrong! Try later');
|
$('#uploadForm')[0].reset();
|
||||||
|
|
||||||
|
if (response.code === 200 && response.dataStatus === true && response
|
||||||
|
.data !== "") {
|
||||||
|
toastr.success(
|
||||||
|
'File upload successs, Data validation is in-progress',
|
||||||
|
'Success');
|
||||||
|
$('.close').click()
|
||||||
|
window.location.reload(true);
|
||||||
|
} else if (response.code === 404 && response.dataStatus === false) {
|
||||||
|
console.error('no data found', response);
|
||||||
|
// alert(response.message);
|
||||||
|
toastr.error(response.message, 'Failed');
|
||||||
|
window.location.reload(true);
|
||||||
|
} else {
|
||||||
|
console.error('Something went wrong!');
|
||||||
|
// alert('Something went wrong! Try later');
|
||||||
|
toastr.error('Something went wrong! Try later', 'Error');
|
||||||
|
window.location.reload(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
// Request failed, handle error
|
||||||
|
console.error("Request failed:", status, error);
|
||||||
toastr.error('Something went wrong! Try later', 'Error');
|
toastr.error('Something went wrong! Try later', 'Error');
|
||||||
|
$('#uploadForm')[0].reset();
|
||||||
window.location.reload(true);
|
window.location.reload(true);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
},
|
$('body').on('click', '.reload', function() {
|
||||||
error: function(xhr, status, error) {
|
|
||||||
// Request failed, handle error
|
console.log('status');
|
||||||
console.error("Request failed:", status, error);
|
status = this.getAttribute('data-id')
|
||||||
toastr.error('Something went wrong! Try later', 'Error');
|
console.log(status);
|
||||||
$('#uploadForm')[0].reset();
|
if (status == 'inprogress') {
|
||||||
window.location.reload(true);
|
window.location.reload(true);
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
})
|
|
||||||
|
|
||||||
$('body').on('click', '.reload', function() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
console.log('status');
|
$('#tickets-table').DataTable({
|
||||||
status = this.getAttribute('data-id')
|
|
||||||
console.log(status);
|
|
||||||
if (status == 'inprogress') {
|
|
||||||
window.location.reload(true);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right
|
||||||
|
|
||||||
$('#tickets-table').DataTable({
|
|
||||||
|
|
||||||
// dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// "<'row'<'col-sm-12'tr>>" +
|
|
||||||
// "<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
|
||||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
|
||||||
"<'row'<'col-sm-12'tr>>" +
|
"<'row'<'col-sm-12'tr>>" +
|
||||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>", lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
||||||
lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]],
|
buttons: [
|
||||||
buttons: [{
|
{
|
||||||
extend: 'collection',
|
extend: 'collection',
|
||||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||||
className: 'btn app-btn-secondary ',
|
className: 'btn app-btn-secondary mr-2',
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
extend: 'csv',
|
extend: 'csv',
|
||||||
text: '<i class="mdi mdi-file-delimited " ></i><span class=" btn-custom"> CSV </span>',
|
text: '<i class="mdi mdi-file-delimited " ></i><span class=" btn-custom"> CSV </span>',
|
||||||
title: 'Employee-Upload-List',
|
title: 'Employee-Upload-List',
|
||||||
className: 'app-btn-primary ',
|
className: 'app-btn-primary ',
|
||||||
exportOptions: {
|
exportOptions: {
|
||||||
columns: ':not(:last-child)'
|
columns: ':not(:last-child)'
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '<i class="mdi mdi-filter" ></i><span class=" btn-custom"> Filter </span>',
|
||||||
|
className: 'btn app-btn-primary mr-2',
|
||||||
|
action: function(e, dt, node, config) {
|
||||||
|
openPolicyFilterNav(1);
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
|
{
|
||||||
|
text: '<i class="mdi mdi-filter-remove"></i><span class="btn-custom"> Clear Filter </span>',
|
||||||
|
className: 'btn app-btn-info mr-2 hide-clear-filter',
|
||||||
|
action: function(e, dt, node, config) {
|
||||||
|
clearFilterData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
language: {
|
language: {
|
||||||
search: `
|
search: `
|
||||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
|
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
|
||||||
@ -507,27 +539,9 @@ $(document).ready(function() {
|
|||||||
searchPlaceholder: "Search",
|
searchPlaceholder: "Search",
|
||||||
emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
||||||
},
|
},
|
||||||
// "buttons": [{
|
paging: true,
|
||||||
// "extend": 'csv',
|
});
|
||||||
// "text": 'CSV',
|
|
||||||
// "title": 'Employee-Upload-List',
|
|
||||||
// "className": 'my_class',
|
|
||||||
// "exportOptions": {
|
|
||||||
// "columns": ':not(:last-child)'
|
|
||||||
// },
|
|
||||||
// }],
|
|
||||||
// "initComplete": function(settings, json) {
|
|
||||||
// $('.my_class').css({
|
|
||||||
// "position": "relative",
|
|
||||||
// "left": "79px"
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// language: {
|
|
||||||
// search: "_INPUT_",
|
|
||||||
// searchPlaceholder: "Search..."
|
|
||||||
// },
|
|
||||||
paging: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
@ -447,7 +447,7 @@
|
|||||||
}
|
}
|
||||||
$('.loader, .loader-mask').fadeOut();
|
$('.loader, .loader-mask').fadeOut();
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function (xhr, status, error) {
|
||||||
$('.loader, .loader-mask').fadeOut();
|
$('.loader, .loader-mask').fadeOut();
|
||||||
if (xhr.status === 400) {
|
if (xhr.status === 400) {
|
||||||
let response = JSON.parse(xhr.responseText);
|
let response = JSON.parse(xhr.responseText);
|
||||||
@ -500,8 +500,29 @@
|
|||||||
$('#content_section').val(data.content_section);
|
$('#content_section').val(data.content_section);
|
||||||
}
|
}
|
||||||
$('#heading').val(data.heading);
|
$('#heading').val(data.heading);
|
||||||
// $('#content').val(data.content);
|
|
||||||
// $('#notes').val(data.notes);
|
/**
|
||||||
|
* --------------------------------------------------------------------------
|
||||||
|
* FIX & EXPLANATION: Safely Loading HTML into the Editor
|
||||||
|
* --------------------------------------------------------------------------
|
||||||
|
* ISSUE:
|
||||||
|
* Loading HTML directly from an AJAX response into a webpage can be a
|
||||||
|
* major security risk (XSS). One might think `data.content` is unsafe.
|
||||||
|
*
|
||||||
|
* RESOLUTION:
|
||||||
|
* This operation is SAFE and CORRECT because of the "defense-in-depth"
|
||||||
|
* strategy we implemented in the `AppContentManagementController`.
|
||||||
|
*
|
||||||
|
* 1. The `data.content` and `data.notes` being received here have already
|
||||||
|
* been validated and sanitized on the server *before* they were ever
|
||||||
|
* saved to the database.
|
||||||
|
* 2. The controller's `sanitizeHtml()` function removed all dangerous tags
|
||||||
|
* (like <script>) and attributes (like `onclick`).
|
||||||
|
*
|
||||||
|
* Therefore, the HTML in `data.content` is trusted. We can safely assign it
|
||||||
|
* directly to the Jodit editor's `.value` property, which will correctly
|
||||||
|
* render the allowed HTML for the user to edit.
|
||||||
|
*/
|
||||||
if (content_editor) {
|
if (content_editor) {
|
||||||
content_editor.value = data.content || '';
|
content_editor.value = data.content || '';
|
||||||
}
|
}
|
||||||
@ -511,6 +532,24 @@
|
|||||||
openModal();
|
openModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simple client-side XSS pattern check to give instant feedback
|
||||||
|
function hasUnsafeHtml(html) {
|
||||||
|
if (!html) return false;
|
||||||
|
|
||||||
|
const decoded = $('<textarea/>').html(html).text().toLowerCase();
|
||||||
|
|
||||||
|
const patterns = [
|
||||||
|
/<\s*script/i,
|
||||||
|
/on\w+\s*=/i,
|
||||||
|
/javascript\s*:/i,
|
||||||
|
/vbscript\s*:/i,
|
||||||
|
/data\s*:\s*text\/html/i,
|
||||||
|
/expression\s*\(/i
|
||||||
|
];
|
||||||
|
|
||||||
|
return patterns.some(p => p.test(decoded));
|
||||||
|
}
|
||||||
|
|
||||||
$('#frontEndContentForm').on('submit', function(e) {
|
$('#frontEndContentForm').on('submit', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const form = this;
|
const form = this;
|
||||||
@ -527,6 +566,11 @@
|
|||||||
toastr.warning("Content is required", 'Warning');
|
toastr.warning("Content is required", 'Warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasUnsafeHtml(content_editor.value)) {
|
||||||
|
toastr.error("Content contains restricted tags or attributes. Script tags, iframes and event handlers (like onclick, onerror, onmouseover) are not allowed.", "Validation Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notes validation
|
// Notes validation
|
||||||
@ -535,6 +579,11 @@
|
|||||||
toastr.warning("Notes is required", 'Warning');
|
toastr.warning("Notes is required", 'Warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasUnsafeHtml(notes_editor.value)) {
|
||||||
|
toastr.error("Notes contains restricted tags or attributes. Script tags, iframes and event handlers (like onclick, onerror, onmouseover) are not allowed.", "Validation Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If valid, submit via AJAX
|
// If valid, submit via AJAX
|
||||||
|
|||||||
@ -1,4 +1,58 @@
|
|||||||
<style>
|
<style>
|
||||||
|
/* UI improvements for module cards - all 5 in one row on large screens */
|
||||||
|
.module-cards-row .section {
|
||||||
|
min-height: 60px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
.module-cards-row .section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
min-height: 40px;
|
||||||
|
cursor: default;
|
||||||
|
margin-left: 25px;
|
||||||
|
}
|
||||||
|
.module-cards-row .section-title input[type="checkbox"] {
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.module-cards-row .section-title .mdi {
|
||||||
|
margin-bottom: 0;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
/* Fallback: ensure 5 equal columns on large screens if row-cols not supported */
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.module-cards-row > .col {
|
||||||
|
flex: 0 0 20%;
|
||||||
|
max-width: 20%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checkbox alignment - vertically center with labels */
|
||||||
|
.checkbox-item {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
gap: 0.5rem;
|
||||||
|
min-height: 1.5rem;
|
||||||
|
}
|
||||||
|
.checkbox-item input[type="checkbox"] {
|
||||||
|
margin: 0 !important;
|
||||||
|
flex-shrink: 0;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.checkbox-item label {
|
||||||
|
margin: 0 !important;
|
||||||
|
flex: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-group {
|
||||||
|
margin-left: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
<?php foreach ($hr_access_data as $key => $value) {
|
<?php foreach ($hr_access_data as $key => $value) {
|
||||||
$key = $key + 1;
|
$key = $key + 1;
|
||||||
?>
|
?>
|
||||||
@ -72,6 +126,27 @@
|
|||||||
|
|
||||||
<?php foreach ($hr_access_data as $key => $value) {
|
<?php foreach ($hr_access_data as $key => $value) {
|
||||||
$key = $key + 1;
|
$key = $key + 1;
|
||||||
|
|
||||||
|
// Calculate data counts per user for toggling rules
|
||||||
|
$prePolicyCount = 0;
|
||||||
|
if (!empty($pre_policy_data)) {
|
||||||
|
foreach ($pre_policy_data as $p) {
|
||||||
|
if (!empty($value['pre_hr_id']) && $value['pre_branch_id'] === $p['branch_id']) {
|
||||||
|
$prePolicyCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$postPolicyCount = 0;
|
||||||
|
if (!empty($post_policy_data)) {
|
||||||
|
foreach ($post_policy_data as $p) {
|
||||||
|
if (!empty($value['post_hr_id']) && $value['post_branch_id'] === $p['branch_id']) {
|
||||||
|
$postPolicyCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$cdCount = !empty($post_cd_data) ? count($post_cd_data) : 0;
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!-- User 1 Card -->
|
<!-- User 1 Card -->
|
||||||
@ -99,12 +174,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="content-inner" style="background-color: #F5FFFF;">
|
<div class="content-inner" style="background-color: #F5FFFF;">
|
||||||
<div class="row">
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-5 module-cards-row">
|
||||||
<div class="col-md-6 col-lg-3">
|
<div class="col">
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<input type="checkbox" class="main-checkbox"
|
<input type="checkbox" class="main-checkbox"
|
||||||
onchange="toggleSection(this, '<?= 'user' . $key ?>-pre')" name="<?= 'user' . $key ?>_modules"
|
onchange="toggleSection(this, '<?= 'user' . $key ?>-pre', <?= $prePolicyCount ?>)" name="<?= 'user' . $key ?>_modules"
|
||||||
value="1" <?php if (in_array(1, $value['allowed_pre_modules'] ?? [])) {
|
value="1" <?php if (in_array(1, $value['allowed_pre_modules'] ?? [])) {
|
||||||
echo 'checked';
|
echo 'checked';
|
||||||
} ?>>
|
} ?>>
|
||||||
@ -161,11 +236,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6 col-lg-3">
|
<div class="col">
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<input type="checkbox" class="main-checkbox"
|
<input type="checkbox" class="main-checkbox"
|
||||||
onchange="toggleSection(this, '<?= 'user' . $key ?>-active')" name="<?= 'user' . $key ?>_modules"
|
onchange="toggleSection(this, '<?= 'user' . $key ?>-active', <?= $postPolicyCount ?>)" name="<?= 'user' . $key ?>_modules"
|
||||||
value="2" <?php if (in_array(2, $value['allowed_post_modules'])) {
|
value="2" <?php if (in_array(2, $value['allowed_post_modules'])) {
|
||||||
echo 'checked';
|
echo 'checked';
|
||||||
} ?>>
|
} ?>>
|
||||||
@ -236,11 +311,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6 col-lg-3">
|
<div class="col">
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<input type="checkbox" class="main-checkbox"
|
<input type="checkbox" class="main-checkbox"
|
||||||
onchange="toggleSection(this, '<?= 'user' . $key ?>-cd')" name="<?= 'user' . $key ?>_modules" value="3" <?php if (in_array(3, $value['allowed_post_modules'])) {
|
onchange="toggleSection(this, '<?= 'user' . $key ?>-cd', <?= $cdCount ?>)" name="<?= 'user' . $key ?>_modules" value="3" <?php if (in_array(3, $value['allowed_post_modules'])) {
|
||||||
echo 'checked';
|
echo 'checked';
|
||||||
} ?>>
|
} ?>>
|
||||||
<i class="mdi mdi-file mr-2"></i>CD statement
|
<i class="mdi mdi-file mr-2"></i>CD statement
|
||||||
@ -270,7 +345,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6 col-lg-3">
|
<div class="col">
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<input type="checkbox" class="main-checkbox" name="<?= 'user' . $key ?>_modules" value="4" <?php if (in_array(4, $value['allowed_post_modules'])) {
|
<input type="checkbox" class="main-checkbox" name="<?= 'user' . $key ?>_modules" value="4" <?php if (in_array(4, $value['allowed_post_modules'])) {
|
||||||
@ -280,6 +355,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col">
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-title">
|
||||||
|
<input type="checkbox" class="main-checkbox" name="<?= 'user' . $key ?>_modules" value="5" <?php if (in_array(5, $value['allowed_post_modules'] ?? [])) {
|
||||||
|
echo 'checked';
|
||||||
|
} ?>>
|
||||||
|
<i class="mdi mdi-chart-line mr-2"></i>Insights
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -302,4 +388,74 @@
|
|||||||
<?php } ?>
|
<?php } ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function toggleAccordion(header) {
|
||||||
|
const card = header.parentElement;
|
||||||
|
const content = card.querySelector('.card-content');
|
||||||
|
const icon = header.querySelector('.accordion-icon i');
|
||||||
|
|
||||||
|
// Close all other accordions
|
||||||
|
document.querySelectorAll('.user-card').forEach(otherCard => {
|
||||||
|
if (otherCard !== card) {
|
||||||
|
const otherContent = otherCard.querySelector('.card-content');
|
||||||
|
const otherIcon = otherCard.querySelector('.accordion-icon i');
|
||||||
|
if (otherContent && otherIcon) {
|
||||||
|
otherContent.classList.remove('active');
|
||||||
|
otherIcon.setAttribute('class', 'mdi mdi-chevron-down');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle current accordion
|
||||||
|
if (content && icon) {
|
||||||
|
content.classList.toggle('active');
|
||||||
|
if (content.classList.contains('active')) {
|
||||||
|
icon.setAttribute('class', 'mdi mdi-chevron-up');
|
||||||
|
} else {
|
||||||
|
icon.setAttribute('class', 'mdi mdi-chevron-down');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSection(checkbox, sectionId, dataCount) {
|
||||||
|
const section = document.getElementById(sectionId);
|
||||||
|
const checkboxes = section.querySelectorAll('input[type="checkbox"]');
|
||||||
|
|
||||||
|
// If checkbox is being unchecked, always allow without any restriction
|
||||||
|
if (!checkbox.checked) {
|
||||||
|
section.classList.add('hidden');
|
||||||
|
checkboxes.forEach(cb => {
|
||||||
|
cb.checked = false;
|
||||||
|
cb.disabled = true;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// From here, checkbox is being checked
|
||||||
|
// If there is no data, show warning and revert the checkbox state
|
||||||
|
if (!dataCount || dataCount === 0) {
|
||||||
|
checkbox.checked = false;
|
||||||
|
if (typeof toastr !== 'undefined' && toastr.warning) {
|
||||||
|
toastr.warning('No data available for this section.', 'Warning');
|
||||||
|
} else {
|
||||||
|
alert('No data available for this section.');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data is available and checkbox is being checked - show section and enable inner checkboxes
|
||||||
|
section.classList.remove('hidden');
|
||||||
|
checkboxes.forEach(cb => {
|
||||||
|
cb.disabled = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCheckedValues(name) {
|
||||||
|
const checkboxes = document.querySelectorAll(`input[name="${name}"]:checked`);
|
||||||
|
return Array.from(checkboxes).map(cb => cb.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
@ -1,12 +1,112 @@
|
|||||||
<style>
|
<style>
|
||||||
.table th,
|
.table th,
|
||||||
.table td {
|
.table td {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.dataTable tbody td {
|
||||||
|
padding: 4px 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.dataTable thead th {
|
||||||
|
padding: 4px 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataTables_filter {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-sidebar {
|
||||||
|
height: 100%;
|
||||||
|
width: 0;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1001;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
overflow-x: hidden;
|
||||||
|
transition: 0.5s;
|
||||||
|
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-sidebar-header {
|
||||||
|
padding: 15px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-sidebar-header h4 {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduce filter header height (this view only) */
|
||||||
|
#filter-sidebar .filter-sidebar-header {
|
||||||
|
padding: 10px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filter-sidebar .filter-sidebar-header .closebtn {
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-sidebar-header .closebtn {
|
||||||
|
font-size: 28px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-sidebar .closebtn:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-sidebar-content {
|
||||||
|
padding: 20px;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-sidebar-footer {
|
||||||
|
padding: 15px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
border-top: 1px solid #dee2e6;
|
||||||
|
flex-shrink: 0;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slightly smaller filter buttons (this view only) */
|
||||||
|
#filter-sidebar .filter-sidebar-footer {
|
||||||
|
padding: 12px 16px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filter-sidebar .filter-sidebar-footer .btn {
|
||||||
|
padding: 4px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Also shrink DataTables toolbar buttons on this page */
|
||||||
|
.dataTables_wrapper .dt-buttons .btn {
|
||||||
|
padding: 4px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataTables_wrapper .dt-buttons .btn .btn-custom {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-clear-filter{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
table.dataTable tbody td {
|
|
||||||
padding: 4px 4px !important;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -51,6 +151,105 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div id="filter-sidebar" class="filter-sidebar">
|
||||||
|
|
||||||
|
<div class="filter-sidebar-header">
|
||||||
|
<h4 class="m-0">Filter</h4>
|
||||||
|
<a href="javascript:void(0)" class="closebtn" onclick="closeFilterNav()">×</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filter-sidebar-content">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="form-row">
|
||||||
|
|
||||||
|
<input type="hidden" id="tab_type" value="1">
|
||||||
|
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label>Client<span class="text-danger"></span></label> <br/>
|
||||||
|
<select name="client_id" class="form-control" id="client_id_for_filter">
|
||||||
|
<option value="">Select</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label>Branch</label> <br />
|
||||||
|
<select name="client_branch_id" class="form-control" id="client_branch_id_for_filter">
|
||||||
|
<option value="0">Select</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label>Policy<span class="text-danger" id="policy_danger"></span></label> <br />
|
||||||
|
<select name="client_policy_id" class="form-control" id="policy_id_for_filter">
|
||||||
|
<option value="">Select</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-12">
|
||||||
|
<label>Event<span class="text-danger"></span></label> <br />
|
||||||
|
<select name="event_type" class="form-control" id="event_type_for_filter">
|
||||||
|
<option value="">Select</option>
|
||||||
|
<?php
|
||||||
|
if (isset($events) && count($events)) {
|
||||||
|
foreach ($events as $key => $action) {
|
||||||
|
echo "<option value='$key'>$action</option>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-12" id="date_div">
|
||||||
|
<label>Date<span class="text-danger"></span></label>
|
||||||
|
<div id="reportrange" class="form-control" style="background: #fff; cursor: pointer; padding: 5px 10px; border: 1px solid #ccc; width: 100%">
|
||||||
|
<i class="mdi mdi-calendar-blank"></i>
|
||||||
|
<span></span> <i class="mdi mdi-menu-down"></i>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" id="startDate">
|
||||||
|
<input type="hidden" id="endDate">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-12 hide_filter_input">
|
||||||
|
<label>Insurer/TPA Data<span class="text-danger"></span></label> <br />
|
||||||
|
<select name="insurer_or_tpa" class="form-control" id="insurer_or_tpa_for_filter">
|
||||||
|
<option value="">Select</option>
|
||||||
|
<?php
|
||||||
|
if (isset($insurer_or_tpa) && count($insurer_or_tpa)) {
|
||||||
|
foreach ($insurer_or_tpa as $key => $action) {
|
||||||
|
echo "<option value=" . $key . ">" . $action . "</option>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-12 hide_filter_input">
|
||||||
|
<label>Action<span class="text-danger"></span></label> <br />
|
||||||
|
<select name="action_type" class="form-control" id="action_type_for_filter">
|
||||||
|
<option value="">Select</option>
|
||||||
|
<?php
|
||||||
|
if (isset($import_or_export) && count($import_or_export)) {
|
||||||
|
foreach ($import_or_export as $key => $action) {
|
||||||
|
echo "<option value=" . $key . ">" . $action . "</option>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filter-sidebar-footer">
|
||||||
|
<a href="<?= base_url("/employee/upload"); ?>" class="btn btn-secondary" id="clear-filters">Clear</a>
|
||||||
|
<a href="<?= base_url("/employee/upload"); ?>" class="btn btn-primary" id="get_base_url" onclick="fetchFilterData(event); closeFilterNav();">Submit</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row" id="client_add">
|
<div class="row" id="client_add">
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
@ -92,9 +291,113 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
let tab = urlParams.get('tab_type');
|
||||||
|
|
||||||
|
console.log('Detected Tab Parameter:', tab);
|
||||||
|
|
||||||
|
// 1. Show/Hide Clear Filter Button
|
||||||
|
if (tab && tab !== "") {
|
||||||
|
$('.hide-clear-filter').show();
|
||||||
|
} else {
|
||||||
|
$('.hide-clear-filter').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. The Tab Switcher Logic
|
||||||
|
function activateTab(tabId) {
|
||||||
|
var targetLink = $('#' + tabId);
|
||||||
|
if (targetLink.length > 0) {
|
||||||
|
// Try Method A: Bootstrap 4/5 Standard
|
||||||
|
if (typeof bootstrap !== 'undefined' && bootstrap.Tab) {
|
||||||
|
var tabTrigger = new bootstrap.Tab(targetLink[0]);
|
||||||
|
tabTrigger.show();
|
||||||
|
} else if ($.fn.tab) {
|
||||||
|
targetLink.tab('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try Method B: Manual Fallback (Force Display)
|
||||||
|
$('.nav-link').removeClass('active active-tab').attr('aria-expanded', 'false');
|
||||||
|
targetLink.addClass('active active-tab').attr('aria-expanded', 'true');
|
||||||
|
|
||||||
|
var targetPane = targetLink.attr('href');
|
||||||
|
$('.tab-pane').removeClass('show active');
|
||||||
|
$(targetPane).addClass('show active');
|
||||||
|
|
||||||
|
console.log('Manually activated tab: ' + tabId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute with a slight delay to ensure includes are loaded
|
||||||
|
setTimeout(function() {
|
||||||
|
if (tab === "2" || tab === "kyc_tab") {
|
||||||
|
activateTab('kyc_tab');
|
||||||
|
} else if (tab === "3" || tab === "hr_tab") {
|
||||||
|
activateTab('hr_tab');
|
||||||
|
} else {
|
||||||
|
activateTab('general_tab');
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
// 3. Keep the Sidebar input updated for future filters
|
||||||
|
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||||
|
$('#tab_type').val($(e.target).attr('id'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
|
||||||
|
$('#client_id_for_filter').select2();
|
||||||
|
$('#client_branch_id_for_filter').select2();
|
||||||
|
$('#policy_id_for_filter').select2();
|
||||||
|
|
||||||
|
$('#client_id_for_filter').on('change', function() {
|
||||||
|
|
||||||
|
let client_id = $(this).val();
|
||||||
|
console.log('client_id_for_filter', client_id);
|
||||||
|
|
||||||
|
if(branch_list != '') {
|
||||||
|
// console.log(branch_list[client_id]);
|
||||||
|
let data = branch_list[client_id];
|
||||||
|
appendBranchForFilter(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#client_branch_id_for_filter').on('change', function() {
|
||||||
|
|
||||||
|
let branch_id = $(this).val();
|
||||||
|
console.log("client_branch_id_for_filter", branch_id);
|
||||||
|
|
||||||
|
if(policy_list != '' && branch_id != '') {
|
||||||
|
let data = policy_list[branch_id];
|
||||||
|
appendPoliciesForFilter(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
/* Modified by Gemini Code Assist */
|
||||||
|
function openPolicyFilterNav(input) {
|
||||||
|
|
||||||
|
if(input == 2){
|
||||||
|
$('.hide_filter_input').show();
|
||||||
|
}else{
|
||||||
|
$('.hide_filter_input').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("filter-sidebar").style.width = "350px";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modified by Gemini Code Assist */
|
||||||
|
function closeFilterNav() {
|
||||||
|
document.getElementById("filter-sidebar").style.width = "0";
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
if (window.location.hash === '#KYC-DOC-tab') {
|
if (window.location.hash === '#KYC-DOC-tab') {
|
||||||
@ -115,4 +418,181 @@
|
|||||||
.toLowerCase() // convert everything to lowercase
|
.toLowerCase() // convert everything to lowercase
|
||||||
.replace(/\b\w/g, char => char.toUpperCase()); // capitalize each word
|
.replace(/\b\w/g, char => char.toUpperCase()); // capitalize each word
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function appendBranchForFilter(data) {
|
||||||
|
|
||||||
|
$('#client_branch_id_for_filter').empty();
|
||||||
|
$('#client_branch_id_for_filter').append($('<option>', {
|
||||||
|
value: '0',
|
||||||
|
text: 'Select'
|
||||||
|
}));
|
||||||
|
$.each(data, function(index, item) {
|
||||||
|
var option = $('<option>', {
|
||||||
|
value: item.id,
|
||||||
|
text: item.branch_name
|
||||||
|
});
|
||||||
|
if (client_branch_id_param == item.id) {
|
||||||
|
option.attr('selected', true);
|
||||||
|
}
|
||||||
|
$('#client_branch_id_for_filter').append(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendPoliciesForFilter(data) {
|
||||||
|
|
||||||
|
$('#policy_id_for_filter').empty();
|
||||||
|
$('#policy_id_for_filter').append($('<option>', {
|
||||||
|
value: '0',
|
||||||
|
text: 'Select',
|
||||||
|
selected: true
|
||||||
|
}));
|
||||||
|
|
||||||
|
$.each(data, function(index, item) {
|
||||||
|
|
||||||
|
var option = $('<option>', {
|
||||||
|
value: item.id,
|
||||||
|
text:`${item.policy_type ?? ''} - ${item.policy_no ?? ''}`
|
||||||
|
});
|
||||||
|
if (client_policy_param == item.id) {
|
||||||
|
option.attr('selected', true);
|
||||||
|
}
|
||||||
|
$('#policy_id_for_filter').append(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||||
|
|
||||||
|
var activeTabId = $(e.target).attr('id'); // Gets the ID of the clicked tab
|
||||||
|
var activeTabText = $(e.target).text().trim(); // Gets the text inside the tab
|
||||||
|
|
||||||
|
console.log("Tab Changed!");
|
||||||
|
console.log("Active Tab ID: " + activeTabId);
|
||||||
|
console.log("Active Tab Name: " + activeTabText);
|
||||||
|
|
||||||
|
if(activeTabId == 'kyc_tab'){
|
||||||
|
$('#tab_type').val(2)
|
||||||
|
$('.hide_filter_input').show();
|
||||||
|
}else{
|
||||||
|
$('#tab_type').val(1)
|
||||||
|
$('.hide_filter_input').hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function fetchFilterData(event)
|
||||||
|
{
|
||||||
|
event.preventDefault(); // Prevent default action (navigation)
|
||||||
|
|
||||||
|
// 1. Get the current active tab type (from your hidden input)
|
||||||
|
var tab_type = $('#tab_type').val();
|
||||||
|
|
||||||
|
// 2. Get data using the CORRECT IDs from your HTML sidebar
|
||||||
|
var client_id = $('#client_id_for_filter').val();
|
||||||
|
var branch_id = $('#client_branch_id_for_filter').val();
|
||||||
|
var policy_id = $('#policy_id_for_filter').val();
|
||||||
|
var event_type = $('#event_type_for_filter').val();
|
||||||
|
var insurer_or_tpa = $('#insurer_or_tpa_for_filter').val();
|
||||||
|
var action_type = $('#action_type').val(); // This ID was correct
|
||||||
|
|
||||||
|
// Note: If you have date pickers elsewhere, keep these.
|
||||||
|
// Otherwise, ensure they exist in your UI.
|
||||||
|
var start_date = $('#startDate').val() || '';
|
||||||
|
var end_date = $('#endDate').val() || '';
|
||||||
|
|
||||||
|
// 3. Construct the Query Parameters object
|
||||||
|
var queryParams = {
|
||||||
|
tab_type: tab_type,
|
||||||
|
client_id: client_id,
|
||||||
|
branch_id: branch_id,
|
||||||
|
policy_id: policy_id,
|
||||||
|
event_type: event_type,
|
||||||
|
insurer_or_tpa: insurer_or_tpa,
|
||||||
|
action_type: action_type,
|
||||||
|
start_date: start_date,
|
||||||
|
end_date: end_date
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("Filters applied:", queryParams);
|
||||||
|
|
||||||
|
// 4. Build the Query String and Redirect
|
||||||
|
const queryString = new URLSearchParams(queryParams).toString();
|
||||||
|
const baseUrl = $('#get_base_url').attr('href');
|
||||||
|
console.log("queryString to:", queryString);
|
||||||
|
console.log("baseUrl to:", baseUrl);
|
||||||
|
|
||||||
|
const apiURL = baseUrl + "?" + queryString;
|
||||||
|
|
||||||
|
console.log("Redirecting to:", apiURL);
|
||||||
|
window.location.href = apiURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearFilterData(){
|
||||||
|
|
||||||
|
const url = $('#get_base_url').attr('href');
|
||||||
|
console.log("url to:", url);
|
||||||
|
|
||||||
|
window.location.href = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
// 1. Check if dates exist in URL, otherwise leave them null/empty
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const startDateParam = urlParams.get('start_date');
|
||||||
|
const endDateParam = urlParams.get('end_date');
|
||||||
|
|
||||||
|
var start = startDateParam ? moment(startDateParam, 'DD/MM/YYYY') : null;
|
||||||
|
var end = endDateParam ? moment(endDateParam, 'DD/MM/YYYY') : null;
|
||||||
|
|
||||||
|
function cb(start, end) {
|
||||||
|
if (start && end) {
|
||||||
|
$('#reportrange span').html(start.format('D/MM/YYYY') + ' - ' + end.format('D/MM/YYYY'));
|
||||||
|
$('#startDate').val(start.format('DD/MM/YYYY'));
|
||||||
|
$('#endDate').val(end.format('DD/MM/YYYY'));
|
||||||
|
} else {
|
||||||
|
// Display a placeholder if no date is selected
|
||||||
|
$('#reportrange span').html('Select Date Range');
|
||||||
|
$('#startDate').val('');
|
||||||
|
$('#endDate').val('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#reportrange').daterangepicker({
|
||||||
|
autoUpdateInput: false, // CRITICAL: Prevents the picker from forcing a value
|
||||||
|
startDate: start || moment().subtract(29, 'days'),
|
||||||
|
endDate: end || moment(),
|
||||||
|
locale: {
|
||||||
|
cancelLabel: 'Clear',
|
||||||
|
format: 'DD/MM/YYYY'
|
||||||
|
},
|
||||||
|
ranges: {
|
||||||
|
'Today': [moment(), moment()],
|
||||||
|
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
|
||||||
|
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
|
||||||
|
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
|
||||||
|
'This Month': [moment().startOf('month'), moment().endOf('month')],
|
||||||
|
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle the "Apply" button click
|
||||||
|
$('#reportrange').on('apply.daterangepicker', function(ev, picker) {
|
||||||
|
cb(picker.startDate, picker.endDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle the "Cancel/Clear" button click
|
||||||
|
$('#reportrange').on('cancel.daterangepicker', function(ev, picker) {
|
||||||
|
cb(null, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Run once on load
|
||||||
|
cb(start, end);
|
||||||
|
|
||||||
|
$('#clear-filters').on('click', function() {
|
||||||
|
// ... (your existing dropdown resets) ...
|
||||||
|
cb(null, null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -488,7 +488,7 @@
|
|||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
$('.loader').fadeOut();
|
$('.loader').fadeOut();
|
||||||
$('.loader-mask').delay(350).fadeOut('slow');
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
toastr.warning('Something Wrong!', 'warning');
|
toastr.warning('Something Wrong!', 'Warning');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
},
|
},
|
||||||
complete: function() {
|
complete: function() {
|
||||||
|
|||||||
@ -351,6 +351,24 @@
|
|||||||
.hr-item:nth-child(4) { animation-delay: 0.2s; }
|
.hr-item:nth-child(4) { animation-delay: 0.2s; }
|
||||||
.hr-item:nth-child(5) { animation-delay: 0.25s; }
|
.hr-item:nth-child(5) { animation-delay: 0.25s; }
|
||||||
|
|
||||||
|
/* Reduce gap between accordion and cards (this tab only) */
|
||||||
|
#KYC-DOC-tab #accordion.mb-3 {
|
||||||
|
margin-bottom: 0.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#KYC-DOC-tab #accordion .card.mb-1 {
|
||||||
|
margin-bottom: 0.35rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#KYC-DOC-tab #accordion .card-body {
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#KYC-DOC-tab #file_list {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="tab-pane fade" id="KYC-DOC-tab">
|
<div class="tab-pane fade" id="KYC-DOC-tab">
|
||||||
@ -360,7 +378,7 @@
|
|||||||
<div class="card mb-1">
|
<div class="card mb-1">
|
||||||
|
|
||||||
<h4 class="m-1">
|
<h4 class="m-1">
|
||||||
<span>Filters</span>
|
<!-- <span>Filters</span> -->
|
||||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne" aria-expanded="true">
|
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne" aria-expanded="true">
|
||||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -2330,13 +2330,13 @@ body[data-sidebar-size="condensed"] .footer {
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
<li class="li-seperate" id="user-manual-li">
|
<!-- <li class="li-seperate" id="user-manual-li">
|
||||||
<a id="user_manual" href="<?= base_url("autobookstackLogin") ?>" class="waves-effect img-inactive" target="_blank" rel="noopener noreferrer">
|
<a id="user_manual" href="<?= base_url("autobookstackLogin") ?>" class="waves-effect img-inactive" target="_blank" rel="noopener noreferrer">
|
||||||
<img
|
<img
|
||||||
src="<?= base_url() . "public"; ?>/assets/images/user_manual_sb.png" alt="Logo" height="20">
|
src="<?= base_url() . "public"; ?>/assets/images/user_manual_sb.png" alt="Logo" height="20">
|
||||||
<span>User Manual</span>
|
<span>User Manual</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li> -->
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<!-- End Sidebar -->
|
<!-- End Sidebar -->
|
||||||
|
|||||||
@ -102,8 +102,12 @@
|
|||||||
border-width: 1px !important;
|
border-width: 1px !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<?php
|
||||||
|
$isLeadEditEb = isset($lead_edit_data) && ! empty($lead_edit_data);
|
||||||
|
$ebSubtitle = $isLeadEditEb ? 'Edit Opportunities - (EB)' : 'Add Opportunities - (EB)';
|
||||||
|
?>
|
||||||
<script>
|
<script>
|
||||||
var pageSubTitle = 'Add Opportunity';
|
var pageSubTitle = '<?= $ebSubtitle ?>';
|
||||||
var pageBackButton = '<a href="<?= base_url('leads/list') ?>" data-toggle="tooltip" data-placement="top" title="Back"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
var pageBackButton = '<a href="<?= base_url('leads/list') ?>" data-toggle="tooltip" data-placement="top" title="Back"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -298,26 +302,26 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-4">
|
<div class="form-group col-md-4">
|
||||||
<label for="client_branch">Branch Name<span class="text-danger">*</span></label>
|
<label for="branch_name">Branch Name<span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="branch_name" name="branch_name"
|
<input type="text" class="form-control" id="branch_name" name="branch_name"
|
||||||
placeholder="Enter Branch Name" required>
|
placeholder="Enter Branch Name" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-4">
|
<div class="form-group col-md-4">
|
||||||
<label for="client_branch">Branch Code<span class="text-danger">*</span></label>
|
<label for="branch_code">Branch Code<span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="branch_code" name="branch_code"
|
<input type="text" class="form-control" id="branch_code" name="branch_code"
|
||||||
placeholder="Enter Branch Code" required>
|
placeholder="Enter Branch Code" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-4 renewalFields">
|
<div class="form-group col-md-4 renewalFields">
|
||||||
<label for="contact_person_summary">Contact Person</label>
|
<label for="contact_person_summary">Contact Person<span class="text-danger">*</span></label>
|
||||||
<select class="form-control" name="contact_person_summary" id="contact_person_summary">
|
<select class="form-control" name="contact_person_summary" id="contact_person_summary">
|
||||||
<option value="">Select a Contact</option>
|
<option value="">Select a Contact</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-4 freshFields">
|
<div class="form-group col-md-4 freshFields">
|
||||||
<label for="client_branch">Contact Person Name<span class="text-danger">*</span></label>
|
<label for="contact_person_name">Contact Person Name<span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="contact_person_name" name="contact_person_name"
|
<input type="text" class="form-control" id="contact_person_name" name="contact_person_name"
|
||||||
placeholder="Enter Contact Name" required>
|
placeholder="Enter Contact Name" required>
|
||||||
</div>
|
</div>
|
||||||
@ -397,7 +401,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-4">
|
<div class="form-group col-md-4">
|
||||||
<label for="">Next Reminder Date<span class="text-danger"></span></label>
|
<label for="next_reminder_date">Next Reminder Date<span class="text-danger"></span></label>
|
||||||
<div class="input-icon">
|
<div class="input-icon">
|
||||||
<input type="text" class="form-control" id="next_reminder_date" name="next_reminder_date" placeholder="DD/MM/YYYY">
|
<input type="text" class="form-control" id="next_reminder_date" name="next_reminder_date" placeholder="DD/MM/YYYY">
|
||||||
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
||||||
@ -773,14 +777,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
hide_list_show_add();
|
hide_list_show_add();
|
||||||
|
|
||||||
var page_title = 'Edit Opportunity';
|
pageSubTitle = 'Edit Opportunities - (EB)';
|
||||||
var pageSubTitle = 'Edit Opportunity';
|
pageBackButton = '<a href="<?= base_url('leads/list') ?>" data-toggle="tooltip" data-placement="top" title="Back"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
||||||
var pageBackButton = '<a href="<?= base_url('leads/list') ?>" data-toggle="tooltip" data-placement="top" title="Back"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
|
||||||
|
|
||||||
$('#page_title').text(page_title);
|
|
||||||
$('#leads_primarykey').val(res.data.id);
|
$('#leads_primarykey').val(res.data.id);
|
||||||
$('#actual_lead_id').val(res.data.actual_lead_id || 0);
|
$('#actual_lead_id').val(res.data.actual_lead_id || 0);
|
||||||
$('#policy_start_date').val(res.data.policy_end_date);
|
$('#policy_start_date').val(res.data.policy_end_date);
|
||||||
@ -1242,7 +1243,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-4">
|
<div class="form-group col-md-4">
|
||||||
<label for="">Date of Commencement <span class="text-danger"></span></label>
|
<label for="policy_start_date">Date of Commencement <span class="text-danger">*</span></label>
|
||||||
<div class="input-icon">
|
<div class="input-icon">
|
||||||
<input type="text" class="form-control policy_start_date" id="policy_start_date_${increment}" name="policy_start_date[]" placeholder="Enter DOC" onchange="calculatePolicyMetrics(this)">
|
<input type="text" class="form-control policy_start_date" id="policy_start_date_${increment}" name="policy_start_date[]" placeholder="Enter DOC" onchange="calculatePolicyMetrics(this)">
|
||||||
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
||||||
@ -1251,7 +1252,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-4">
|
<div class="form-group col-md-4">
|
||||||
<label for="">Date of Expiry <span class="text-danger"></span></label>
|
<label for="policy_end_date">Date of Expiry <span class="text-danger">*</span></label>
|
||||||
<div class="input-icon">
|
<div class="input-icon">
|
||||||
<input type="text" class="form-control policy_end_date" id="policy_end_date_${increment}" name="policy_end_date[]" placeholder="Enter DOE">
|
<input type="text" class="form-control policy_end_date" id="policy_end_date_${increment}" name="policy_end_date[]" placeholder="Enter DOE">
|
||||||
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
<i class="mdi mdi-calendar-blank-outline additional-icon"></i>
|
||||||
@ -1617,8 +1618,19 @@
|
|||||||
console.log('--- End of calculatePolicyMetrics ---\n');
|
console.log('--- End of calculatePolicyMetrics ---\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(document).on("input", "#incept_no_of_lives", function () {
|
||||||
|
if (this.value.length > 10) {
|
||||||
|
this.value = this.value.slice(0, 10); // max 10 digits
|
||||||
|
}
|
||||||
|
this.value = this.value.replace(/\D/g, '');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
$(document).on("input", "#incept_emp_count, #incept_dept_count, #renewal_emp_count, #renewal_dept_count, #exp_emp_count, #exp_dept_count", function() {
|
$(document).on("input", "#incept_emp_count, #incept_dept_count, #renewal_emp_count, #renewal_dept_count, #exp_emp_count, #exp_dept_count", function() {
|
||||||
|
if (this.value.length > 10) {
|
||||||
|
this.value = this.value.slice(0, 10); // max 10 digits
|
||||||
|
}
|
||||||
|
this.value = this.value.replace(/\D/g, '');
|
||||||
calculateTotalLives(this);
|
calculateTotalLives(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -2457,14 +2469,24 @@
|
|||||||
$('#client_name').val(actual_lead_client_details.company_name || '');
|
$('#client_name').val(actual_lead_client_details.company_name || '');
|
||||||
|
|
||||||
$('#gst').val(actual_lead_client_details.gst_number || '');
|
$('#gst').val(actual_lead_client_details.gst_number || '');
|
||||||
|
|
||||||
// 🔥 Important:
|
existingShortName = actual_lead_client_details.short_name || '';
|
||||||
// Only auto-generate short name IF empty (avoid overwrite in edit)
|
if (existingShortName) {
|
||||||
if (!$('#client_short_name').val()) {
|
// ── Short name EXISTS — just populate and validate ────────
|
||||||
$('#client_name').trigger('input');
|
$('#client_short_name').val(existingShortName);
|
||||||
|
// validateInput($('#client_short_name')[0], 'clients', 'short_name');
|
||||||
} else {
|
} else {
|
||||||
// Run duplicate validation once
|
// Auto-generate short name from company name
|
||||||
validateInput($('#client_short_name')[0], "clients", "short_name");
|
// Use a small delay to ensure DOM is ready
|
||||||
|
setTimeout(function () {
|
||||||
|
let baseName = actual_lead_client_details.company_name
|
||||||
|
.trim()
|
||||||
|
.substring(0, 10)
|
||||||
|
.replace(/\s+/g, '')
|
||||||
|
.toUpperCase();
|
||||||
|
|
||||||
|
makeUniqueShortName(baseName);
|
||||||
|
}, 300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -47,8 +47,12 @@
|
|||||||
border-width: 1px !important;
|
border-width: 1px !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<?php
|
||||||
|
$isLeadEditNonEb = isset($lead_edit_data) && ! empty($lead_edit_data);
|
||||||
|
$nonEbSubtitle = $isLeadEditNonEb ? 'Edit Opportunities - (Non EB)' : 'Add Opportunities - (Non EB)';
|
||||||
|
?>
|
||||||
<script>
|
<script>
|
||||||
var pageSubTitle = 'Add Opportunity';
|
var pageSubTitle = '<?= $nonEbSubtitle ?>';
|
||||||
var pageBackButton = '<a href="<?= base_url('leads/list') ?>" data-toggle="tooltip" data-placement="top" title="Back"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
var pageBackButton = '<a href="<?= base_url('leads/list') ?>" data-toggle="tooltip" data-placement="top" title="Back"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -1315,14 +1319,24 @@ var pageBackButton = '<a href="<?= base_url('leads/list') ?>" data-toggle="toolt
|
|||||||
$('#client_name').val(actual_lead_client_details.company_name || '');
|
$('#client_name').val(actual_lead_client_details.company_name || '');
|
||||||
|
|
||||||
$('#gst').val(actual_lead_client_details.gst_number || '');
|
$('#gst').val(actual_lead_client_details.gst_number || '');
|
||||||
|
existingShortName = actual_lead_client_details.short_name || '';
|
||||||
|
if (existingShortName) {
|
||||||
|
// ── Short name EXISTS — just populate and validate ────────
|
||||||
|
$('#client_short_name').val(existingShortName);
|
||||||
|
// validateInput($('#client_short_name')[0], 'clients', 'short_name');
|
||||||
|
|
||||||
// 🔥 Important:
|
|
||||||
// Only auto-generate short name IF empty (avoid overwrite in edit)
|
|
||||||
if (!$('#client_short_name').val()) {
|
|
||||||
$('#client_name').trigger('input');
|
|
||||||
} else {
|
} else {
|
||||||
// Run duplicate validation once
|
// Auto-generate short name from company name
|
||||||
validateInput($('#client_short_name')[0], "clients", "short_name");
|
// Use a small delay to ensure DOM is ready
|
||||||
|
setTimeout(function () {
|
||||||
|
let baseName = actual_lead_client_details.company_name
|
||||||
|
.trim()
|
||||||
|
.substring(0, 10)
|
||||||
|
.replace(/\s+/g, '')
|
||||||
|
.toUpperCase();
|
||||||
|
|
||||||
|
makeUniqueShortName(baseName);
|
||||||
|
}, 300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,16 +8,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body" style="overflow-y: auto;height: 90vh;">
|
<div class="modal-body" style="overflow-y: auto;height: 90vh;">
|
||||||
<form class="parsley-examples" method="post" id="client_form" enctype="multipart/form-data">
|
<form class="parsley-examples" method="post" id="client_form" enctype="multipart/form-data">
|
||||||
|
<input type="hidden" name="from_modal" value="1">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
<label for="client_name">Client Name<span class="text-danger">*</span></label>
|
<label for="client_name_for_modal">Client Name<span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="client_name" name="client_name" required>
|
<input type="text" class="form-control" id="client_name_for_modal" name="client_name" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
<label for="short_name">Client Short name<span class="text-danger">*</span></label>
|
<label for="client_short_name_for_modal">Client Short name<span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="short_name" name="short_name" onkeyup="validateInputForClient(this, 'clients', 'short_name')" required>
|
<input type="text" class="form-control" id="client_short_name_for_modal" name="short_name" onkeyup="validateInputForClient(this, 'clients', 'short_name')" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
@ -31,7 +32,7 @@
|
|||||||
<!-- Client Type -->
|
<!-- Client Type -->
|
||||||
<div class="form-row clienttypediv" style="display: none;">
|
<div class="form-row clienttypediv" style="display: none;">
|
||||||
|
|
||||||
<div class="form-group col-md-6">
|
<!-- <div class="form-group col-md-6">
|
||||||
<label for="dob">Date of Birth<span class="text-danger">*</span></label>
|
<label for="dob">Date of Birth<span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="dob" placeholder="DD/MM/YYYY" name="dob" >
|
<input type="text" class="form-control" id="dob" placeholder="DD/MM/YYYY" name="dob" >
|
||||||
</div>
|
</div>
|
||||||
@ -40,16 +41,16 @@
|
|||||||
<label for="cost_center">Mobile<span class="text-danger"></span></label>
|
<label for="cost_center">Mobile<span class="text-danger"></span></label>
|
||||||
<input type="text" class="form-control" id="phone" placeholder="Enter Mobile Number" maxlength="10" name="phone"
|
<input type="text" class="form-control" id="phone" placeholder="Enter Mobile Number" maxlength="10" name="phone"
|
||||||
onkeypress="return onlyNumbers(event)" onchange="validateInputForClient(this, 'clients', 'phone')">
|
onkeypress="return onlyNumbers(event)" onchange="validateInputForClient(this, 'clients', 'phone')">
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<div class="form-group col-md-12">
|
<!-- <div class="form-group col-md-12">
|
||||||
<label for="email2">Email<span class="text-danger">*</span></label>
|
<label for="email2">Email<span class="text-danger">*</span></label>
|
||||||
<input type="email" class="form-control" id="email2" placeholder="Enter Email" name="email2">
|
<input type="email" class="form-control" id="email2" placeholder="Enter Email" name="email2">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
<label for="aadhar">Aadhar</label>
|
<label for="aadhar">Aadhar</label>
|
||||||
<input type="text" class="form-control" id="aadhar" placeholder="Enter Aadher No" name="aadhar" maxlength="12" onchange="validateInputForClient(this, 'clients', 'aadhar')">
|
<input type="text" class="form-control" id="aadhar" placeholder="Enter Aadher No" name="aadhar" maxlength="12" onchange="validateInputForClient(this, 'clients', 'aadhar')">
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -57,8 +58,8 @@
|
|||||||
<div class="form-row branchdiv">
|
<div class="form-row branchdiv">
|
||||||
|
|
||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
<label for="short_name">Entity Type<span class="text-danger"></span></label>
|
<label for="short_name">Entity Type<span class="text-danger">*</span></label>
|
||||||
<select class="form-control" id="entity_type_id_for_client" name="entity_type_id">
|
<select class="form-control" id="entity_type_id_for_client" name="entity_type_id" required>
|
||||||
<option value="" selected>Select Entity</option>
|
<option value="" selected>Select Entity</option>
|
||||||
<?php
|
<?php
|
||||||
if (isset($entity) && count($entity)) {
|
if (isset($entity) && count($entity)) {
|
||||||
@ -87,7 +88,7 @@
|
|||||||
|
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label for="branch_code">Mobile<span class="text-danger">*</span></label>
|
<label for="branch_code">Mobile<span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="mobile" maxlength="10" name="mobile" onkeypress="return onlyNumbers(event)" required>
|
<input type="text" class="form-control" id="mobile" maxlength="10" name="mobile" onkeypress="return onlyNumbers(event)" oninput="this.value = this.value.replace(/[^0-9]/g, '');" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
<label for="email">Email<span class="text-danger">*</span></label>
|
<label for="email">Email<span class="text-danger">*</span></label>
|
||||||
@ -243,6 +244,10 @@
|
|||||||
appendClients(res.clients, res.client_id);
|
appendClients(res.clients, res.client_id);
|
||||||
appendBranch(res.branches, res.branch_id);
|
appendBranch(res.branches, res.branch_id);
|
||||||
|
|
||||||
|
$('#client_name').val($('#client_name_for_modal').val());
|
||||||
|
$('#client_short_name').val($('#client_short_name_for_modal').val());
|
||||||
|
$('#contact_person_name').val(res.data.name);
|
||||||
|
|
||||||
$('#gst').val(res.data.gst);
|
$('#gst').val(res.data.gst);
|
||||||
$('#branch_name').val(res.data.branch_name);
|
$('#branch_name').val(res.data.branch_name);
|
||||||
$('#branch_code').val(res.data.branch_code);
|
$('#branch_code').val(res.data.branch_code);
|
||||||
@ -285,11 +290,87 @@
|
|||||||
error: function (xhr, status, error) {
|
error: function (xhr, status, error) {
|
||||||
console.error(xhr.responseText);
|
console.error(xhr.responseText);
|
||||||
console.error(status, error);
|
console.error(status, error);
|
||||||
isClientFormSubmitting = false;
|
setTimeout(function() {
|
||||||
$('.loader').fadeOut();
|
$('.loader').fadeOut();
|
||||||
$('.loader-mask').delay(350).fadeOut('slow');
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
|
// $('#client_add_modal_submit_btn').prop('disabled', false);
|
||||||
|
if (xhr.status === 400) {
|
||||||
|
let response = JSON.parse(xhr.responseText);
|
||||||
|
let errorMessages = "";
|
||||||
|
let seenMessages = []; // Array to store unique messages
|
||||||
|
if (response.errors) {
|
||||||
|
$.each(response.errors, function (field, message) {
|
||||||
|
if (!seenMessages.includes(message)) {
|
||||||
|
errorMessages += `• ${message}<br>`;
|
||||||
|
seenMessages.push(message); // Mark this message as "seen"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toastr.error(errorMessages, 'Validation Error', { "allowHtml": true });
|
||||||
|
} else {
|
||||||
|
toastr.warning(response.message || 'Validation failed', 'Warning');
|
||||||
|
}
|
||||||
|
} else if (xhr.status === 403) {
|
||||||
|
let response = JSON.parse(xhr.responseText);
|
||||||
|
toastr.error(response.message, 'Security Policy');
|
||||||
|
} else if (xhr.status === 404) {
|
||||||
|
toastr.warning('Resource not found', 'Warning');
|
||||||
|
} else if (xhr.status === 500) {
|
||||||
|
toastr.error('Something went wrong . Please try again later.', 'Server Error');
|
||||||
|
} else {
|
||||||
|
toastr.error('An unexpected error occurred. Please try again later.', 'Error');
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#client_name_for_modal').on('input', function() {
|
||||||
|
clearTimeout(shortNameTimer);
|
||||||
|
shortNameTimer = setTimeout(generateShortNameModal, 200);
|
||||||
|
});
|
||||||
|
|
||||||
|
function generateShortNameModal() {
|
||||||
|
let clientInput = $("#client_name_for_modal");
|
||||||
|
let shortInput = $("#client_short_name_for_modal");
|
||||||
|
let newClientName = clientInput.val().trim();
|
||||||
|
|
||||||
|
console.log(`LN : NEW - ${newClientName}`);
|
||||||
|
if (newClientName.length == 0) {
|
||||||
|
shortInput.val('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let shortName = newClientName.substring(0, 10).replace(/\s+/g, '').toUpperCase();
|
||||||
|
console.log(`LN : NEW - ${shortName}`);
|
||||||
|
makeUniqueShortNameModal(shortName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeUniqueShortNameModal(baseName) {
|
||||||
|
let input = $("#client_short_name_for_modal")[0];
|
||||||
|
|
||||||
|
checkDuplicateTableFieldValue("clients", "short_name", baseName, function(isDuplicate) {
|
||||||
|
if (isDuplicate) {
|
||||||
|
let counter = 1;
|
||||||
|
function tryNext() {
|
||||||
|
let padded = String(counter).padStart(3, '0'); // 001, 002, 003
|
||||||
|
let newName = baseName + padded;
|
||||||
|
|
||||||
|
checkDuplicateTableFieldValue("clients", "short_name", newName, function(exists) {
|
||||||
|
if (exists) {
|
||||||
|
counter++;
|
||||||
|
tryNext();
|
||||||
|
} else {
|
||||||
|
$("#client_short_name_for_modal").val(newName);
|
||||||
|
validateInput(input, "clients", "short_name");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
tryNext();
|
||||||
|
} else {
|
||||||
|
$("#client_short_name_for_modal").val(baseName);
|
||||||
|
validateInput(input, "clients", "short_name");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -156,7 +156,7 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<!-- <div class="row">
|
||||||
<div class="col-6" style="align-self: center;">
|
<div class="col-6" style="align-self: center;">
|
||||||
<h4 style="position: relative;">Add Endorsement</h4>
|
<h4 style="position: relative;">Add Endorsement</h4>
|
||||||
</div>
|
</div>
|
||||||
@ -164,7 +164,7 @@
|
|||||||
<a href="<?= base_url('policy_tranction/endorsement/list') ?>" id="btnAdd" class="btn btn-primary waves-effect waves-light"
|
<a href="<?= base_url('policy_tranction/endorsement/list') ?>" id="btnAdd" class="btn btn-primary waves-effect waves-light"
|
||||||
>Back</a>
|
>Back</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
<form role="form" class="parsley-examples" method="post" id="endorsement_form_id" enctype="multipart/form-data">
|
<form role="form" class="parsley-examples" method="post" id="endorsement_form_id" enctype="multipart/form-data">
|
||||||
|
|
||||||
<input type="hidden" name="id" id="policy_tranction_primarykey">
|
<input type="hidden" name="id" id="policy_tranction_primarykey">
|
||||||
@ -1052,6 +1052,12 @@
|
|||||||
if(res.status == true){
|
if(res.status == true){
|
||||||
|
|
||||||
hide_list_show_add()
|
hide_list_show_add()
|
||||||
|
var page_title = 'Edit Endorsement' + (res.data.client_short_name || res.data.policy_type || res.data.policy_no ?
|
||||||
|
' - ' + [res.data.client_short_name, res.data.policy_type, res.data.policy_no]
|
||||||
|
.filter(Boolean).join('-') : '');
|
||||||
|
var backUrl = '<?= base_url("policy_tranction/endorsement/list"); ?>';
|
||||||
|
var backButton = `<a href="${backUrl}" aria-label="Back to list"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>`;
|
||||||
|
updateNavTitle(page_title, backButton);
|
||||||
|
|
||||||
$('#bro_payable_by').val(res.data.bro_payable_by)
|
$('#bro_payable_by').val(res.data.bro_payable_by)
|
||||||
|
|
||||||
|
|||||||
@ -638,7 +638,37 @@
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var pageName = "<?= esc($page_name ?? '') ?>";
|
||||||
|
var userName = "<?= esc(get_session_userdata()->first_name ?? 'NOT SET') ?>";
|
||||||
|
|
||||||
|
function updateNavTitle(subTitle, backButton) {
|
||||||
|
var titleHtml = "";
|
||||||
|
var $titleEl = $(".top-navbar-title");
|
||||||
|
var $navDiv = $(".top-navbar-div");
|
||||||
|
|
||||||
|
if (pageName && backButton && subTitle) {
|
||||||
|
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||||
|
titleHtml += `<h6>${backButton} ${subTitle}</h6>`;
|
||||||
|
$titleEl.css({ display: "block", bottom: "12px" });
|
||||||
|
$navDiv.css({ marginTop: "0px" });
|
||||||
|
} else if (pageName && subTitle) {
|
||||||
|
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||||
|
titleHtml += `<h6>${subTitle}</h6>`;
|
||||||
|
$titleEl.css({ display: "block", bottom: "12px" });
|
||||||
|
$navDiv.css({ marginTop: "0px" });
|
||||||
|
} else {
|
||||||
|
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||||
|
$titleEl.css({ display: "flex", bottom: "0px" });
|
||||||
|
$navDiv.css({ marginTop: "20px" });
|
||||||
|
}
|
||||||
|
|
||||||
|
$titleEl.html(titleHtml);
|
||||||
|
}
|
||||||
|
|
||||||
function hide_list_show_add() {
|
function hide_list_show_add() {
|
||||||
|
var backUrl = '<?= base_url("policy_tranction/endorsement/list"); ?>';
|
||||||
|
var backButton = `<a href="${backUrl}" aria-label="Back to list"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>`;
|
||||||
|
updateNavTitle('Add Endorsement', backButton);
|
||||||
removeAllColumnsExceptFirst('insurerTable');
|
removeAllColumnsExceptFirst('insurerTable');
|
||||||
$('.hideter').hide()
|
$('.hideter').hide()
|
||||||
$('.hidetp').hide()
|
$('.hidetp').hide()
|
||||||
@ -654,6 +684,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function show_list_hide_add() {
|
function show_list_hide_add() {
|
||||||
|
updateNavTitle(false, false);
|
||||||
$('#pt_onboarding').hide()
|
$('#pt_onboarding').hide()
|
||||||
$('#endorsement_list').show()
|
$('#endorsement_list').show()
|
||||||
$('#endorsement_filter').show()
|
$('#endorsement_filter').show()
|
||||||
|
|||||||
@ -293,7 +293,10 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<script>
|
||||||
|
var pageSubTitle = undefined;
|
||||||
|
var pageBackButton = undefined;
|
||||||
|
</script>
|
||||||
<div class="tab-pane fade active show" id="form">
|
<div class="tab-pane fade active show" id="form">
|
||||||
<input type="hidden" id="entity_type_id">
|
<input type="hidden" id="entity_type_id">
|
||||||
<div class="row" id="inception_form">
|
<div class="row" id="inception_form">
|
||||||
@ -302,16 +305,16 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row" style="margin-bottom:1rem;">
|
<div class="row" style="margin-bottom:1rem;">
|
||||||
|
|
||||||
<div class="col-6" style="align-self: center;">
|
<!-- <div class="col-6" style="align-self: center;">
|
||||||
<h4 id="page_title" style="position: relative;">Add Policy</h4>
|
<h4 id="page_title" style="position: relative;">Add Policy</h4>
|
||||||
</div>
|
</div> -->
|
||||||
<div class="col-3" style="text-align: right; position: relative; left: 211px;">
|
<!-- <div class="col-3" style="text-align: right; position: relative; left: 211px;"> -->
|
||||||
<!-- <button class="btn btn-primary waves-effect waves-light" onclick="fileupload(this)">file upload</button> -->
|
<!-- <button class="btn btn-primary waves-effect waves-light" onclick="fileupload(this)">file upload</button> -->
|
||||||
</div>
|
<!-- </div> -->
|
||||||
<div class="col-1" style="text-align: right; position: relative; left: 193px;">
|
<!-- <div class="col-1" style="text-align: right; position: relative; left: 193px;">
|
||||||
<a href="<?= base_url('policy_tranction/inception/list') ?>" type="button" id="btnAdd" class="btn btn-primary waves-effect waves-light"
|
<a href="<?= base_url('policy_tranction/inception/list') ?>" type="button" id="btnAdd" class="btn btn-primary waves-effect waves-light"
|
||||||
>Back</a>
|
>Back</a>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
<form role="form" class="parsley-examples" method="post" id="inception_form_id" enctype="multipart/form-data">
|
<form role="form" class="parsley-examples" method="post" id="inception_form_id" enctype="multipart/form-data">
|
||||||
|
|
||||||
@ -2255,8 +2258,9 @@
|
|||||||
var page_title = 'Edit Policy' + (res.data.client_short_name || res.data.policy_type || res.data.policy_no ?
|
var page_title = 'Edit Policy' + (res.data.client_short_name || res.data.policy_type || res.data.policy_no ?
|
||||||
' - ' + [res.data.client_short_name, res.data.policy_type, res.data.policy_no]
|
' - ' + [res.data.client_short_name, res.data.policy_type, res.data.policy_no]
|
||||||
.filter(Boolean).join('-') : '');
|
.filter(Boolean).join('-') : '');
|
||||||
|
var backUrl = '<?= base_url("policy_tranction/inception/list"); ?>';
|
||||||
$('#page_title').text(page_title);
|
var backButton = `<a href="${backUrl}" aria-label="Back to list"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>`;
|
||||||
|
updateNavTitle(page_title, backButton);
|
||||||
$('#client_id_kyc').val(res.data.client_id);
|
$('#client_id_kyc').val(res.data.client_id);
|
||||||
$('#policy_tranction_primarykey_for_file_upload').val(res.data.id);
|
$('#policy_tranction_primarykey_for_file_upload').val(res.data.id);
|
||||||
$('#client_id_for_vehicle_file_upload').val(res.data.client_id);
|
$('#client_id_for_vehicle_file_upload').val(res.data.client_id);
|
||||||
|
|||||||
@ -720,10 +720,39 @@ $(document).ready(function(){
|
|||||||
})
|
})
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------------
|
||||||
|
var pageName = "<?= esc($page_name ?? '') ?>";
|
||||||
|
var userName = "<?= esc(get_session_userdata()->first_name ?? 'NOT SET') ?>";
|
||||||
|
|
||||||
|
function updateNavTitle(subTitle, backButton) {
|
||||||
|
var titleHtml = "";
|
||||||
|
var $titleEl = $(".top-navbar-title");
|
||||||
|
var $navDiv = $(".top-navbar-div");
|
||||||
|
|
||||||
|
if (pageName && backButton && subTitle) {
|
||||||
|
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||||
|
titleHtml += `<h6>${backButton} ${subTitle}</h6>`;
|
||||||
|
$titleEl.css({ display: "block", bottom: "12px" });
|
||||||
|
$navDiv.css({ marginTop: "0px" });
|
||||||
|
} else if (pageName && subTitle) {
|
||||||
|
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||||
|
titleHtml += `<h6>${subTitle}</h6>`;
|
||||||
|
$titleEl.css({ display: "block", bottom: "12px" });
|
||||||
|
$navDiv.css({ marginTop: "0px" });
|
||||||
|
} else {
|
||||||
|
titleHtml = `<h3 style="margin-right:5px !important;">${pageName}</h3>`;
|
||||||
|
$titleEl.css({ display: "flex", bottom: "0px" });
|
||||||
|
$navDiv.css({ marginTop: "20px" });
|
||||||
|
}
|
||||||
|
|
||||||
|
$titleEl.html(titleHtml);
|
||||||
|
}
|
||||||
|
|
||||||
function hide_list_show_add()
|
function hide_list_show_add()
|
||||||
{
|
{
|
||||||
$('#page_title').text('Add Policy')
|
// $('#page_title').text('Add Policy')
|
||||||
|
var backUrl = '<?= base_url("policy_tranction/inception/list"); ?>';
|
||||||
|
var backButton = `<a href="${backUrl}" aria-label="Back to list"><i class="mdi mdi-arrow-left" style="font-size: 17px;"></i></a>`;
|
||||||
|
updateNavTitle('Add Policy', backButton);
|
||||||
$('#inception_form_id')[0].reset();
|
$('#inception_form_id')[0].reset();
|
||||||
$('#client_id').val('').change().prop('disabled', false);
|
$('#client_id').val('').change().prop('disabled', false);
|
||||||
$('#tpa').val('').change().prop('disabled', false);
|
$('#tpa').val('').change().prop('disabled', false);
|
||||||
@ -754,6 +783,9 @@ function hide_list_show_add()
|
|||||||
|
|
||||||
function show_list_hide_add()
|
function show_list_hide_add()
|
||||||
{
|
{
|
||||||
|
pageSubTitle = undefined;
|
||||||
|
pageBackButton = undefined;
|
||||||
|
updateNavTitle(false, false);
|
||||||
$('#pt_onboarding').hide()
|
$('#pt_onboarding').hide()
|
||||||
$('#inception_list').show()
|
$('#inception_list').show()
|
||||||
$('#inception_filter').show();
|
$('#inception_filter').show();
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<div class="form-row renewalCalculation">
|
<div class="form-row renewalCalculation">
|
||||||
|
|
||||||
<div class="form-group col-md-3 renewalFields" style="display: none;">
|
<div class="form-group col-md-3 renewalFields" style="display: none;">
|
||||||
<label for="incurred_claim_date">Incurred Claim Date<span class="text-danger"></span></label>
|
<label for="incurred_claim_date">Incurred Claim Date<span class="text-danger">*</span></label>
|
||||||
<input value="<?= isset($lead_edit_data['incurred_claims_date']) ? $lead_edit_data['incurred_claims_date'] : '' ?>" type="text" class="form-control incurred_claim" id="incurred_claim_date<?= $increment ?>"
|
<input value="<?= isset($lead_edit_data['incurred_claims_date']) ? $lead_edit_data['incurred_claims_date'] : '' ?>" type="text" class="form-control incurred_claim" id="incurred_claim_date<?= $increment ?>"
|
||||||
name="incurred_claim_date[]" placeholder="Enter DOE" oninput="calculatePolicyMetrics(this)">
|
name="incurred_claim_date[]" placeholder="Enter DOE" oninput="calculatePolicyMetrics(this)">
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -123,7 +123,7 @@
|
|||||||
.timeline-item:last-child::before { display: none !important;}
|
.timeline-item:last-child::before { display: none !important;}
|
||||||
|
|
||||||
/* opportunities Styling */
|
/* opportunities Styling */
|
||||||
.opportunities-list { display: grid; gap: 15px;}
|
.opportunities-list { display: grid; gap: 15px; margin-bottom: 12px; }
|
||||||
.opportunity-card { background: white; border-radius: 12px; padding: 20px; border: 1px solid #e0e0e0; transition: all 0.2s;}
|
.opportunity-card { background: white; border-radius: 12px; padding: 20px; border: 1px solid #e0e0e0; transition: all 0.2s;}
|
||||||
.opportunity-header { display: flex; justify-content: space-between; align-items: start; margin-bottom: 15px;}
|
.opportunity-header { display: flex; justify-content: space-between; align-items: start; margin-bottom: 15px;}
|
||||||
.opportunity-title { font-weight: 600; font-size: 16px; margin-bottom: 5px;}
|
.opportunity-title { font-weight: 600; font-size: 16px; margin-bottom: 5px;}
|
||||||
@ -171,7 +171,7 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<hr class="my-0">
|
<hr style="margin-bottom: 0 !important;">
|
||||||
<div class="lead-header">
|
<div class="lead-header">
|
||||||
|
|
||||||
<!-- LEFT SIDE -->
|
<!-- LEFT SIDE -->
|
||||||
@ -213,7 +213,7 @@
|
|||||||
<div class="modal-content" style="max-width: 850px;">
|
<div class="modal-content" style="max-width: 850px;">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<div>
|
<div>
|
||||||
<h2 id="det_company">Lead Detail</h2>
|
<h4 class="modal-title" id="det_company" style="margin-top: 5px;" >Lead Detail</h2>
|
||||||
<span id="det_status_badge" class="lead-status" style="margin-top: unset;"></span>
|
<span id="det_status_badge" class="lead-status" style="margin-top: unset;"></span>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn-close" title="Close" onclick="closeModal('leadDetailModal')">×</button>
|
<button class="btn-close" title="Close" onclick="closeModal('leadDetailModal')">×</button>
|
||||||
@ -408,7 +408,7 @@
|
|||||||
<div class="modal" id="opportunityModal">
|
<div class="modal" id="opportunityModal">
|
||||||
<div class="modal-content" style="max-width: 600px;">
|
<div class="modal-content" style="max-width: 600px;">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h2>Select Opportunity Type</h2>
|
<h4 class="modal-title" style="margin-top: 15px;margin-bottom: 15px;">Select Opportunity Type</h4>
|
||||||
<button class="btn-close" title="Close" onclick="closeModal('opportunityModal')">×</button>
|
<button class="btn-close" title="Close" onclick="closeModal('opportunityModal')">×</button>
|
||||||
</div>
|
</div>
|
||||||
<hr class="my-0">
|
<hr class="my-0">
|
||||||
@ -683,7 +683,7 @@ async function fetchActivities(isLoadMore = false) {
|
|||||||
${activityIcons[a.activity_type]} ${a.activity_type}
|
${activityIcons[a.activity_type]} ${a.activity_type}
|
||||||
</span>
|
</span>
|
||||||
<span class="activity-status-badge status-${a.status.toLowerCase().replace(' ', '-')}"">
|
<span class="activity-status-badge status-${a.status.toLowerCase().replace(' ', '-')}"">
|
||||||
${a.status}
|
${capitalize(a.status)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="activity-details">
|
<div class="activity-details">
|
||||||
@ -814,7 +814,7 @@ function renderCard(opps) {
|
|||||||
// ${o.client_short_name}
|
// ${o.client_short_name}
|
||||||
return `
|
return `
|
||||||
<div class="opportunities-list" id="leadOpportunitiesList">
|
<div class="opportunities-list" id="leadOpportunitiesList">
|
||||||
<div class="opportunity-card">
|
<div class="opportunity-card" style="bottom:2px;">
|
||||||
<div class="opportunity-header">
|
<div class="opportunity-header">
|
||||||
<div class="opportunity-title">${lead_type}</div>
|
<div class="opportunity-title">${lead_type}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -940,6 +940,11 @@ function openOpportunityModal() {
|
|||||||
openModal('opportunityModal');
|
openModal('opportunityModal');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function capitalize(str) {
|
||||||
|
if (!str) return '';
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
function switchTab(tabName) {
|
function switchTab(tabName) {
|
||||||
//Default Activity Tab how to resset here
|
//Default Activity Tab how to resset here
|
||||||
// 1. Hide all tab content
|
// 1. Hide all tab content
|
||||||
|
|||||||
@ -71,7 +71,7 @@
|
|||||||
.timeline-item:last-child::before { display: none !important;}
|
.timeline-item:last-child::before { display: none !important;}
|
||||||
|
|
||||||
/* opportunities Styling */
|
/* opportunities Styling */
|
||||||
.opportunities-list { display: grid; gap: 15px;}
|
.opportunities-list { display: grid; gap: 15px; margin-bottom: 12px; }
|
||||||
.opportunity-card { background: white; border-radius: 12px; padding: 20px; border: 1px solid #e0e0e0; transition: all 0.2s;}
|
.opportunity-card { background: white; border-radius: 12px; padding: 20px; border: 1px solid #e0e0e0; transition: all 0.2s;}
|
||||||
.opportunity-header { display: flex; justify-content: space-between; align-items: start; margin-bottom: 15px;}
|
.opportunity-header { display: flex; justify-content: space-between; align-items: start; margin-bottom: 15px;}
|
||||||
.opportunity-title { font-weight: 600; font-size: 16px; margin-bottom: 5px;}
|
.opportunity-title { font-weight: 600; font-size: 16px; margin-bottom: 5px;}
|
||||||
@ -145,7 +145,7 @@
|
|||||||
<div class="tab" data-filter="Prospects" onclick="setFilter('Prospects', this)">Prospects</div>
|
<div class="tab" data-filter="Prospects" onclick="setFilter('Prospects', this)">Prospects</div>
|
||||||
<div class="tab" data-filter="Not a Prospects" onclick="setFilter('Not a Prospects', this)">Not a Prospects</div>
|
<div class="tab" data-filter="Not a Prospects" onclick="setFilter('Not a Prospects', this)">Not a Prospects</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
<hr class="my-0">
|
<hr style="margin-bottom: 0 !important;">
|
||||||
<div class="lead-header">
|
<div class="lead-header">
|
||||||
|
|
||||||
<!-- LEFT SIDE -->
|
<!-- LEFT SIDE -->
|
||||||
@ -196,10 +196,43 @@
|
|||||||
<div class="modal-body p-4">
|
<div class="modal-body p-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-12 mb-1">
|
<div class="col-12 mb-1">
|
||||||
<div class="col-xl-12 col-lg-12 col-md-12">
|
<div class="col-xl-12 col-lg-12 col-md-12" style="position:relative;">
|
||||||
<label class="form-label">Company Name<span class="text-danger">*</span></label>
|
|
||||||
<input type="text" class="form-control" name="company_name" style="width: 100%;" placeholder="Enter Company Name" oninput="this.value = this.value.replace(/[^A-Za-z\s]/g, '')" required>
|
<!-- Label with inline short name badge -->
|
||||||
|
<label class="form-label">
|
||||||
|
Company Name<span class="text-danger">*</span>
|
||||||
|
|
||||||
|
<span id="shortNameBadge" style="display:none; background:#FAEEDA; color:#854F0B;
|
||||||
|
padding:2px 10px; border-radius:999px; font-size:11px; font-weight:600;
|
||||||
|
letter-spacing:0.5px; vertical-align:middle;">
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<!-- Hidden fields -->
|
||||||
|
<input type="hidden" name="client_id" id="lead_client_id" value="">
|
||||||
|
<input type="hidden" name="short_name" id="lead_short_name_hidden" value="">
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Selected pill (shown after picking existing client) -->
|
||||||
|
<div id="companySelected" style="display:none; align-items:center; gap:8px;
|
||||||
|
border:1px solid #B5D4F4; border-radius:6px; padding:6px 12px; background:#E6F1FB;">
|
||||||
|
<span id="companySelectedName" style="flex:1; font-size:14px; font-weight:500;"></span>
|
||||||
|
<span onclick="clearCompanySelection('add')" style="cursor:pointer; color:#888; font-size:18px; line-height:1;">×</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Search input -->
|
||||||
|
<input type="text" id="companySearchInput" class="form-control"
|
||||||
|
name="company_name" placeholder="Search or create company..."
|
||||||
|
autocomplete="off" oninput="searchCompany(this.value, 'add')" required
|
||||||
|
style="width:100%;">
|
||||||
|
|
||||||
|
<!-- Dropdown -->
|
||||||
|
<div id="companyDropdown" style="display:none; position:absolute; z-index:9999;
|
||||||
|
width:100%; background:#fff; border:1px solid #ddd; border-radius:6px;
|
||||||
|
box-shadow:0 4px 12px rgba(0,0,0,0.1); max-height:220px; overflow-y:auto;
|
||||||
|
top:100%; left:0;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row mb-1">
|
<div class="form-row mb-1">
|
||||||
<div class="col-md-12 ml-3" style="width: 97%;">
|
<div class="col-md-12 ml-3" style="width: 97%;">
|
||||||
@ -207,7 +240,7 @@
|
|||||||
<input type="email" class="form-control" name="email" style="width: 100%;" placeholder="Enter the Email" oninput="this.value = this.value.replace(/[^a-zA-Z0-9@.\-_+]/g, '')">
|
<input type="email" class="form-control" name="email" style="width: 100%;" placeholder="Enter the Email" oninput="this.value = this.value.replace(/[^a-zA-Z0-9@.\-_+]/g, '')">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-12" style="width: 94%;">
|
<div class="col-md-12" style="width: 94%;">
|
||||||
<label class="form-label">Phone</label>
|
<label class="form-label">Mobile</label>
|
||||||
<!-- <input type="text" class="form-control" name="phone" style="width: 100%;" placeholder="Enter the Phone Number" oninput="this.value = this.value.replace(/[^\d\s+]/g, '')"> -->
|
<!-- <input type="text" class="form-control" name="phone" style="width: 100%;" placeholder="Enter the Phone Number" oninput="this.value = this.value.replace(/[^\d\s+]/g, '')"> -->
|
||||||
<input type="text" class="form-control" name="phone" style="width: 100%;" placeholder="Enter the Phone Number" maxlength="10" oninput="this.value = this.value.replace(/[^0-9]/g, '').substring(0, 10);">
|
<input type="text" class="form-control" name="phone" style="width: 100%;" placeholder="Enter the Phone Number" maxlength="10" oninput="this.value = this.value.replace(/[^0-9]/g, '').substring(0, 10);">
|
||||||
</div>
|
</div>
|
||||||
@ -248,7 +281,7 @@
|
|||||||
<div class="modal-content" style="max-width: 850px;">
|
<div class="modal-content" style="max-width: 850px;">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<div>
|
<div>
|
||||||
<h2 id="det_company">Lead Detail</h2>
|
<h4 class="modal-title" id="det_company" style="margin-top: 5px;" >Lead Detail</h2>
|
||||||
<span id="det_status_badge" class="lead-status" style="margin-top: unset;"></span>
|
<span id="det_status_badge" class="lead-status" style="margin-top: unset;"></span>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn-close" onclick="closeModal('leadDetailModal')" title="Close">×</button>
|
<button class="btn-close" onclick="closeModal('leadDetailModal')" title="Close">×</button>
|
||||||
@ -435,7 +468,7 @@
|
|||||||
<div class="modal" id="opportunityModal">
|
<div class="modal" id="opportunityModal">
|
||||||
<div class="modal-content" style="max-width: 600px;">
|
<div class="modal-content" style="max-width: 600px;">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h2>Select Opportunity Type</h2>
|
<h4 class="modal-title" style="margin-top: 15px;margin-bottom: 15px;">Select Opportunity Type</h4>
|
||||||
<button class="btn-close" onclick="closeModal('opportunityModal')" title="Close">×</button>
|
<button class="btn-close" onclick="closeModal('opportunityModal')" title="Close">×</button>
|
||||||
</div>
|
</div>
|
||||||
<hr class="my-0">
|
<hr class="my-0">
|
||||||
@ -470,7 +503,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal" id="editLeadModal">
|
<div class="modal" id="editLeadModal">
|
||||||
<div class="modal-content">
|
<div class="modal-content" style="overflow-x: hidden;">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h4 class="modal-title">Edit Lead</h4>
|
<h4 class="modal-title">Edit Lead</h4>
|
||||||
<button class="btn-close" onclick="closeModal('editLeadModal')" title="Close">×</button>
|
<button class="btn-close" onclick="closeModal('editLeadModal')" title="Close">×</button>
|
||||||
@ -481,10 +514,47 @@
|
|||||||
<div class="modal-body p-4">
|
<div class="modal-body p-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-12 mb-1">
|
<div class="col-12 mb-1">
|
||||||
<div class="col-xl-12 col-lg-12 col-md-12">
|
<!-- <div class="col-xl-12 col-lg-12 col-md-12">
|
||||||
<label class="form-label">Company Name <span class="text-danger">*</span></label>
|
<label class="form-label">Company Name <span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" name="company_name" style="width: 100%;" placeholder="Enter Company Name" oninput="this.value = this.value.replace(/[^A-Za-z\s]/g, '')" required>
|
<input type="text" class="form-control" name="company_name" style="width: 100%;" placeholder="Enter Company Name" oninput="this.value = this.value.replace(/[^A-Za-z\s]/g, '')" required>
|
||||||
|
</div> -->
|
||||||
|
<div class="col-xl-12 col-lg-12 col-md-12" style="position:relative;">
|
||||||
|
|
||||||
|
<!-- Label with inline short name badge -->
|
||||||
|
<label class="form-label">
|
||||||
|
Company Name <span class="text-danger">*</span>
|
||||||
|
|
||||||
|
<span id="editShortNameBadge" style="display:none; background:#FAEEDA; color:#854F0B;
|
||||||
|
padding:2px 10px; border-radius:999px; font-size:11px; font-weight:600;
|
||||||
|
letter-spacing:0.5px; vertical-align:middle;">
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<!-- Hidden fields -->
|
||||||
|
<input type="hidden" name="client_id" id="edit_lead_client_id" value="">
|
||||||
|
<input type="hidden" name="short_name" id="edit_lead_short_name_hidden" value="">
|
||||||
|
|
||||||
|
<!-- Selected pill (shown after picking existing client) -->
|
||||||
|
<div id="editCompanySelected" style="display:none; align-items:center; gap:8px;
|
||||||
|
border:1px solid #B5D4F4; border-radius:6px; padding:6px 12px; background:#E6F1FB;">
|
||||||
|
<span id="editCompanySelectedName" style="flex:1; font-size:14px; font-weight:500;"></span>
|
||||||
|
<span onclick="clearCompanySelection('edit')" style="cursor:pointer; color:#888; font-size:18px; line-height:1;">×</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Search input -->
|
||||||
|
<input type="text" id="editCompanySearchInput" class="form-control"
|
||||||
|
name="company_name" placeholder="Search or create company..."
|
||||||
|
autocomplete="off" oninput="searchCompany(this.value, 'edit')" required
|
||||||
|
style="width:100%;">
|
||||||
|
|
||||||
|
<!-- Dropdown -->
|
||||||
|
<div id="editCompanyDropdown" style="display:none; position:absolute; z-index:9999;
|
||||||
|
width:100%; background:#fff; border:1px solid #ddd; border-radius:6px;
|
||||||
|
box-shadow:0 4px 12px rgba(0,0,0,0.1); max-height:220px; overflow-y:auto;
|
||||||
|
top:100%; left:0;">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row mb-1">
|
<div class="form-row mb-1">
|
||||||
<div class="col-md-12 ml-3" style="width: 97%;">
|
<div class="col-md-12 ml-3" style="width: 97%;">
|
||||||
@ -492,7 +562,7 @@
|
|||||||
<input type="email" class="form-control" name="email" style="width: 100%;" placeholder="Enter the Email" oninput="this.value = this.value.replace(/[^a-zA-Z0-9@.\-_+]/g, '')">
|
<input type="email" class="form-control" name="email" style="width: 100%;" placeholder="Enter the Email" oninput="this.value = this.value.replace(/[^a-zA-Z0-9@.\-_+]/g, '')">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-12" style="width: 94%;">
|
<div class="col-md-12" style="width: 94%;">
|
||||||
<label class="form-label">Phone</label>
|
<label class="form-label">Mobile</label>
|
||||||
<!-- <input type="text" class="form-control" name="phone" style="width: 100%;" placeholder="Enter the Phone Number" oninput="this.value = this.value.replace(/[^\d\s+]/g, '')"> -->
|
<!-- <input type="text" class="form-control" name="phone" style="width: 100%;" placeholder="Enter the Phone Number" oninput="this.value = this.value.replace(/[^\d\s+]/g, '')"> -->
|
||||||
<input type="text" class="form-control" name="phone" style="width: 100%;" placeholder="Enter the Phone Number" maxlength="10" oninput="this.value = this.value.replace(/[^0-9]/g, '').substring(0, 10);">
|
<input type="text" class="form-control" name="phone" style="width: 100%;" placeholder="Enter the Phone Number" maxlength="10" oninput="this.value = this.value.replace(/[^0-9]/g, '').substring(0, 10);">
|
||||||
</div>
|
</div>
|
||||||
@ -553,28 +623,44 @@
|
|||||||
<input type="hidden" id="editing_contact_id" value="">
|
<input type="hidden" id="editing_contact_id" value="">
|
||||||
<div class="row g-0-5 align-items-end ml-1">
|
<div class="row g-0-5 align-items-end ml-1">
|
||||||
<div class="col-md-3 col-12 mb-2 mb-md-0">
|
<div class="col-md-3 col-12 mb-2 mb-md-0">
|
||||||
|
<label class="form-label">Person Name <span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control form-control-sm" id="contact_name" placeholder="Person Name" oninput="this.value = this.value.replace(/[^A-Za-z\s]/g, '')">
|
<input type="text" class="form-control form-control-sm" id="contact_name" placeholder="Person Name" oninput="this.value = this.value.replace(/[^A-Za-z\s]/g, '')">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-3 col-6">
|
<div class="col-md-3 col-6">
|
||||||
|
<label class="form-label">Mobile <span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control form-control-sm" id="contact_mobile" placeholder="Mobile" maxlength="10" oninput="this.value = this.value.replace(/[^0-9]/g, '').substring(0, 10);">
|
<input type="text" class="form-control form-control-sm" id="contact_mobile" placeholder="Mobile" maxlength="10" oninput="this.value = this.value.replace(/[^0-9]/g, '').substring(0, 10);">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-3 col-6">
|
<div class="col-md-3 col-6">
|
||||||
|
<label class="form-label">Designation </label>
|
||||||
<input type="text" class="form-control form-control-sm" id="contact_designation" placeholder="Designation" oninput="this.value = this.value.replace(/[^A-Za-z\s]/g, '')">
|
<input type="text" class="form-control form-control-sm" id="contact_designation" placeholder="Designation" oninput="this.value = this.value.replace(/[^A-Za-z\s]/g, '')">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-1 col-3">
|
<div class="col-md-1 col-3">
|
||||||
|
<label class="form-label"> </label>
|
||||||
<div class="primary-container">
|
<div class="primary-container">
|
||||||
<label for="contact_is_primary" class="primary-label">Primary</label>
|
<label for="contact_is_primary" class="primary-label">Primary</label>
|
||||||
<input class="form-check-input" type="checkbox" id="contact_is_primary">
|
<input class="form-check-input" type="checkbox" id="contact_is_primary">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-2 col-6">
|
<!-- <div class="col-md-2 col-6">
|
||||||
|
<label class="form-label"> </label>
|
||||||
<button type="button" id="btnSaveContact" class="btn btn-sm btn-info text-white" style="width:85%">
|
<button type="button" id="btnSaveContact" class="btn btn-sm btn-info text-white" style="width:85%">
|
||||||
✔ Save
|
✔ Save
|
||||||
</button>
|
</button>
|
||||||
|
</div> -->
|
||||||
|
<div class="col-md-2 col-6">
|
||||||
|
<label class="form-label"> </label>
|
||||||
|
<div style="display:flex; gap:2px;">
|
||||||
|
<button type="button" id="btnSaveContact" class="btn btn-sm btn-info text-white" style="width:42%" title="Save">
|
||||||
|
✔
|
||||||
|
</button>
|
||||||
|
<button type="button" id="btnClearContact" class="btn btn-sm btn-danger text-white" style="width:42%" title="Clear">
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -713,13 +799,22 @@ function closeModal(id) {
|
|||||||
document.getElementById('btn_add_opportunity').style.display = 'none';
|
document.getElementById('btn_add_opportunity').style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (id === 'editLeadModal') {
|
||||||
|
// // const Eform = document.getElementById('editLeadForm');
|
||||||
|
// // if (!Eform) return; // safety check
|
||||||
|
// // Eform.reset();
|
||||||
|
// form.querySelectorAll('[name="status"] option')
|
||||||
|
// .forEach(opt => opt.hidden = false);
|
||||||
|
// form.querySelector('[name="status"]').value = 'New';
|
||||||
|
// }
|
||||||
|
// Replace both add and edit company cleanup blocks with:
|
||||||
|
if (id === 'addLeadModal') {
|
||||||
|
_resetCompanyUI('add');
|
||||||
|
}
|
||||||
if (id === 'editLeadModal') {
|
if (id === 'editLeadModal') {
|
||||||
// const Eform = document.getElementById('editLeadForm');
|
form.querySelectorAll('[name="status"] option').forEach(opt => opt.hidden = false);
|
||||||
// if (!Eform) return; // safety check
|
|
||||||
// Eform.reset();
|
|
||||||
form.querySelectorAll('[name="status"] option')
|
|
||||||
.forEach(opt => opt.hidden = false);
|
|
||||||
form.querySelector('[name="status"]').value = 'New';
|
form.querySelector('[name="status"]').value = 'New';
|
||||||
|
_resetCompanyUI('edit');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -730,6 +825,32 @@ function selectType(val, el) {
|
|||||||
selectedType = val;
|
selectedType = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetContactForm() {
|
||||||
|
document.getElementById('editing_contact_id').value = '';
|
||||||
|
document.getElementById('contact_name').value = '';
|
||||||
|
document.getElementById('contact_mobile').value = '';
|
||||||
|
document.getElementById('contact_designation').value = '';
|
||||||
|
document.getElementById('contact_is_primary').checked = false;
|
||||||
|
document.getElementById('btnSaveContact').innerHTML = "✔";
|
||||||
|
document.getElementById('btnSaveContact').title = "Save";
|
||||||
|
document.getElementById('btnSaveContact').style.background = "#02a8b5";
|
||||||
|
}
|
||||||
|
// Helper — add this once anywhere in your JS
|
||||||
|
function _resetCompanyUI(mode) {
|
||||||
|
const ctx = COMPANY_CTX[mode];
|
||||||
|
const state = companyState[mode];
|
||||||
|
state.isExisting = false;
|
||||||
|
state.selectedValue = '';
|
||||||
|
document.getElementById(ctx.clientId).value = '';
|
||||||
|
document.getElementById(ctx.shortNameHidden).value = ''; // ← only this
|
||||||
|
document.getElementById(ctx.selected).style.display = 'none';
|
||||||
|
document.getElementById(ctx.searchInput).style.display = '';
|
||||||
|
document.getElementById(ctx.searchInput).value = '';
|
||||||
|
document.getElementById(ctx.dropdown).style.display = 'none';
|
||||||
|
const b = document.getElementById(ctx.badge);
|
||||||
|
if (b) { b.textContent = ''; b.style.display = 'none'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function selectFollowUpActivityType(val, el) {
|
function selectFollowUpActivityType(val, el) {
|
||||||
document.querySelectorAll('.f_activity_type').forEach(b => b.classList.remove('active'));
|
document.querySelectorAll('.f_activity_type').forEach(b => b.classList.remove('active'));
|
||||||
@ -980,7 +1101,7 @@ function renderCard(opps) {
|
|||||||
// ${o.client_short_name}
|
// ${o.client_short_name}
|
||||||
return `
|
return `
|
||||||
<div class="opportunities-list" id="leadOpportunitiesList">
|
<div class="opportunities-list" id="leadOpportunitiesList">
|
||||||
<div class="opportunity-card">
|
<div class="opportunity-card" style="bottom:2px;">
|
||||||
<div class="opportunity-header">
|
<div class="opportunity-header">
|
||||||
<div class="opportunity-title">${lead_type}</div>
|
<div class="opportunity-title">${lead_type}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1143,6 +1264,10 @@ document.getElementById('addLeadForm').onsubmit = async (e) => {
|
|||||||
// let phoneRegex = /^\+?[0-9\s]{0,10}$/; // Phone regex (+, numbers, spaces allowed, 10-20 length)
|
// let phoneRegex = /^\+?[0-9\s]{0,10}$/; // Phone regex (+, numbers, spaces allowed, 10-20 length)
|
||||||
let phoneRegex = /^\d{10}$/;
|
let phoneRegex = /^\d{10}$/;
|
||||||
|
|
||||||
|
let addState = companyState['add']; // use 'edit' in editLeadForm
|
||||||
|
let modeState = companyState['add']; // ← change to 'edit' for edit form
|
||||||
|
|
||||||
|
|
||||||
// 1. HELPER FUNCTION: Shows Toastr and focuses the specific field
|
// 1. HELPER FUNCTION: Shows Toastr and focuses the specific field
|
||||||
function showError(message, fieldName) {
|
function showError(message, fieldName) {
|
||||||
toastr.warning(message, 'Validation Error');
|
toastr.warning(message, 'Validation Error');
|
||||||
@ -1151,6 +1276,13 @@ document.getElementById('addLeadForm').onsubmit = async (e) => {
|
|||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!modeState.selectedValue && !data.client_id) {
|
||||||
|
return showError('Please select a company from the dropdown or click "Create" to add a new one.', 'company_name');
|
||||||
|
}
|
||||||
|
if (!companyState['add'].selectedValue) {
|
||||||
|
return showError('Please select or create a company from the dropdown.', 'company_name');
|
||||||
|
}
|
||||||
|
|
||||||
// --- VALIDATION CHECKS (Button is still normal here) ---
|
// --- VALIDATION CHECKS (Button is still normal here) ---
|
||||||
if (!companyName && !email && !phone && !data.assigned_to) {
|
if (!companyName && !email && !phone && !data.assigned_to) {
|
||||||
toastr.warning('Please fill in all the required fields.');
|
toastr.warning('Please fill in all the required fields.');
|
||||||
@ -1256,6 +1388,9 @@ document.getElementById('editLeadForm').onsubmit = async (e) => {
|
|||||||
// Just grab the button variables first, DO NOT disable yet
|
// Just grab the button variables first, DO NOT disable yet
|
||||||
const submitBtn = e.target.querySelector('button[type="submit"]');
|
const submitBtn = e.target.querySelector('button[type="submit"]');
|
||||||
const originalBtnText = submitBtn.innerText;
|
const originalBtnText = submitBtn.innerText;
|
||||||
|
let addState = companyState['edit']; // use 'edit' in editLeadForm
|
||||||
|
let modeState = companyState['edit']; // ← change to 'edit' for edit form
|
||||||
|
|
||||||
|
|
||||||
const data = Object.fromEntries(new FormData(e.target).entries());
|
const data = Object.fromEntries(new FormData(e.target).entries());
|
||||||
console.log("Lead :", data);
|
console.log("Lead :", data);
|
||||||
@ -1281,6 +1416,14 @@ document.getElementById('editLeadForm').onsubmit = async (e) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- VALIDATION CHECKS (Button is still normal here) ---
|
// --- VALIDATION CHECKS (Button is still normal here) ---
|
||||||
|
if (!modeState.selectedValue && !data.client_id) {
|
||||||
|
return showError('Please select a company from the dropdown or click "Create" to add a new one.', 'company_name');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!companyState['edit'].selectedValue) {
|
||||||
|
return showError('Please select or create a company from the dropdown.', 'company_name');
|
||||||
|
}
|
||||||
|
|
||||||
if (!companyName && !email && !phone && !data.assigned_to) {
|
if (!companyName && !email && !phone && !data.assigned_to) {
|
||||||
toastr.warning('Please fill in all the required fields.');
|
toastr.warning('Please fill in all the required fields.');
|
||||||
return;
|
return;
|
||||||
@ -1343,7 +1486,9 @@ document.getElementById('editLeadForm').onsubmit = async (e) => {
|
|||||||
$('#contactPersonsList').find('.contact-card').remove();
|
$('#contactPersonsList').find('.contact-card').remove();
|
||||||
$('#noContactPersonsData').show();
|
$('#noContactPersonsData').show();
|
||||||
document.getElementById('editing_contact_id').value = '';
|
document.getElementById('editing_contact_id').value = '';
|
||||||
document.getElementById('btnSaveContact').innerHTML = "✔ Save";
|
document.getElementById('btnSaveContact').innerHTML = "✔";
|
||||||
|
document.getElementById('btnSaveContact').title = "Save";
|
||||||
|
document.getElementById('btnSaveContact').style.background = "#02a8b5";
|
||||||
} else {
|
} else {
|
||||||
let err = await res.json();
|
let err = await res.json();
|
||||||
if (res.status === 400) {
|
if (res.status === 400) {
|
||||||
@ -1688,8 +1833,11 @@ document.getElementById('do_follow').addEventListener('change', function () {
|
|||||||
|
|
||||||
|
|
||||||
async function openEditLeadModal(id) {
|
async function openEditLeadModal(id) {
|
||||||
|
|
||||||
// 1. Reset UI State
|
// 1. Reset UI State
|
||||||
document.getElementById('hidden_lead_id').value = id;
|
document.getElementById('hidden_lead_id').value = id;
|
||||||
|
// ✅ ADD THIS — reset contact form state every time modal opens
|
||||||
|
resetContactForm();
|
||||||
const container = $('#contactPersonsList');
|
const container = $('#contactPersonsList');
|
||||||
// Remove only previous contact cards, keep the NoData span for now
|
// Remove only previous contact cards, keep the NoData span for now
|
||||||
container.find('.contact-card').remove();
|
container.find('.contact-card').remove();
|
||||||
@ -1706,6 +1854,22 @@ async function openEditLeadModal(id) {
|
|||||||
|
|
||||||
// 3. Populate Form Fields
|
// 3. Populate Form Fields
|
||||||
const form = document.getElementById('editLeadForm');
|
const form = document.getElementById('editLeadForm');
|
||||||
|
// Populate company for edit modal
|
||||||
|
_resetCompanyUI('edit');
|
||||||
|
if (lead.client_id) {
|
||||||
|
selectExistingCompany({
|
||||||
|
id: lead.client_id,
|
||||||
|
client_name: lead.company_name || '',
|
||||||
|
short_name: lead.short_name || '',
|
||||||
|
email: '', // don't overwrite existing email
|
||||||
|
phone: '', // don't overwrite existing phone
|
||||||
|
}, 'edit');
|
||||||
|
} else {
|
||||||
|
// No linked client — just show the typed name, allow search
|
||||||
|
companyState['edit'].selectedValue = lead.company_name || '';
|
||||||
|
const inp = document.getElementById('editCompanySearchInput');
|
||||||
|
inp.value = lead.company_name || '';
|
||||||
|
}
|
||||||
|
|
||||||
// Mapping fields carefully
|
// Mapping fields carefully
|
||||||
form.querySelector('[name="company_name"]').value = lead.company_name || '';
|
form.querySelector('[name="company_name"]').value = lead.company_name || '';
|
||||||
@ -1739,16 +1903,23 @@ async function openEditLeadModal(id) {
|
|||||||
<div class="fw-bold text-truncate" style="font-size:14px;">${contact.name} ${primaryBadge}</div>
|
<div class="fw-bold text-truncate" style="font-size:14px;">${contact.name} ${primaryBadge}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="flex: 2; min-width: 0; color:#666; font-size:13px;">${contact.mobile}</div>
|
<div style="flex: 2; min-width: 0; color:#666; font-size:13px;">${contact.mobile || '-'}</div>
|
||||||
<div style="flex: 2; min-width: 0; color:#666; font-size:13px;"><div class="text-truncate">${contact.designation}</div></div>
|
<div style="flex: 2; min-width: 0; color:#666; font-size:13px;"><div class="text-truncate">${contact.designation || '-'}</div></div>
|
||||||
|
|
||||||
<div class="d-flex" style="gap:5px;">
|
<div class="d-flex" style="gap:5px;">
|
||||||
<button type="button" class="btnEditContact" data-id="${contact.contact_id}" data-info='${JSON.stringify(contact)}' title="Edit Contact Person"
|
<button type="button" class="btnEditContact" data-id="${contact.contact_id}" data-info='${JSON.stringify(contact)}' title="Edit Contact Person"
|
||||||
style="background:none; border:none; cursor:pointer; color:#1976d2; font-size:18px; flex-shrink:0; padding:4px 6px; border-radius:6px; transition:background 0.2s;"
|
style="background:none; border:none; cursor:pointer; font-size:16px; flex-shrink:0; padding:4px 6px; border-radius:6px; transition:background 0.2s;"
|
||||||
onmouseover="this.style.background='#e3f2fd'" onmouseout="this.style.background='none'">✏️</button>
|
onmouseover="this.style.background='#e3f2fd'; this.querySelector('i').style.color='#1976d2';"
|
||||||
|
onmouseout="this.style.background='none'; this.querySelector('i').style.color='#555';">
|
||||||
|
<i class="ri-pencil-fill" style="color:#555;"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
<button type="button" class="btnRemoveContact" data-id="${contact.contact_id}" title="Remove Contact Person"
|
<button type="button" class="btnRemoveContact" data-id="${contact.contact_id}" title="Remove Contact Person"
|
||||||
style="background:none; border:none; cursor:pointer; color:#1976d2; font-size:18px; flex-shrink:0; padding:4px 6px; border-radius:6px; transition:background 0.2s;"
|
style="background:none; border:none; cursor:pointer; font-size:16px; flex-shrink:0; padding:4px 6px; border-radius:6px; transition:background 0.2s;"
|
||||||
onmouseover="this.style.background='#ffebee'" onmouseout="this.style.background='none'">🗑️</button>
|
onmouseover="this.style.background='#ffebee'; this.querySelector('i').style.color='#d32f2f';"
|
||||||
|
onmouseout="this.style.background='none'; this.querySelector('i').style.color='#555';">
|
||||||
|
<i class="ri-delete-bin-fill" style="color:#555;"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@ -1786,9 +1957,9 @@ document.getElementById('contactPersonsList').onclick = async (e) => {
|
|||||||
document.getElementById('contact_is_primary').checked = contactData.is_primary == 1;
|
document.getElementById('contact_is_primary').checked = contactData.is_primary == 1;
|
||||||
|
|
||||||
// Change Button UI
|
// Change Button UI
|
||||||
const saveBtn = document.getElementById('btnSaveContact');
|
document.getElementById('btnSaveContact').innerHTML = "<i class='ri-pencil-fill'></i>";
|
||||||
saveBtn.innerHTML = "Update";
|
document.getElementById('btnSaveContact').title = "Update";
|
||||||
|
document.getElementById('btnSaveContact').style.background = "#02a8b5";
|
||||||
document.getElementById('contact_name').focus();
|
document.getElementById('contact_name').focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1841,14 +2012,8 @@ if (btnSaveContact) {
|
|||||||
toastr.success(editingId ? 'Contact Updated' : 'Contact Saved');
|
toastr.success(editingId ? 'Contact Updated' : 'Contact Saved');
|
||||||
|
|
||||||
// Reset Form UI
|
// Reset Form UI
|
||||||
document.getElementById('editing_contact_id').value = '';
|
resetContactForm();
|
||||||
document.getElementById('contact_name').value = '';
|
|
||||||
document.getElementById('contact_mobile').value = '';
|
|
||||||
document.getElementById('contact_designation').value = '';
|
|
||||||
document.getElementById('contact_is_primary').checked = false;
|
|
||||||
|
|
||||||
const saveBtn = document.getElementById('btnSaveContact');
|
|
||||||
saveBtn.innerHTML = "✔ Save";
|
|
||||||
// Refresh List
|
// Refresh List
|
||||||
openEditLeadModal(leadId);
|
openEditLeadModal(leadId);
|
||||||
} else {
|
} else {
|
||||||
@ -1857,7 +2022,11 @@ if (btnSaveContact) {
|
|||||||
}
|
}
|
||||||
} catch (error) { console.error(error); }
|
} catch (error) { console.error(error); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
const btnClearContact = document.getElementById('btnClearContact');
|
||||||
|
if (btnClearContact) {
|
||||||
|
btnClearContact.onclick = () => resetContactForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const statusOrder = ['New', 'Potential', 'Prospects', 'Not a Prospects'];
|
const statusOrder = ['New', 'Potential', 'Prospects', 'Not a Prospects'];
|
||||||
@ -1887,6 +2056,7 @@ if (btnSaveContact) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function convertDBFormatted(input) {
|
function convertDBFormatted(input) {
|
||||||
|
|
||||||
if (!input) return null;
|
if (!input) return null;
|
||||||
@ -1916,4 +2086,277 @@ function convertDBFormatted(input) {
|
|||||||
}
|
}
|
||||||
fetchLeads();
|
fetchLeads();
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
// COMPANY SEARCH — Odoo-style autocomplete
|
||||||
|
// UNIFIED COMPANY SEARCH — works for both Add & Edit modals
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
// Context config for each modal
|
||||||
|
const COMPANY_CTX = {
|
||||||
|
add: {
|
||||||
|
searchInput: 'companySearchInput',
|
||||||
|
dropdown: 'companyDropdown',
|
||||||
|
selected: 'companySelected',
|
||||||
|
selectedName: 'companySelectedName',
|
||||||
|
badge: 'shortNameBadge',
|
||||||
|
clientId: 'lead_client_id',
|
||||||
|
shortNameHidden:'lead_short_name_hidden',
|
||||||
|
formId: 'addLeadForm',
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
searchInput: 'editCompanySearchInput',
|
||||||
|
dropdown: 'editCompanyDropdown',
|
||||||
|
selected: 'editCompanySelected',
|
||||||
|
selectedName: 'editCompanySelectedName',
|
||||||
|
badge: 'editShortNameBadge',
|
||||||
|
clientId: 'edit_lead_client_id',
|
||||||
|
shortNameHidden:'edit_lead_short_name_hidden',
|
||||||
|
formId: 'editLeadForm',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Track state per modal
|
||||||
|
const companyState = { add: { isExisting: false, selectedValue: '' },
|
||||||
|
edit: { isExisting: false, selectedValue: '' } };
|
||||||
|
|
||||||
|
// Timer per modal
|
||||||
|
const companyTimers = { add: null, edit: null };
|
||||||
|
|
||||||
|
// ── Called from oninput on both search inputs ─────────────────────
|
||||||
|
function searchCompany(val, mode) {
|
||||||
|
const ctx = COMPANY_CTX[mode];
|
||||||
|
const state = companyState[mode];
|
||||||
|
clearTimeout(companyTimers[mode]);
|
||||||
|
|
||||||
|
const dropdown = document.getElementById(ctx.dropdown);
|
||||||
|
const badge = document.getElementById(ctx.badge);
|
||||||
|
|
||||||
|
if (!val || val.trim().length < 1) {
|
||||||
|
dropdown.style.display = 'none';
|
||||||
|
badge.textContent = '';
|
||||||
|
badge.style.display = 'none';
|
||||||
|
document.getElementById(ctx.shortNameHidden).value = '';
|
||||||
|
document.getElementById(ctx.shortNameField).value = '';
|
||||||
|
state.selectedValue = ''; // ← clear selected value tracking
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only generate short name for new clients while typing
|
||||||
|
if (!state.isExisting) {
|
||||||
|
generateAndSetShortName(val, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
companyTimers[mode] = setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`<?= base_url('sales/searchClients') ?>?q=${encodeURIComponent(val)}`);
|
||||||
|
const json = await res.json();
|
||||||
|
renderCompanyDropdown(json.data || [], val, mode);
|
||||||
|
} catch (e) {
|
||||||
|
renderCompanyDropdown([], val, mode);
|
||||||
|
}
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCompanyDropdown(results, query, mode) {
|
||||||
|
const ctx = COMPANY_CTX[mode];
|
||||||
|
const dropdown = document.getElementById(ctx.dropdown);
|
||||||
|
dropdown.innerHTML = '';
|
||||||
|
|
||||||
|
// ── Existing matches ──────────────────────────────────────────
|
||||||
|
results.forEach(client => {
|
||||||
|
const safe = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
const highlighted = client.client_name.replace(
|
||||||
|
new RegExp(`(${safe})`, 'gi'),
|
||||||
|
'<strong style="color:#185FA5;">$1</strong>'
|
||||||
|
);
|
||||||
|
const initials = client.client_name.substring(0, 2).toUpperCase();
|
||||||
|
|
||||||
|
const item = document.createElement('div');
|
||||||
|
item.style.cssText = 'display:flex;align-items:center;gap:10px;padding:9px 14px;cursor:pointer;font-size:13px;border-bottom:1px solid #f5f5f5;';
|
||||||
|
item.innerHTML = `
|
||||||
|
<div style="width:32px;height:32px;border-radius:50%;background:#B5D4F4;display:flex;
|
||||||
|
align-items:center;justify-content:center;font-size:11px;font-weight:600;
|
||||||
|
color:#0C447C;flex-shrink:0;">${initials}</div>
|
||||||
|
<div style="flex:1;min-width:0;">
|
||||||
|
<div style="font-weight:500;">${highlighted}</div>
|
||||||
|
<div style="font-size:11px;color:#aaa;margin-top:1px;">
|
||||||
|
<span style="background:#e8f4fd;color:#185FA5;padding:1px 6px;border-radius:4px;font-size:10px;">${client.short_name || 'N/A'}</span>
|
||||||
|
${client.email ? ` ${client.email}` : ''}
|
||||||
|
${client.phone ? ` · ${client.phone}` : ''}
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
item.onmouseenter = () => item.style.background = '#f7f9fc';
|
||||||
|
item.onmouseleave = () => item.style.background = '';
|
||||||
|
item.onclick = () => selectExistingCompany(client, mode);
|
||||||
|
dropdown.appendChild(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Divider ───────────────────────────────────────────────────
|
||||||
|
if (results.length > 0) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.style.cssText = 'height:1px;background:#eee;margin:4px 0;';
|
||||||
|
dropdown.appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Create new option ─────────────────────────────────────────
|
||||||
|
const createItem = document.createElement('div');
|
||||||
|
createItem.style.cssText = 'display:flex;align-items:center;gap:8px;padding:10px 14px;cursor:pointer;font-size:13px;color:#185FA5;font-weight:500;';
|
||||||
|
createItem.innerHTML = `
|
||||||
|
<span style="background:#E6F1FB;border-radius:4px;padding:2px 8px;font-size:11px;font-weight:700;">+</span>
|
||||||
|
Create <strong>"${query}"</strong>`;
|
||||||
|
createItem.onmouseenter = () => createItem.style.background = '#f0f7ff';
|
||||||
|
createItem.onmouseleave = () => createItem.style.background = '';
|
||||||
|
createItem.onclick = () => selectNewCompany(query, mode);
|
||||||
|
dropdown.appendChild(createItem);
|
||||||
|
|
||||||
|
dropdown.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── User picked an EXISTING client from dropdown ──────────────────
|
||||||
|
function selectExistingCompany(client, mode) {
|
||||||
|
const ctx = COMPANY_CTX[mode];
|
||||||
|
const state = companyState[mode];
|
||||||
|
state.isExisting = true;
|
||||||
|
state.selectedValue = client.client_name;
|
||||||
|
|
||||||
|
document.getElementById(ctx.clientId).value = client.id;
|
||||||
|
document.getElementById(ctx.shortNameHidden).value = client.short_name || ''; // ← only this
|
||||||
|
|
||||||
|
const searchInp = document.getElementById(ctx.searchInput);
|
||||||
|
searchInp.value = client.client_name;
|
||||||
|
searchInp.style.display = 'none';
|
||||||
|
document.getElementById(ctx.selectedName).textContent = client.client_name;
|
||||||
|
document.getElementById(ctx.selected).style.display = 'flex';
|
||||||
|
document.getElementById(ctx.dropdown).style.display = 'none';
|
||||||
|
|
||||||
|
const badge = document.getElementById(ctx.badge);
|
||||||
|
badge.textContent = client.short_name || '';
|
||||||
|
badge.style.cssText = 'display:inline-block;background:#E6F1FB;color:#185FA5;' +
|
||||||
|
'padding:2px 10px;border-radius:999px;font-size:11px;font-weight:600;' +
|
||||||
|
'letter-spacing:0.5px;vertical-align:middle;';
|
||||||
|
|
||||||
|
const form = document.getElementById(ctx.formId);
|
||||||
|
const ef = form.querySelector('[name="email"]');
|
||||||
|
const pf = form.querySelector('[name="phone"]');
|
||||||
|
if (ef && client.email && !ef.value) ef.value = client.email;
|
||||||
|
if (pf && client.phone && !pf.value) pf.value = client.phone;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── User clicked "Create new" ─────────────────────────────────────
|
||||||
|
async function selectNewCompany(name, mode) {
|
||||||
|
// ── Duplicate check ──────────────────────────────────
|
||||||
|
try {
|
||||||
|
const res = await fetch(`<?= base_url('sales/checkDuplicate') ?>?table=clients&field=client_name&value=${encodeURIComponent(name)}`);
|
||||||
|
const json = await res.json();
|
||||||
|
if (json.exists) {
|
||||||
|
toastr.warning(`"${name}" already exists. Search and select it from the dropdown instead.`);
|
||||||
|
document.getElementById(COMPANY_CTX[mode].searchInput).value = '';
|
||||||
|
document.getElementById(COMPANY_CTX[mode].searchInput).focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
// silently allow on network error — server will catch it
|
||||||
|
}
|
||||||
|
// ── Rest of your existing code (unchanged) ───────────
|
||||||
|
const ctx = COMPANY_CTX[mode];
|
||||||
|
const state = companyState[mode];
|
||||||
|
state.isExisting = false;
|
||||||
|
state.selectedValue = name; // ← track what was confirmed as new
|
||||||
|
document.getElementById(ctx.clientId).value = '';
|
||||||
|
const inp = document.getElementById(ctx.searchInput);
|
||||||
|
inp.value = name;
|
||||||
|
document.getElementById(ctx.dropdown).style.display = 'none';
|
||||||
|
generateAndSetShortName(name, mode);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── × button on pill ─────────────────────────────────────────────
|
||||||
|
function clearCompanySelection(mode) {
|
||||||
|
const ctx = COMPANY_CTX[mode];
|
||||||
|
const state = companyState[mode];
|
||||||
|
state.isExisting = false;
|
||||||
|
state.selectedValue = '';
|
||||||
|
|
||||||
|
document.getElementById(ctx.clientId).value = '';
|
||||||
|
document.getElementById(ctx.shortNameHidden).value = ''; // ← only this
|
||||||
|
document.getElementById(ctx.selected).style.display = 'none';
|
||||||
|
|
||||||
|
const badge = document.getElementById(ctx.badge);
|
||||||
|
badge.textContent = '';
|
||||||
|
badge.style.display = 'none';
|
||||||
|
|
||||||
|
const inp = document.getElementById(ctx.searchInput);
|
||||||
|
inp.style.display = '';
|
||||||
|
inp.value = '';
|
||||||
|
inp.focus();
|
||||||
|
|
||||||
|
if (mode === 'add') {
|
||||||
|
const form = document.getElementById(ctx.formId);
|
||||||
|
const ef = form.querySelector('[name="email"]');
|
||||||
|
const pf = form.querySelector('[name="phone"]');
|
||||||
|
if (ef) ef.value = '';
|
||||||
|
if (pf) pf.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Short name generation (shared) ───────────────────────────────
|
||||||
|
function generateAndSetShortName(name, mode) {
|
||||||
|
// FIX 2.2: trim ALL spaces before generating
|
||||||
|
const base = name.trim().replace(/\s+/g, '').substring(0, 10).toUpperCase();
|
||||||
|
if (!base) return;
|
||||||
|
|
||||||
|
checkShortNameInBothTables(base, (isDupe) => {
|
||||||
|
if (!isDupe) {
|
||||||
|
setShortName(base, mode);
|
||||||
|
} else {
|
||||||
|
let counter = 1;
|
||||||
|
const tryNext = () => {
|
||||||
|
const candidate = base.substring(0, 8) + String(counter).padStart(2, '0');
|
||||||
|
checkShortNameInBothTables(candidate, (exists) => {
|
||||||
|
if (exists) { counter++; tryNext(); }
|
||||||
|
else { setShortName(candidate, mode); }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
tryNext();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setShortName(val, mode) {
|
||||||
|
const ctx = COMPANY_CTX[mode];
|
||||||
|
document.getElementById(ctx.shortNameHidden).value = val; // ← only this
|
||||||
|
|
||||||
|
const badge = document.getElementById(ctx.badge);
|
||||||
|
badge.textContent = val;
|
||||||
|
badge.style.cssText = 'display:inline-block;background:#FAEEDA;color:#854F0B;' +
|
||||||
|
'padding:2px 10px;border-radius:999px;font-size:11px;font-weight:600;' +
|
||||||
|
'letter-spacing:0.5px;vertical-align:middle;';
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkDuplicateTableFieldValue(table, field, value, callback) {
|
||||||
|
if (!value || value.trim() === '') { callback(false); return; }
|
||||||
|
fetch(`<?= base_url('sales/checkDuplicate') ?>?table=${encodeURIComponent(table)}&field=${encodeURIComponent(field)}&value=${encodeURIComponent(value)}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(json => callback(json.exists === true))
|
||||||
|
.catch(() => callback(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkShortNameInBothTables(value, callback) {
|
||||||
|
let results = { clients: false, leads: false };
|
||||||
|
let completed = 0;
|
||||||
|
const done = () => { completed++; if (completed === 2) callback(results.clients || results.leads); };
|
||||||
|
checkDuplicateTableFieldValue('clients', 'short_name', value, (d) => { results.clients = d; done(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Close dropdowns on outside click ─────────────────────────────
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
['add', 'edit'].forEach(mode => {
|
||||||
|
const ctx = COMPANY_CTX[mode];
|
||||||
|
if (!e.target.closest('#' + ctx.searchInput) && !e.target.closest('#' + ctx.dropdown)) {
|
||||||
|
const dd = document.getElementById(ctx.dropdown);
|
||||||
|
if (dd) dd.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -129,7 +129,7 @@
|
|||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
$('.loader').fadeOut();
|
$('.loader').fadeOut();
|
||||||
$('.loader-mask').delay(350).fadeOut('slow');
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
toastr.warning('Something Went Wrong!', 'warning');
|
toastr.warning('Something Went Wrong!', 'Warning');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -346,10 +346,7 @@
|
|||||||
|
|
||||||
<div class="form-group col-md-3">
|
<div class="form-group col-md-3">
|
||||||
<label class="label-font-size" id="pod_no_label_1" for="pod_no">POD No with Courier Name <span id="pod_no_label_1_span" class="text-danger"></span></label>
|
<label class="label-font-size" id="pod_no_label_1" for="pod_no">POD No with Courier Name <span id="pod_no_label_1_span" class="text-danger"></span></label>
|
||||||
<input type="text" class="form-control" id="pod_no" placeholder="Enter POD NO"
|
<input type="text" class="form-control" id="pod_no" placeholder="Enter POD NO" value="<?= isset($ticket_data['pod_no']) ? $ticket_data['pod_no'] : '' ?>" name="pod_no">
|
||||||
value="<?= isset($ticket_data['pod_no']) ? $ticket_data['pod_no'] : '' ?>"
|
|
||||||
oninput="this.value = this.value.replace(/[^0-9]/g, '');"
|
|
||||||
name="pod_no">
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
<td><?php echo $row['display_name']; ?></td>
|
<td><?php echo $row['display_name']; ?></td>
|
||||||
<td><?php echo $row['old_value'].' => '.$row['new_value']; ?></td>
|
<td><?php echo $row['old_value'].' => '.$row['new_value']; ?></td>
|
||||||
<!-- <td><?php //echo $row['new_value']; ?></td> -->
|
<!-- <td><?php //echo $row['new_value']; ?></td> -->
|
||||||
<td><?php echo !empty($row['modified_by'])? $row['modified_by'] : "Created by employee"; ?></td>
|
<td><?php echo !empty($row['modified_by'])? $row['modified_by'] : "Created by employee"; ?> <?= $row['updated_by'] == "0" ? "(API)" : ""; ?></td>
|
||||||
<!-- <td><?php //echo $row['created_at']; ?></td> -->
|
<!-- <td><?php //echo $row['created_at']; ?></td> -->
|
||||||
<td><?php echo date("d-m-Y h:i:s A", strtotime($row['created_at'])) ?? " - "; ?></td>
|
<td><?php echo date("d-m-Y h:i:s A", strtotime($row['created_at'])) ?? " - "; ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -126,9 +126,8 @@
|
|||||||
<input type="text" class="form-control" id="emp_code" name="emp_code">
|
<input type="text" class="form-control" id="emp_code" name="emp_code">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
<label for="emp_mobile_no">Employee Mobile No<span
|
<label for="emp_mobile_no">Employee Mobile No<span class="text-danger"></span></label>
|
||||||
class="text-danger"></span></label>
|
<input type="text" class="form-control" id="emp_mobile_no" name="emp_mobile_no" maxlength="10" pattern="[0-9]{10}" inputmode="numeric" oninput="this.value = this.value.replace(/[^0-9]/g, '')">
|
||||||
<input type="text" class="form-control" id="emp_mobile_no" name="emp_mobile_no">
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-12">
|
<div class="form-group col-md-12">
|
||||||
@ -227,6 +226,7 @@
|
|||||||
|
|
||||||
function fetchTicketListData() {
|
function fetchTicketListData() {
|
||||||
closeFilterNav();
|
closeFilterNav();
|
||||||
|
|
||||||
var ticket_type = $('#ticket_type').val();
|
var ticket_type = $('#ticket_type').val();
|
||||||
var pod_no = $('#pod_no').val();
|
var pod_no = $('#pod_no').val();
|
||||||
var claim_no = $('#claim_no').val();
|
var claim_no = $('#claim_no').val();
|
||||||
@ -242,9 +242,24 @@
|
|||||||
var end_date = $('#endDate').val();
|
var end_date = $('#endDate').val();
|
||||||
var date_type = $('#date_type').val();
|
var date_type = $('#date_type').val();
|
||||||
|
|
||||||
|
function isEmpty(value) {
|
||||||
|
return value === null || value === undefined || value.toString().trim() === '' || value == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmpty(ticket_type) && isEmpty(claim_no) && isEmpty(emp_code) && isEmpty(claim_status) && isEmpty(emp_mobile_no) && isEmpty(tpa_id) && isEmpty(client_id) && isEmpty(insurer_id) && isEmpty(date_type)) {
|
||||||
|
toastr.warning('Please enter at least one search criteria.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (date_type) {
|
||||||
|
if (!start_date || !end_date) {
|
||||||
|
toastr.warning('Please select both Start Date and End Date.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var requestData = {
|
var requestData = {
|
||||||
ticket_type_id: ticket_type,
|
ticket_type_id: ticket_type,
|
||||||
pod_no: pod_no,
|
|
||||||
claim_number: claim_no,
|
claim_number: claim_no,
|
||||||
emp_code: emp_code,
|
emp_code: emp_code,
|
||||||
claim_status_id: claim_status,
|
claim_status_id: claim_status,
|
||||||
@ -412,22 +427,24 @@
|
|||||||
$('#pod_no').val('');
|
$('#pod_no').val('');
|
||||||
$('#claim_no').val('');
|
$('#claim_no').val('');
|
||||||
$('#emp_code').val('');
|
$('#emp_code').val('');
|
||||||
$('#claim_status').val('0');
|
|
||||||
$('#emp_mobile_no').val('');
|
$('#emp_mobile_no').val('');
|
||||||
$('#tpa_id').val('0');
|
|
||||||
$('#client_id').val('0');
|
|
||||||
$('#insurer_id').val('0');
|
|
||||||
$('#date_div').hide();
|
$('#date_div').hide();
|
||||||
$('#date_type').val('0');
|
$('#date_type').val('0');
|
||||||
$('#start_date').val('');
|
$('#start_date').val('');
|
||||||
$('#end_date').val('');
|
$('#end_date').val('');
|
||||||
|
|
||||||
|
$('#claim_status').val('0').select2();
|
||||||
|
$('#tpa_id').val('0').select2();
|
||||||
|
$('#client_id').val('0').select2();
|
||||||
|
$('#insurer_id').val('0').select2();
|
||||||
|
|
||||||
// Reset the date range picker to default
|
// Reset the date range picker to default
|
||||||
$('#reportrange').data('daterangepicker').setStartDate(moment().subtract(29, 'days'));
|
$('#reportrange').data('daterangepicker').setStartDate(moment().subtract(29, 'days'));
|
||||||
$('#reportrange').data('daterangepicker').setEndDate(moment());
|
$('#reportrange').data('daterangepicker').setEndDate(moment());
|
||||||
cb(start, end); // Update the displayed date range
|
cb(start, end); // Update the displayed date range
|
||||||
|
|
||||||
localStorage.setItem('filterData', JSON.stringify(filterData));
|
// localStorage.setItem('filterData', JSON.stringify(filterData));
|
||||||
window.location.href = "<?= base_url("/ticket/list") ?>";
|
window.location.href = "<?= base_url("/ticket/list") ?>";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -4555,17 +4555,17 @@ function ajaxRequest(formData) {
|
|||||||
$('.loader-mask').delay(350).fadeOut('slow');
|
$('.loader-mask').delay(350).fadeOut('slow');
|
||||||
|
|
||||||
console.log(res);
|
console.log(res);
|
||||||
if (res.status == 'Success' && res.code == 200) {
|
if (res.status == 'success' && res.code == 200) {
|
||||||
toastr.success('Mail sent Successfully', 'Success')
|
toastr.success('Mail sent Successfully', 'SUCCESS')
|
||||||
if(res.is_placement == true){
|
if(res.is_placement == true){
|
||||||
clearInterval(intervalId);
|
clearInterval(intervalId);
|
||||||
console.log('******** Intervel Cleared ********')
|
console.log('******** Intervel Cleared ********')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (res.messgae) {
|
if (res.messgae) {
|
||||||
toastr.error(res.messgae, 'Error')
|
toastr.error(res.messgae, 'ERROR')
|
||||||
} else {
|
} else {
|
||||||
toastr.error('Mail send failed', 'Error')
|
toastr.error('Mail send failed', 'ERROR')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
public/assets/images/sales_tracker_light_sb.png
Normal file
BIN
public/assets/images/sales_tracker_light_sb.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Loading…
Reference in New Issue
Block a user