FEAT_DEPENDENT_ADD_FLOW_IN_THE_POST_BENIFTS_APP

This commit is contained in:
VENKATESHWARAN 2026-08-03 10:57:55 +05:30
parent bf2908723a
commit 0928dc852b
14 changed files with 1867 additions and 61 deletions

View File

@ -235,6 +235,7 @@ $routes->group("/client", ["filter" => "authMVC"], function ($routes) {
$routes->group("/employee", ["filter" => "authMVC"], function ($routes) {
$routes->get("list", "EmployeeController::list");
$routes->get("pending-approvals", "EmployeeController::pendingApprovalsList");
$routes->get("search", "EmployeeController::search");
$routes->get("upload", "EmployeeController::employeesUplodWithEvents");
$routes->post("upload", "EmployeeController::employeesUplodWithEvents");
@ -511,6 +512,7 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) {
$routes->get('debug', 'ClaimReportDashboardController::debug');
$routes->get('debug/(:num)', 'ClaimReportDashboardController::debug/$1');
$routes->get('sync', 'ClaimReportDashboardController::sync');
$routes->get('download-excel', 'ClaimReportDashboardController::downloadExcel');
});
$routes->group('enrollment-collection-v1', static function ($routes) {
@ -531,11 +533,12 @@ $routes->cli('cli/croneDailyActivityReport', 'DashboardController::croneDailyAct
// Employee dependent APIs (no JWT / App-Signature; util uses authMVC only)
$routes->get('relationshipList', 'EmployeeRestController::relationshipList');
$routes->get('getEmployeeAndDependence', 'EmployeeRestController::getEmployeeAndDependence');
$routes->post('editEmployeeAndDependence', 'EmployeeRestController::editEmployeeAndDependence');
$routes->post('addEmployeeAndDependencev2', 'EmployeeRestController::addEmployeeAndDependencev2');
$routes->get('getEmployeeByPolicyv2', 'EmployeeRestController::getEmployeeByPolicyv2');
$routes->get('deleteDependence', 'EmployeeRestController::deleteDependence');
$routes->get('getEmployeeAndDependenceByClientId', 'EmployeeRestController::getEmployeeAndDependenceByClientId');
$routes->get('getPendingApprovalDependents', 'EmployeeRestController::getPendingApprovalDependents');
$routes->post('processDependentAdd', 'EmployeeRestController::processDependentAdd');
@ -764,26 +767,29 @@ $routes->group("employeeRest", ["filter" => ['GlobalPostFileUploadGuard', 'ratel
$routes->post("ecardRequest", "ApiServiceController::ecardRequest");
$routes->get("getWellnessURL", "ApiServiceController::getWellnessURL");
$routes->post("logHrActivity", "RestAuthenticationController::logHrActivity");
// $routes->post("getVerifiedUserData", "RestAuthenticationController::getVerifiedUserData");
$routes->post("getChatResponse", "ChatBotController::getChatResponse");
$routes->post("storeFireBase", "EmployeeRestController::storeFireBase");
$routes->get("getEmployeeProfile", "EmployeeRestController::getEmployeeProfile");
$routes->post("employeeUpload", "EmployeeRestController::employeeUpload");
$routes->get("relationshipList", "EmployeeRestController::relationshipList");
$routes->get("getEmployeeAndDependence", "EmployeeRestController::getEmployeeAndDependence");
$routes->post("editEmployeeAndDependence", "EmployeeRestController::editEmployeeAndDependence");
$routes->post("addEmployeeAndDependence", "EmployeeRestController::addEmployeeAndDependence");
$routes->get("getEmployeePolicy", "EmployeeRestController::getEmployeePolicy");
$routes->post("createOrUpdateEmployeePolicySiAmount", "EmployeeRestController::createOrUpdateEmployeePolicySiAmount");
$routes->get("deleteDependence", "EmployeeRestController::deleteDependence");
$routes->get("getEmployeeAndDependenceByClientId", "EmployeeRestController::getEmployeeAndDependenceByClientId");
// $routes->post("addEmployeeAndDependence", "EmployeeRestController::addEmployeeAndDependence");
// $routes->get("deleteDependence", "EmployeeRestController::deleteDependence");
// $routes->get("getEmployeePolicy", "EmployeeRestController::getEmployeePolicy");
$routes->get("getEmployeePolicy", "EmployeeRestController::getEmployeePolicyv2");
$routes->post("addEmployeeAndDependence", "EmployeeRestController::addEmployeeAndDependencev2");
$routes->get("deleteDependence", "EmployeeRestController::deleteDependencev2");
$routes->get("getPendingApprovalDependents", "EmployeeRestController::getPendingApprovalDependents");
$routes->post('processDependentAdd', 'EmployeeRestController::processDependentAdd');
$routes->get("getClientPolicy", "EmployeeRestController::getClientPolicy");
$routes->get("getClientRM", "EmployeeRestController::getClientRM");
$routes->get("getAddOnPolicy", "EmployeeRestController::getAddOnPolicy");
@ -876,6 +882,7 @@ $routes->group("employeeRest", ["filter" => ['GlobalPostFileUploadGuard', 'ratel
$routes->get('all', 'ClaimReportDashboardController::all');
$routes->get('debug', 'ClaimReportDashboardController::debug');
$routes->get('debug/(:num)', 'ClaimReportDashboardController::debug/$1');
$routes->get('download-excel', 'ClaimReportDashboardController::downloadExcel');
});
$routes->group('enrollment-collection-v1', static function ($routes) {

View File

@ -6,6 +6,9 @@ use App\Models\ClaimReportDashboardModel;
use App\Models\ClaimsCollectionV2DashboardModel;
use App\Models\ClaimDumpFileModel;
use CodeIgniter\API\ResponseTrait;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
/**
* Claims Collection dashboard API using claim_report.
@ -554,4 +557,199 @@ class ClaimReportDashboardController extends BaseController
->setBody($body);
}
/**
* Download Excel of TPA dump rows linked from claim_report for a policy.
*
* Query: client_policy_id (or client_policy) required.
* Uses claim_report.source_table + source_row_id to load dump rows.
* Excel columns = only fields AFTER master_reject_reason (that column and earlier are excluded).
*
* GET /util/claims-collection-report/download-excel?client_policy_id=123
*/
public function downloadExcel()
{
@set_time_limit(0);
@ini_set('max_execution_time', '0');
$policyId = $this->resolvePolicyId();
if ($policyId <= 0) {
return $this->respond([
'status' => false,
'message' => 'client_policy_id is required.',
], 422);
}
$db = db_connect();
if (!$db->tableExists('claim_report')) {
return $this->respond([
'status' => false,
'message' => 'Table claim_report does not exist.',
], 500);
}
$allowedDumpTables = [
'claims_dump_vidal',
'claims_dump_abhi',
'claims_dump_medi_assist',
'claims_dump_fhpl',
'claims_dump_reliance',
'claims_dump_icici',
];
$reportRows = $db->table('claim_report')
->select('source_table, source_row_id')
->where('client_policy_id', $policyId)
->where('is_active', 1)
->where('source_table IS NOT NULL', null, false)
->where('source_row_id IS NOT NULL', null, false)
->get()
->getResultArray();
if ($reportRows === []) {
return $this->respond([
'status' => false,
'message' => 'No claim_report records with dump linkage found for this policy.',
], 404);
}
// Group dump IDs by source_table
$idsByTable = [];
foreach ($reportRows as $row) {
$table = trim((string) ($row['source_table'] ?? ''));
$dumpId = (int) ($row['source_row_id'] ?? 0);
if ($table === '' || $dumpId <= 0 || ! in_array($table, $allowedDumpTables, true)) {
continue;
}
if (!$db->tableExists($table)) {
continue;
}
$idsByTable[$table][$dumpId] = $dumpId;
}
if ($idsByTable === []) {
return $this->respond([
'status' => false,
'message' => 'No valid TPA dump table references found for this policy.',
], 404);
}
$spreadsheet = new Spreadsheet();
$spreadsheet->removeSheetByIndex(0);
$sheetIndex = 0;
$totalRows = 0;
foreach ($idsByTable as $dumpTable => $dumpIds) {
$dumpIds = array_values($dumpIds);
$exportColumns = $this->getDumpColumnsAfterMasterReject($db, $dumpTable);
if ($exportColumns === []) {
continue;
}
$dumpRows = $db->table($dumpTable)
->whereIn('id', $dumpIds)
->orderBy('id', 'ASC')
->get()
->getResultArray();
if ($dumpRows === []) {
continue;
}
$sheetTitle = substr($dumpTable, 0, 31);
$sheet = $spreadsheet->createSheet($sheetIndex);
$sheet->setTitle($sheetTitle);
$sheetIndex++;
// Headers
foreach ($exportColumns as $colIdx => $colName) {
$sheet->setCellValue(
Coordinate::stringFromColumnIndex($colIdx + 1) . '1',
$colName
);
}
// Data
$excelRow = 2;
foreach ($dumpRows as $dumpRow) {
foreach ($exportColumns as $colIdx => $colName) {
$sheet->setCellValue(
Coordinate::stringFromColumnIndex($colIdx + 1) . $excelRow,
$dumpRow[$colName] ?? null
);
}
$excelRow++;
$totalRows++;
}
}
if ($sheetIndex === 0 || $totalRows === 0) {
return $this->respond([
'status' => false,
'message' => 'No dump table rows found for the linked claim_report records.',
], 404);
}
$spreadsheet->setActiveSheetIndex(0);
$policy = $db->table('client_policy')
->select('policy_no')
->where('id', $policyId)
->get()
->getRowArray();
$policyNo = trim((string) ($policy['policy_no'] ?? ''));
if ($policyNo === '') {
$policyNo = (string) $policyId;
}
// Safe filename segment
$policyNoSafe = preg_replace('/[^A-Za-z0-9_\-]+/', '_', $policyNo) ?: (string) $policyId;
$filename = 'claim_report_' . $policyNoSafe . '_' . date('Ymd_His') . '.xlsx';
ob_start();
(new Xlsx($spreadsheet))->save('php://output');
$excelOutput = ob_get_clean();
return $this->response
->setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"')
->setHeader('Cache-Control', 'max-age=0')
->setBody($excelOutput);
}
/**
* Return dump-table column names that appear AFTER master_reject_reason
* (master_reject_reason itself and all earlier columns are excluded).
*
* @return list<string>
*/
protected function getDumpColumnsAfterMasterReject($db, string $table): array
{
$fields = $db->getFieldNames($table);
if ($fields === [] || $fields === false) {
return [];
}
$marker = null;
foreach ($fields as $idx => $name) {
$lower = strtolower((string) $name);
if (
$lower === 'master_reject_reason'
|| $lower === 'master_rejected_reason'
|| $lower === 'master_rejection_reason'
) {
$marker = $idx;
break;
}
}
// Marker not found — export nothing rather than leaking internal columns
if ($marker === null) {
return [];
}
return array_values(array_slice($fields, $marker + 1));
}
}

View File

@ -164,6 +164,39 @@ class EmployeeController extends AdminController
$this->loadLayout('employee_list', $data);
}
public function pendingApprovalsList()
{
$data = [];
$filterData = $this->request->getGet() ?: [];
$data['employees'] = $this->employeePolicyModel->getPendingApprovalDependents(
client_id: $filterData['client_id'] ?? null,
policy_id: $filterData['policy_id'] ?? null,
branch_id: $filterData['branch_id'] ?? null,
);
$data['getData'] = [
'client_id' => $filterData['client_id'] ?? '0',
'policy_id' => $filterData['policy_id'] ?? '0',
'branch_id' => $filterData['branch_id'] ?? '0',
'emp_code' => '',
'emp_name' => '',
'status' => ['pending_approval'],
];
// AJAX filter submit → return table HTML only
if (count($this->request->getGet())) {
$html = view('employee_data_list', $data);
return $this->respond(['status' => true, 'html' => $html], 200);
}
// Default page load → list all pending dependents
$data['tab_name'] = 'Pending Approvals';
$data['page_name'] = 'Pending Approvals';
$data['default_table_html'] = view('employee_data_list', $data);
$this->loadLayout('employee_pending_approvals_list', $data);
}
public function getClientWithPolicies()
{
try {

View File

@ -327,12 +327,26 @@ class EmployeeRestController extends AdminController
}
}
public function createEmployeePolicyData($employee_id, $emp_code, $client_id, $client_policy_id, $basic_cover_si = null, $payable_employee = 0)
public function createEmployeePolicyData($employee_id, $emp_code, $client_id, $client_policy_id, $basic_cover_si = null, $payable_employee = 0, $extra_data = [])
{
$client_policy = $this->clientPolicyModel->where('id', $client_policy_id)->where('client_id', $client_id)->first();
$policy_terms = json_decode($client_policy['policy_terms']);
$dependent_effective_date = $extra_data['dependent_effective_date'] ?? null;
$hr_id = $extra_data['hr_id'] ?? null;
$self_data = $this->employeeModel
->select('employees.id')
->join('employee_polices', 'employee_polices.employee_id = employees.id')
->where('employee_polices.client_policy_id', $client_policy_id)
->where('employees.emp_code', $emp_code)
->where('employees.client_id', $client_id)
->where('employees.relationship', 'self')
->where('employees.is_active', 1)
->where('employee_polices.is_active', 1)
->first();
if ($basic_cover_si == null) {
$client_policy = $this->clientPolicyModel->where('id', $client_policy_id)->where('client_id', $client_id)->first();
$policy_terms = json_decode($client_policy['policy_terms']);
$basic_cover_si = $policy_terms->sum_insured;
}
@ -344,7 +358,7 @@ class EmployeeRestController extends AdminController
->where('client_policy_id', $client_policy_id)
->where('employee_id', $employee_id)
->where('is_active', 1)
->set(['basic_cover_si' => $basic_cover_si])
->set(['basic_cover_si' => $basic_cover_si, 'updated_by' => $self_data['id'] ?? null])
->update();
} else {
@ -352,8 +366,11 @@ class EmployeeRestController extends AdminController
$data['client_policy_id'] = $client_policy_id;
$data['basic_cover_si'] = $basic_cover_si;
$data['payable_employee'] = $payable_employee;
$data['status'] = 'draft';
$data['date_coverage'] = $this->getEmployeeCoverageDate($emp_code, $client_policy_id);
$data['policy_end_date'] = $client_policy['policy_end_date'];
$data['date_coverage'] = $this->convertDateFormatYMD($dependent_effective_date);
$data['status'] = 'pending_approval';
$data['created_by'] = $hr_id ?? $self_data['id'] ?? null;
$data['emp_policy_created_by'] = $hr_id ? "HR" : 'USER';
$this->employeePolicyModel->insert($data);
}
@ -2394,21 +2411,21 @@ class EmployeeRestController extends AdminController
$slab_details = $this->policesModel->getPolicySlabRatesForEmpOnboard($client_policy_id, $client_id);
// print_r($slab_details);die();
$existing_famility_details = $this->employeeModel->getEmpFamilybyEmpCode(client_id: $client_id, client_policy_id: $client_policy_id, emp_code: $emp_code, emp_status: ['draft', 'enrolled'], policy_status: ['draft', 'enrolled']);
$existing_famility_details = $this->employeeModel->getEmpFamilybyEmpCode(client_id: $client_id, client_policy_id: $client_policy_id, emp_code: $emp_code, emp_status: ['draft', 'enrolled', 'pending_approval'], policy_status: ['draft', 'enrolled', 'pending_approval']);
if (! count($existing_famility_details) && $policy_type == 2) //top up addon only
{
//get basepolicy id then pull emplist from base policy if only current policy is DA addon policy and emplist is zero
// echo 'inside';
$client_policy_id = $base_policy;
$existing_famility_details = $this->employeeModel->getEmpFamilybyEmpCode(client_id: $client_id, client_policy_id: $client_policy_id, emp_code: $emp_code, emp_status: ['draft', 'enrolled'], policy_status: ['draft', 'enrolled']);
$existing_famility_details = $this->employeeModel->getEmpFamilybyEmpCode(client_id: $client_id, client_policy_id: $client_policy_id, emp_code: $emp_code, emp_status: ['draft', 'enrolled', 'pending_approval'], policy_status: ['draft', 'enrolled', 'pending_approval']);
}
$file = ['id' => null, 'client_id' => $client_id, 'policy_id' => $client_policy_id, 'action' => 'inception'];
// dd($this->employeeModel->getLastQuery());
// print_r($existing_famility_details);die();
$employee_data_group_by_family = data_group_by_family($existing_famility_details, $data_source = 'db');
// print_r(($employee_data_group_by_family));//die();
// print_r(($employee_data_group_by_family)); die();
$existing_units = $this->clientBranchModel->getExisitingUnits(client_id: $client_id, client_branch_id: $client_branch_id);
@ -6331,4 +6348,529 @@ class EmployeeRestController extends AdminController
}
}
//---------------------------------------------------------------------------------------------------------------------------------------------
// Employee Dependent Add API Endpoints
//---------------------------------------------------------------------------------------------------------------------------------------------
public function addEmployeeAndDependencev2()
{
try {
$data = $this->request->getJSON();
// print_r($data);die;
if ($data) {
$Count = 0;
foreach ($data as $item) {
$self_data = $this->employeeModel
->select('employees.id')
->join('employee_polices', 'employee_polices.employee_id = employees.id')
->where('employee_polices.client_policy_id', $item->client_policy_id)
->where('employees.emp_code', $item->emp_code)
->where('employees.client_id', $item->client_id)
->where('employees.relationship', 'self')
->where('employees.is_active', 1)
->where('employee_polices.is_active', 1)
->first();
$item->family_floater_key = $this->RelationshipMap($item->relationship);
$item->gender = $this->GenderMap($item->relationship, $item->emp_code);
$item->dob = $this->convertDateFormatYMD($item->dob);
$item->emp_status = 'pending_approval';
$item->emp_created_by = isset($item->hr_id) ? 'HR' : 'USER';
if (isset($item->id)) {
//update old data
$id = $item->id;
$item->updated_by = $item->hr_id ?? $self_data['id'];
$employee = $this->employeeModel->where('is_active', 1)->update($id, (array) $item);
if ($employee) {
$Count++;
}
} else {
//create new data
$item->created_by = $item->hr_id ?? $self_data['id'];
$employee = $this->employeeModel->insert($item);
if ($employee) {
$Count++;
}
}
}
if ($Count > 0) {
if (isset($data[0]->id)) {
$this->createEmployeePolicyData($data[0]->id, $data[0]->emp_code, $data[0]->client_id, $data[0]->client_policy_id, $data[0]->basic_cover_si);
} else {
$payable_employee = $this->getEmployeePayableValue($data[0]->client_policy_id, $data[0]->relationship);
$this->createEmployeePolicyData($employee, $data[0]->emp_code, $data[0]->client_id, $data[0]->client_policy_id, $data[0]->basic_cover_si, $payable_employee, ['dependent_effective_date' => $data[0]->dependent_effective_date, 'hr_id' => $data[0]->hr_id ?? null]);
}
$this->updatePremiumAmount($data[0]->client_policy_id, $data[0]->emp_code, $data[0]->client_branch_id);
$result = [];
return $this->respond(['status' => 'success', 'code' => 200, 'data' => $result], 200);
} else {
$result = "No Matches";
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => $result], 200);
}
}else{
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => "Requested parameters are required"], 200);
}
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
}
}
public function deleteDependencev2()
{
try {
if ($this->request->getGet('id')) {
$this->employeeModel->where('id', $this->request->getGet('id'))
->where('is_active', 1)
->set(['emp_status' => 'truncated', 'is_active' => 0])
->update();
$this->employeePolicyModel->where('employee_id', $this->request->getGet('id'))
->where('is_active', 1)
->set(['status' => 'truncated', 'is_active' => 0])
->update();
return $this->respond(['status' => 'success', 'code' => 200, 'data' => []], 200);
} else {
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => []], 200);
}
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
}
}
public function getEmployeeByPolicyv2()
{
try {
$id = $this->request->getGet('id') ?? $this->request->getPost('id');
$emp_code = $this->request->getGet('emp_code') ?? $this->request->getPost('emp_code');
$client_id = $this->request->getGet('client_id') ?? $this->request->getPost('client_id');
$client_branch_id = $this->request->getGet('client_branch_id') ?? $this->request->getPost('client_branch_id');
$client_policy_id = $this->request->getGet('client_policy_id') ?? $this->request->getPost('client_policy_id');
if (empty($id) || empty($emp_code) || empty($client_id) || empty($client_branch_id) || empty($client_policy_id)) {
return $this->respond(['status' => 'failed', 'code' => 400, 'message' => 'Required parameters missing: id, emp_code, client_id, client_branch_id and client_policy_id are required', 'data' => []], 200);
}
$keysToRemove = ["removable_keys"];
// Filtered by client_policy_id — returns at most one GMC policy
$empPolicyList = $this->employeeModel->getEmployeePolicy($id, $client_policy_id);
$array = $empPolicyList[0] ?? null;
if (!$array) {
return $this->respond(['status' => 'failed', 'code' => 404, 'message' => 'Employee policy not found for the given employee and client policy', 'data' => []], 200);
}
// GMC only (policy_type_id = 2)
if ((int) $array->policy_type_id !== 2) {
return $this->respond(['status' => 'failed', 'code' => 400, 'message' => 'Only GMC policy is supported for dependent add', 'data' => []], 200);
}
$empData = $this->employeeModel
->where('emp_code', $emp_code)
->where('client_id', $client_id)
->where('client_branch_id', $client_branch_id)
->where('is_active', 1)
->where('is_addon_value', 0)
->groupStart()
->whereIn('family_floater_key', ['self', 'spouse', 'child'])
->orWhereIn('relationship', ['Self', 'Spouse', 'Son', 'Daughter'])
->groupEnd()
->findAll();
$decodedArray = json_decode($array->Policy_Terms);
$refusingData = (object) array_diff_key((array) $decodedArray, array_flip($keysToRemove));
$array->Policy_Terms = $refusingData;
$getSlabAndGridData = $this->policesModel->getPolicySlabRatesForEmpOnboard($array->ClientPolicyId, $array->ClientId);
$array->SlabRates = $getSlabAndGridData['slab_rates'];
$array->GridMaster = $getSlabAndGridData['grid_master'];
$array->eCardDownload = $array->tpa_id != null
? base_url('download-e-card/') . $array->rand_string . '/1'
: null;
$familyFloates = $array->Policy_Terms->family_floaters ?? (object) [];
// Only spouse + children (ignore parents / parents-in-law)
$hasSpouse = isset($familyFloates->spouse) && (int) $familyFloates->spouse > 0;
$hasChildren = isset($familyFloates->childrens) && (int) $familyFloates->childrens > 0;
$spouseCount = $hasSpouse ? (int) $familyFloates->spouse : 0;
$childrenCount = $hasChildren ? (int) $familyFloates->childrens : 0;
$spouseChildrenTerms = (object) [
'self' => isset($familyFloates->self) ? (int) $familyFloates->self : 1,
'spouse' => $spouseCount,
'childrens' => $childrenCount,
];
$array->notes = $this->FloterNotesConvertion($spouseChildrenTerms);
$allowedRelationships = [];
if ($hasSpouse) {
$allowedRelationships[] = 'Spouse';
}
if ($hasChildren) {
$allowedRelationships[] = 'Son';
$allowedRelationships[] = 'Daughter';
}
$array->allowed_relationships = $allowedRelationships;
if (!empty($getSlabAndGridData['slab_rates'][0]) && ($getSlabAndGridData['slab_rates'][0]['premium_type'] == 1 || $getSlabAndGridData['slab_rates'][0]['premium_type'] == 3)) {
$array->floter_text_heading = 'Floater Sum Insured';
$array->floter_text_description = 'This is a floater sum insured. A floater is a type of sum insured that provides coverage to more than one member ot a family at the same time. Simply put, its a single insurance cover for the entire family.';
} else {
$array->floter_text_heading = 'Sum Insured';
$array->floter_text_description = '';
}
$floters = $this->FloterConvertion($spouseChildrenTerms);
$data = [];
$dependent_and_si_value = 0;
$dependent_and_si_premium_value = 0;
$dependent_and_si_gst_value = 0;
foreach ($floters as $familyFloatesValue) {
$dependent = preg_replace('/\d/', '', $familyFloatesValue);
if (!in_array($dependent, ['self', 'spouse', 'child'], true)) {
continue;
}
if ($dependent === 'spouse' && !$hasSpouse) {
continue;
}
if ($dependent === 'child' && !$hasChildren) {
continue;
}
if (count($empData)) {
foreach ($empData as $key => $value) {
$empFloaterKey = !empty($value['family_floater_key'])
? preg_replace('/\d/', '', strtolower(trim($value['family_floater_key'])))
: $this->RelationshipMap($value['relationship']);
$relationship = strtolower(trim($value['relationship'] ?? ''));
$matches = ($empFloaterKey === $dependent)
|| ($dependent === 'spouse' && $relationship === 'spouse')
|| ($dependent === 'child' && in_array($relationship, ['son', 'daughter'], true))
|| ($dependent === 'self' && $relationship === 'self');
if ($matches) {
$employee_policy = $this->employeePolicyModel
->where('employee_id', $value['id'])
->where('client_policy_id', $array->ClientPolicyId)
->where('is_active', 1)
->get()
->getRow();
if (isset($employee_policy->basic_cover_si)) {
$dependent_and_si_value = ($dependent_and_si_value == 0) ? $employee_policy->basic_cover_si : $dependent_and_si_value;
}
if (isset($employee_policy->payable_employee) && $employee_policy->payable_employee == 1) {
if (isset($employee_policy->rata_premimum)) {
$dependent_and_si_premium_value = $dependent_and_si_premium_value + $employee_policy->rata_premimum;
}
if (isset($employee_policy->gst)) {
$dependent_and_si_gst_value = $dependent_and_si_gst_value + $employee_policy->gst;
}
}
$temp = [];
$temp['is_value_exist'] = true;
$temp['data']['family_floater_key'] = $familyFloatesValue;
$temp['data']['employee_id'] = $value['id'];
$temp['data']['relationship'] = $value['relationship'];
$temp['data']['name'] = $value['name'];
$temp['data']['dob'] = $this->convertDateFormatDMY($value['dob']);
$temp['data']['client_policy_id'] = $array->ClientPolicyId;
$temp['data']['form_type'] = $dependent;
$temp['data']['basic_cover_si'] = isset($employee_policy->basic_cover_si) ? $employee_policy->basic_cover_si : 0;
$temp['data']['age_validation'] = $this->getAgeRange($decodedArray, $familyFloatesValue);
array_push($data, $temp);
unset($empData[$key]);
$floters = array_diff($floters, [$familyFloatesValue]);
break;
}
}
}
}
// Remaining allowed spouse/child slots → Add buttons
if (count($floters)) {
foreach ($floters as $familyFloatesValue) {
$dependent = preg_replace('/\d/', '', $familyFloatesValue);
if (!in_array($dependent, ['spouse', 'child'], true)) {
continue;
}
if ($dependent === 'spouse' && !$hasSpouse) {
continue;
}
if ($dependent === 'child' && !$hasChildren) {
continue;
}
$temp2 = [];
$temp2['is_value_exist'] = false;
$temp2['data']['family_floater_key'] = $familyFloatesValue;
$temp2['data']['client_policy_id'] = $array->ClientPolicyId;
$temp2['data']['button_name'] = 'Add ' . ucfirst(str_replace('_', ' ', $dependent));
$temp2['data']['form_type'] = $dependent;
$temp2['data']['age_validation'] = $this->getAgeRange($decodedArray, $familyFloatesValue);
$temp2['data']['allowed_relationships'] = $dependent === 'spouse'
? ['Spouse']
: ['Son', 'Daughter'];
array_push($data, $temp2);
}
}
$array->mapped_family_floaters = $data;
$array->type = 'GMC';
$array->family_floaters_of_dependent_and_si_value = $dependent_and_si_value > 0 ? $dependent_and_si_value : 0;
$array->family_floaters_of_dependent_and_si_premium_value = $dependent_and_si_premium_value > 0 ? round($dependent_and_si_premium_value) : 0;
$array->family_floaters_of_dependent_and_gst_value = $dependent_and_si_gst_value > 0 ? round($dependent_and_si_gst_value) : 0;
$employeePolicy = $this->getEmployeePolicyMembersForDependentAdd(
$emp_code,
$client_id,
$client_branch_id,
$client_policy_id
);
// Show add button when any spouse/child slot is still available
$addButtonShow = false;
foreach ($data as $floaterItem) {
if (empty($floaterItem['is_value_exist'])) {
$addButtonShow = true;
break;
}
}
return $this->respond([
'status' => 'success',
'code' => 200,
'message' => 'Employee policy and dependent details fetched successfully',
'data' => $array,
'EmployeePolicy' => $employeePolicy,
'add_button_show' => $addButtonShow,
], 200);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'code' => 500, 'message' => 'Something went wrong while fetching employee policy details', 'data' => $e->getMessage()], 500);
}
}
/**
* Fetch Self / Spouse / Children employee+policy rows for a GMC client policy.
* Same data shape as EmployeePolicy in getEmployeeActiveOrInactivePolicy.
*/
private function getEmployeePolicyMembersForDependentAdd($emp_code, $client_id, $client_branch_id, $client_policy_id)
{
$employeeIds = $this->employeeModel
->select('id')
->where('emp_code', $emp_code)
->where('client_id', $client_id)
->where('client_branch_id', $client_branch_id)
->where('is_active', 1)
->where('is_addon_value', 0)
->groupStart()
->whereIn('family_floater_key', ['self', 'spouse', 'child'])
->orWhereIn('relationship', ['Self', 'Spouse', 'Son', 'Daughter'])
->groupEnd()
->findColumn('id');
if (empty($employeeIds)) {
return [];
}
return $this->employeePolicyModel
->select('employees.*, employee_polices.employee_id, employee_polices.basic_cover_si, employee_polices.premium, employee_polices.gst, employee_polices.tpa_id, employee_polices.rand_string, employee_polices.uhid as uhid')
->join('employees', 'employee_polices.employee_id = employees.id', 'left')
->whereIn('employee_polices.employee_id', $employeeIds)
->where('employee_polices.client_policy_id', $client_policy_id)
->where('employee_polices.is_active', 1)
->findAll();
}
public function processDependentAdd()
{
try {
$data = $this->request->getJSON(true);
if (empty($data)) {
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'Request body is required'], 200);
}
$employee_id = $data['employee_id'] ?? null;
$client_policy_id = $data['client_policy_id'] ?? null;
$hr_id = $data['hr_id'] ?? null;
$status = $data['status'] ?? "approved";
$updated_by = $hr_id ?? get_session_userid() ?? null;
// Approver role: HR (app) or ACM (internal portal session user)
$approved_by_role = ! empty($hr_id) ? 'HR' : 'ACM';
if (empty($employee_id)) {
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'employee_id is required'], 200);
}
if (empty($client_policy_id)) {
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'client_policy_id is required'], 200);
}
if (empty($status)) {
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'status is required'], 200);
}
if ($status !== 'approved' && $status !== 'rejected') {
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'status is invalid'], 200);
}
$employee = $this->employeeModel
->where('id', $employee_id)
->where('is_active', 1)
->first();
if (!$employee) {
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Employee not found or inactive'], 200);
}
if (($employee['emp_status'] ?? null) === 'active') {
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'Dependent is already approved'], 200);
}
if (($employee['emp_status'] ?? null) !== 'pending_approval') {
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'Dependent is not pending approval'], 200);
}
$employee_policy = $this->employeePolicyModel
->where('employee_id', $employee_id)
->where('client_policy_id', $client_policy_id)
->where('is_active', 1)
->first();
if (!$employee_policy) {
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Employee policy not found or inactive'], 200);
}
if ($status === 'approved') {
$employee_update_data = [
'updated_by' => $updated_by,
'approved_by' => $approved_by_role,
'emp_status' => 'active',
];
$policy_update_data = [
'updated_by' => $updated_by,
'approved_by' => $approved_by_role,
'status' => 'active',
];
} else {
$employee_update_data = [
'updated_by' => $updated_by,
'approved_by' => $approved_by_role,
'emp_status' => 'rejected',
'is_active' => 0,
];
$policy_update_data = [
'updated_by' => $updated_by,
'approved_by' => $approved_by_role,
'status' => 'rejected',
'is_active' => 0,
];
}
$employee_updated = $this->employeeModel
->where('id', $employee_id)
->where('is_active', 1)
->set($employee_update_data)
->update();
if (!$employee_updated) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => 'Failed to update employee status'], 200);
}
$policy_updated = $this->employeePolicyModel
->where('employee_id', $employee_id)
->where('client_policy_id', $client_policy_id)
->where('is_active', 1)
->set($policy_update_data)
->update();
if (!$policy_updated) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => 'Failed to update employee policy status'], 200);
}
return $this->respond(['status' => 'success', 'code' => 200, 'data' => []], 200);
} catch (\Exception $e) {
log_message('error', 'Error in processDependentAdd: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine());
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
}
}
/**
* List pending_approval dependents (API + mobile).
* Filters: client_id, client_branch_id / branch_id, client_policy_id / policy_id, optional search.
*/
public function getPendingApprovalDependents()
{
try {
$client_id = $this->request->getGet('client_id');
$branch_id = $this->request->getGet('client_branch_id') ?? $this->request->getGet('branch_id');
$policy_id = $this->request->getGet('client_policy_id') ?? $this->request->getGet('policy_id');
$search = trim((string) ($this->request->getGet('search') ?? ''));
$empData = $this->employeePolicyModel->getPendingApprovalDependents(
client_id: $client_id ?? 0,
policy_id: $policy_id ?? 0,
branch_id: $branch_id ?? 0,
search: $search
);
if ($empData) {
return $this->respond([
'status' => 'success',
'code' => 200,
'message' => 'Pending approval dependents fetched successfully',
'data' => $empData,
], 200);
}
return $this->respond([
'status' => 'failed',
'code' => 404,
'message' => 'No pending approval dependents found',
'data' => [],
], 200);
} catch (\Exception $e) {
return $this->respond([
'status' => 'failed',
'code' => 500,
'message' => $e->getMessage(),
'data' => [],
], 500);
}
}
}

View File

@ -0,0 +1,81 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddDependentApprovalTrackingColumns extends Migration
{
public function up()
{
// employees: who created the dependent request, who approved (HR / ACM)
if (! $this->db->fieldExists('emp_created_by', 'employees')) {
$this->forge->addColumn('employees', [
'emp_created_by' => [
'type' => 'VARCHAR',
'constraint' => 50,
'null' => true,
'default' => null,
'after' => 'emp_status',
'comment' => 'Creator role: HR / USER',
],
]);
}
if (! $this->db->fieldExists('approved_by', 'employees')) {
$this->forge->addColumn('employees', [
'approved_by' => [
'type' => 'VARCHAR',
'constraint' => 50,
'null' => true,
'default' => null,
'after' => 'emp_created_by',
'comment' => 'Approver role: HR / ACM',
],
]);
}
// employee_polices: who created the policy row, who approved (HR / ACM)
if (! $this->db->fieldExists('emp_policy_created_by', 'employee_polices')) {
$this->forge->addColumn('employee_polices', [
'emp_policy_created_by' => [
'type' => 'VARCHAR',
'constraint' => 50,
'null' => true,
'default' => null,
'after' => 'status',
'comment' => 'Creator role: HR / USER',
],
]);
}
if (! $this->db->fieldExists('approved_by', 'employee_polices')) {
$this->forge->addColumn('employee_polices', [
'approved_by' => [
'type' => 'VARCHAR',
'constraint' => 50,
'null' => true,
'default' => null,
'after' => 'emp_policy_created_by',
'comment' => 'Approver role: HR / ACM',
],
]);
}
}
public function down()
{
if ($this->db->fieldExists('approved_by', 'employees')) {
$this->forge->dropColumn('employees', 'approved_by');
}
if ($this->db->fieldExists('emp_created_by', 'employees')) {
$this->forge->dropColumn('employees', 'emp_created_by');
}
if ($this->db->fieldExists('approved_by', 'employee_polices')) {
$this->forge->dropColumn('employee_polices', 'approved_by');
}
if ($this->db->fieldExists('emp_policy_created_by', 'employee_polices')) {
$this->forge->dropColumn('employee_polices', 'emp_policy_created_by');
}
}
}

View File

@ -808,7 +808,19 @@ abstract class BaseTpaClaimImportService
}
$ticketMasterModel = new TicketMasterModel();
return $ticketMasterModel->updateBatch($payload, 'id') !== false;
$ok = $ticketMasterModel->updateBatch($payload, 'id') !== false;
if ($ok) {
$ids = [];
foreach ($payload as $row) {
if (!empty($row['id'])) {
$ids[] = (int) $row['id'];
}
}
// updateBatch does not fire model afterUpdate callbacks
(new \App\Models\ClaimReportModel())->syncFromTicketIds($ids);
}
return $ok;
}
/**

View File

@ -55,4 +55,148 @@ class ClaimReportModel extends Model
'created_at',
'updated_at',
];
/**
* Push ticket_master fields into matching claim_report row(s).
* Match by ticket_id first; fallback to (client_policy_id, claim_number).
* Never creates new claim_report rows only updates existing ones.
* Failures are swallowed so ticket updates are never blocked.
*/
public function syncFromTicketId(int $ticketId): bool
{
if ($ticketId <= 0) {
return true;
}
try {
if (!$this->db->tableExists('claim_report')) {
return true;
}
$ticket = $this->db->table('ticket_master')
->where('id', $ticketId)
->get()
->getRowArray();
if (empty($ticket)) {
return true;
}
$update = $this->mapTicketToClaimReport($ticket);
if ($update === []) {
return true;
}
$exists = $this->db->table('claim_report')
->where('ticket_id', $ticketId)
->countAllResults() > 0;
if ($exists) {
$this->db->table('claim_report')
->where('ticket_id', $ticketId)
->update($update);
return true;
}
// Fallback: match by unique key when ticket_id not set on claim_report yet
$claimNumber = trim((string) ($ticket['claim_number'] ?? ''));
$clientPolicyId = (int) ($ticket['client_policy_id'] ?? 0);
if ($claimNumber === '' || $clientPolicyId <= 0) {
return true;
}
$this->db->table('claim_report')
->where('client_policy_id', $clientPolicyId)
->where('claim_number', $claimNumber)
->update($update);
return true;
} catch (\Throwable $e) {
log_message('error', 'claim_report syncFromTicketId failed for ticket {id}: {msg}', [
'id' => $ticketId,
'msg' => $e->getMessage(),
]);
return false;
}
}
/**
* Sync multiple ticket IDs (e.g. after updateBatch).
*
* @param list<int|string> $ticketIds
*/
public function syncFromTicketIds(array $ticketIds): void
{
$ids = array_values(array_unique(array_filter(array_map('intval', $ticketIds))));
foreach ($ids as $id) {
$this->syncFromTicketId($id);
}
}
/**
* @param array<string, mixed> $ticket
* @return array<string, mixed>
*/
protected function mapTicketToClaimReport(array $ticket): array
{
$now = date('Y-m-d H:i:s');
$relation = $ticket['relation'] ?? null;
if ($relation === null || $relation === '') {
$relation = $ticket['relationship'] ?? null;
}
$approved = $ticket['approved_amount'] ?? null;
$claimed = $ticket['claim_amount'] ?? null;
$incurred = $ticket['incurred_amount'] ?? null;
if ($incurred === null || $incurred === '') {
$incurred = $approved !== null && $approved !== '' ? $approved : $claimed;
}
$map = [
'tpa_id' => $ticket['tpa_id'] ?? null,
'client_id' => $ticket['client_id'] ?? null,
'client_policy_id' => $ticket['client_policy_id'] ?? null,
'file_id' => $ticket['file_id'] ?? null,
'ticket_id' => $ticket['id'] ?? null,
'claim_number' => isset($ticket['claim_number']) ? trim((string) $ticket['claim_number']) : null,
'emp_code' => $ticket['emp_code'] ?? null,
'tpa_no' => $ticket['tpa_no'] ?? null,
'emp_id' => $ticket['emp_id'] ?? null,
'insured_emp_id' => $ticket['insured_emp_id'] ?? null,
'claim_amount' => $claimed,
'approved_amount' => $approved,
'incurred_amount' => $incurred,
'si_amt' => $ticket['si_amt'] ?? null,
'tpa_claim_status' => $ticket['tpa_claim_status'] ?? null,
'claim_status_id' => $ticket['claim_status_id'] ?? null,
'tpa_claim_type' => $ticket['tpa_claim_type'] ?? null,
'tpa_ailments' => $ticket['tpa_ailments'] ?? null,
'doa' => $ticket['doa'] ?? null,
'dod' => $ticket['dod'] ?? null,
'date_of_intimat' => $ticket['date_of_intimat'] ?? null,
'settled_date' => $ticket['settled_date'] ?? null,
'approved_date' => $ticket['approved_date'] ?? null,
'claim_dump_date' => $ticket['claim_dump_date'] ?? null,
'hospital_name' => $ticket['hospital_name'] ?? null,
'hospital_city' => $ticket['hospital_city'] ?? null,
'hospital_state' => $ticket['hospital_state'] ?? null,
'hospital_pin_code' => $ticket['hospital_pin_code'] ?? null,
'hospital_address' => $ticket['hospital_address'] ?? null,
'relation' => $relation,
'is_active' => isset($ticket['is_active']) ? (int) $ticket['is_active'] : 1,
'updated_at' => $now,
];
// Do not overwrite claim_number with empty string
if ($map['claim_number'] === '') {
unset($map['claim_number']);
}
// Keep source_table / source_row_id as-is (provenance from dump sync)
return $map;
}
}

View File

@ -31,6 +31,8 @@ class EmployeeModel extends Model
"otp",
"family_floater_key",
"emp_status",
"emp_created_by",
"approved_by",
"created_by",
"updated_by",
"updated_at",
@ -151,7 +153,7 @@ class EmployeeModel extends Model
public function getEmployeePolicy($id)
public function getEmployeePolicy($id, $client_policy_id = null)
{
return $this->db->table('employee_polices')
@ -176,6 +178,9 @@ class EmployeeModel extends Model
->where('employee_polices.status !=', 'truncated')
->where('employee_polices.is_active', 1)
->where('employee_polices.employee_id', $id)
->when($client_policy_id, function($query) use ($client_policy_id){
return $query->where('employee_polices.client_policy_id', $client_policy_id);
})
->get()
->getResult();
}

View File

@ -16,6 +16,8 @@ class EmployeePolicyModel extends Model
"uhid",
"batch_id",
"status",
"emp_policy_created_by",
"approved_by",
"pre_existing_alignments",
"date_of_exit",
"reason_for_exit",
@ -375,6 +377,105 @@ class EmployeePolicyModel extends Model
return $res;
}
/**
* Pending-approval dependents (excludes Self) for a client / branch / policy.
*/
public function getPendingApprovalDependents($client_id = 0, $policy_id = 0, $branch_id = 0, $search = '')
{
$ecard_download_link = "
CASE
WHEN
LOWER(emp.relationship) = 'self' AND
employee_polices.tpa_id IS NOT NULL AND employee_polices.tpa_id != '' AND
employee_polices.uhid IS NOT NULL AND employee_polices.uhid != ''
THEN
CONCAT('" . base_url('download-e-card/') . "', employee_polices.rand_string, '/1')
ELSE
NULL
END AS ecard_download_link
";
$result = $this->select([
'employee_polices.*',
'policy_type.policy_type as policy_name',
'im.short_name as insurer_short_name',
'ib.branch_name as insurer_branch_name',
'ib.branch_code as insurer_branch_code',
'tpam.name as tpa_name',
'tpam.short_name as tpa_short_name',
'tpab.branch_code as tpa_branch_code',
'cm.client_name',
'cm.short_name as client_short_name',
'emp.relationship',
'emp.relationship_code',
'emp.change_event',
'emp.emp_code',
'emp.name',
'emp.email_corporate',
'emp.dob',
'DATE_FORMAT(emp.dob, "%d/%m/%Y") AS formatted_dob',
'emp.gender',
'emp.emp_status',
'emp.is_active as emp_is_active',
'emp.mobile as mobile',
'emp.doj',
'emp.basic_pay',
'emp.band as grade',
'policy_type.policy_type',
'client_branch.branch_name as client_branch_name',
'client_branch.branch_code as client_branch_code',
'cp.policy_no',
'cp.policy_type_id',
'cp.is_addon',
'(
select id from employees
where emp_code = emp.emp_code
and client_id = emp.client_id
and lower(relationship) = "self"
and is_active = 1
limit 1
) as self_employee_id',
$ecard_download_link
])
->join('employees emp', 'employee_polices.employee_id = emp.id')
->join('client_policy cp', 'employee_polices.client_policy_id = cp.id')
->join('policies pm', 'cp.policy_id = pm.id', 'left')
->join('policy_type', 'policy_type.id = cp.policy_type_id')
->join('insurers im', 'cp.insurer_id = im.id')
->join('insurer_branch ib', 'cp.insurer_branch_id = ib.id')
->join('tpa tpam', 'cp.tpa_id = tpam.id', 'left')
->join('tpa_branch tpab', 'cp.tpa_branch_id = tpab.id', 'left')
->join('clients cm', 'cp.client_id = cm.id')
->join('client_branch', 'emp.client_branch_id = client_branch.id')
->where('employee_polices.is_active', 1)
->where('emp.is_active', 1)
->where('employee_polices.status', 'pending_approval')
->where('emp.emp_status', 'pending_approval')
->where("LOWER(emp.relationship) != 'self'", null, false)
->orderBy('emp.emp_code', 'ASC')
->orderBy('employee_polices.employee_id', 'ASC');
if (is_string($client_id) && preg_match('/^[a-f0-9]{32}$/i', $client_id)) {
$result->where('md5(emp.client_id)', $client_id);
} elseif ($client_id != 0 && ! empty($client_id)) {
$result->where('emp.client_id', $client_id);
}
if ($branch_id != 0 && ! empty($branch_id)) {
$result->where('emp.client_branch_id', $branch_id);
}
if ($policy_id != 0 && ! empty($policy_id)) {
$result->where('employee_polices.client_policy_id', $policy_id);
}
if (! empty($search)) {
$this->applyEmployeeGlobalSearch($result, $search);
}
return $result->findAll();
}
/**
* Global search across employee + policy fields (OR + LIKE).
* Searches: emp_code, name, mobile, email, tpa_id, dob, relationship, status.

View File

@ -317,6 +317,9 @@ $gst_total = 0;
case 'enrolled':
echo '<span class="badge2 badge2-secondary2">' . ucfirst($employee['status']) . '</span>';
break;
case 'pending_approval':
echo '<span class="badge badge-info">Pending Approval</span>';
break;
default:
echo $employee['status'];
break;

View File

@ -0,0 +1,406 @@
<style>
.table th,
.table td {
padding: 8px;
}
table.dataTable tbody td {
padding: 4px 4px !important;
}
.select2-selection__choice {
background-color: #0a8794 !important;
color: white !important;
font-weight: bold;
}
.select2-selection__choice__remove {
color: white !important;
margin-right: 5px;
}
.custom-dropdown-menu {
display: none;
position: absolute;
background-color: #ffffff !important;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.25rem;
padding: 0.5rem 0;
min-width: 10rem;
z-index: 9999;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.custom-dropdown-menu .dropdown-item {
display: block !important;
width: 100% !important;
padding: 0.5rem 1rem !important;
color: #212529 !important;
text-decoration: none !important;
background-color: transparent !important;
}
.custom-dropdown-menu .dropdown-item:hover {
background-color: #f8f9fa !important;
color: #16181b !important;
cursor: pointer !important;
}
</style>
<div class="row">
<div class="col-xl-12">
<div class="card-body">
<div id="accordion" class="mb-3">
<div class="card mb-1">
<h4>
Filter
<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>
</a>
</h4>
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion">
<div class="card-body">
<div class="form-row">
<div class="form-group col-md-4">
<label>Client</label> <br />
<select class="form-control" id="clients">
<option value="0">Select</option>
</select>
</div>
<div class="form-group col-md-4">
<label>Branch</label> <br />
<select name="branch_id" class="form-control" id="branch_id">
<option value="0">Select</option>
</select>
</div>
<div class="form-group col-md-4">
<label>Policy</label> <br />
<select class="form-control" id="policies">
<option value="0">Select</option>
</select>
</div>
</div>
<div class="row justify-content-end">
<div class="col-auto">
<a href="<?= base_url('employee/pending-approvals'); ?>"
class="btn btn-primary waves-effect waves-light" id="get-emp-list"
onclick="fetchPendingApprovalsList(event);">Submit</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="employee_table_list"><?= $default_table_html ?? '' ?></div>
<div id="loader" class="loader" style="display:none;">SPINNER</div>
<script>
var client_list = '';
var branch_list = '';
var policy_list = '';
var policy_list_by_client = '';
var client_id = '<?= isset($getData) ? $getData['client_id'] : '0' ?>';
var client_branch_id = '<?= isset($getData) ? $getData['branch_id'] : '0' ?>';
function init() {
const table = document.getElementById("tickets-table") || document.getElementById("employee-data-list-table");
if (!table) return;
let activeDropdown = null;
table.querySelectorAll("tbody tr").forEach(row => {
const customDropdown = createCustomDropdown(row);
if (!customDropdown) return;
document.body.appendChild(customDropdown);
row.addEventListener("click", function(event) {
handleRowClick(event, customDropdown);
});
customDropdown.querySelectorAll('.dropdown-item').forEach(item => {
item.addEventListener('click', function(e) {
handleItemClick(e, item);
});
});
});
document.addEventListener("click", handleDocumentClick);
function createCustomDropdown(row) {
const originalDropdown = row.querySelector('.dropdown-menu');
if (!originalDropdown) return null;
const customDropdown = document.createElement('div');
customDropdown.className = 'custom-dropdown-menu';
const originalItems = originalDropdown.querySelectorAll('.dropdown-item');
customDropdown.innerHTML = originalDropdown.innerHTML;
customDropdown.querySelectorAll('.dropdown-item').forEach((item, index) => {
const originalItem = originalItems[index];
const originalOnclick = originalItem.getAttribute('onclick');
if (originalOnclick) {
item.setAttribute('data-onclick', originalOnclick);
item.removeAttribute('onclick');
}
});
return customDropdown;
}
function handleRowClick(event, customDropdown) {
if (event.target.closest('td:last-child')) {
return;
}
if (activeDropdown) {
activeDropdown.style.display = 'none';
}
const rect = event.target.getBoundingClientRect();
customDropdown.style.display = 'block';
customDropdown.style.position = 'fixed';
customDropdown.style.left = `${rect.left}px`;
customDropdown.style.top = `${rect.bottom + 5}px`;
activeDropdown = customDropdown;
event.stopPropagation();
}
function handleItemClick(e, item) {
const onclickAttr = item.getAttribute('data-onclick');
if (onclickAttr) {
eval(onclickAttr);
}
const href = item.getAttribute('href');
if (href && href !== '#' && href !== 'javascript: void(0);') {
window.location.href = href;
}
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
e.stopPropagation();
}
function handleDocumentClick() {
if (activeDropdown) {
activeDropdown.style.display = 'none';
activeDropdown = null;
}
}
}
$(document).ready(function() {
getClientAndBranchAndPolicy();
setTimeout(function() {
init();
}, 500);
});
function getClientAndBranchAndPolicy() {
$.ajax({
url: '<?= base_url("/util/getClientAndBranchAndPolicy") ?>',
type: "GET",
dataType: 'json',
success: function(res) {
if (res.status == true) {
client_list = res.client_data;
branch_list = res.branch_data;
policy_list = res.policy_data;
policy_list_by_client = res.policyListByClient;
appendClients(client_list);
}
},
error: function(xhr, status, error) {
console.error(xhr.responseText);
console.error(status, error);
}
});
}
function appendClients(data) {
var clientID = <?= isset($getData) ? $getData['client_id'] : '0' ?>;
$.each(data, function(index, item) {
var option = $('<option>', {
value: item.id,
text: item.client_name
});
if (clientID == item.id) {
option.attr('selected', true);
}
$('#clients').append(option);
});
}
function appendBranch(data) {
$('#branch_id').empty();
$('#branch_id').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 == item.id) {
option.attr('selected', true);
}
$('#branch_id').append(option);
});
}
function appendPolicies(data) {
$('#policies').empty();
$('#policies').append($('<option>', {
value: '0',
text: 'Select'
}));
var PolicyID = <?= isset($getData) ? $getData['policy_id'] : '0' ?>;
$.each(data, function(index, item) {
var option = $('<option>', {
value: item.id,
text: `${item.policy_type ?? ''} - ${item.policy_no ?? ''}`
});
if (PolicyID == item.id || PolicyID == item.client_policy_id) {
option.attr('selected', true);
}
$('#policies').append(option);
});
}
$(document).ready(function() {
$('#clients').on('change', function() {
$('#branch_id').empty();
$('#branch_id').append($('<option>', { value: '0', text: 'Select'}));
$('#policies').empty();
$('#policies').append($('<option>', { value: '0', text: 'Select' }));
var selectedClient = $(this).val();
var filteredBranch = branch_list[selectedClient];
if (filteredBranch) {
appendBranch(filteredBranch);
}
});
$('#branch_id').on('change', function() {
var selectedBranch = $(this).val();
var selectedClient = $('#clients').val();
var filteredPoliciesByClient = policy_list_by_client[selectedClient] || [];
var filteredPoliciesByBranch = filteredPoliciesByClient.filter(function(item) {
return item.client_branch_id === selectedBranch;
});
appendPolicies(filteredPoliciesByBranch);
if (Array.isArray(filteredPoliciesByBranch) && filteredPoliciesByBranch.length === 0) {
toastr.warning('No policies found for the selected client branch.');
}
});
});
function objectToQueryString(obj) {
return Object.keys(obj).map(key => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`).join('&');
}
function fetchPendingApprovalsList(event = null) {
if (event) {
event.preventDefault();
}
var client_id = $('#clients').val();
var policy_id = $('#policies').val();
var branch_id = $('#branch_id').val();
var queryParams = {
client_id: client_id,
policy_id: policy_id,
branch_id: branch_id,
};
const queryString = objectToQueryString(queryParams);
const apiURL = $('#get-emp-list').attr('href') + "?" + queryString;
$('.loader').fadeIn();
$('.loader-mask').fadeIn();
$.ajax({
url: apiURL,
method: 'GET',
data: queryParams,
success: function(response) {
if (response.status == true) {
$('#employee_table_list').html(response.html);
$('.loader').fadeOut();
$('.loader-mask').delay(350).fadeOut('slow');
setTimeout(function() {
init();
}, 1000);
}
},
error: function(xhr, status, error) {
console.error('Error:', error);
toastr.error('Failed to fetch Data', 'Error');
$('.loader').fadeOut();
$('.loader-mask').delay(350).fadeOut('slow');
}
});
}
document.getElementById('toggleIcon').addEventListener('click', function() {
var icon = document.getElementById('icon');
icon.classList.toggle('mdi-chevron-down');
icon.classList.toggle('mdi-chevron-up');
});
function processPendingDependent(employeeId, clientPolicyId, status) {
var actionLabel = status === 'approved' ? 'approve' : 'reject';
Swal.fire({
title: 'Are you sure?',
text: 'Do you want to ' + actionLabel + ' this dependent?',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes',
cancelButtonText: 'No'
}).then((result) => {
if (!result.isConfirmed) {
return;
}
$('.loader').fadeIn();
$('.loader-mask').fadeIn();
$.ajax({
url: '<?= base_url('processDependentAdd') ?>',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({
employee_id: employeeId,
client_policy_id: clientPolicyId,
status: status
}),
success: function(response) {
$('.loader').fadeOut();
$('.loader-mask').delay(350).fadeOut('slow');
if (response.status === 'success' || response.code == 200) {
toastr.success(response.message || ('Dependent ' + actionLabel + 'd successfully'));
fetchPendingApprovalsList();
} else {
toastr.error(response.data || response.message || 'Action failed');
}
},
error: function() {
$('.loader').fadeOut();
$('.loader-mask').delay(350).fadeOut('slow');
toastr.error('Action failed', 'Error');
}
});
});
}
</script>

View File

@ -2005,6 +2005,9 @@ body[data-sidebar-size="condensed"] .footer {
<li>
<a href="<?= base_url('/employee/list') ?>">View Members</a>
</li>
<li>
<a href="<?= base_url('/employee/pending-approvals') ?>">View Pending Approvals</a>
</li>
<li>
<a href="<?= base_url('/employee/endorsement-list') ?>">View Endorsement</a>
</li>

View File

@ -229,6 +229,11 @@
style="width: 35%;;" type="number" name="self_max_age" id="self_max_age"
onchange="lowerIsEighteen(this)">
</div>
<div class="self-age d-flex align-items-center ml-3">
<span style="margin-right:8px;color:black;font-size:12px;white-space:nowrap;">Is Payable by employee:</span>
<input type="checkbox" name="is_payable_employee_for_self" id="is_payable_employee_for_self" value="1">
</div>
</div>
<div class="d-flex align-items-center policy-base-info">
@ -253,6 +258,11 @@
style="width: 35%;;" type="number" name="spouse_max_age" id="spouse_max_age"
onchange="lowerIsEighteen(this)">
</div>
<div class="spouse-age d-flex align-items-center ml-3">
<span style="margin-right:8px;color:black;font-size:12px;white-space:nowrap;">Is Payable by employee:</span>
<input type="checkbox" name="is_payable_employee_for_spouse" id="is_payable_employee_for_spouse" value="1">
</div>
</div>
<div class="d-flex align-items-center policy-base-info">
@ -280,6 +290,11 @@
style="width: 35%;;" type="number" name="child_max_age" id="child_max_age"
oninput="this.value = this.value.replace(/\D/g, '').substring(0, 2)">
</div>
<div class="child-age d-flex align-items-center ml-3">
<span style="margin-right:8px;color:black;font-size:12px;white-space:nowrap;">Is Payable by employee:</span>
<input type="checkbox" name="is_payable_employee_for_child" id="is_payable_employee_for_child" value="1">
</div>
</div>
<div class="d-flex align-items-center policy-base-info flex-nowrap">
@ -326,6 +341,11 @@
onchange="lowerIsEighteen(this)">
</div>
<div class="other-member-age d-flex align-items-center ml-3">
<span style="margin-right:8px;color:black;font-size:12px;white-space:nowrap;">Is Payable by employee:</span>
<input type="checkbox" name="is_payable_employee_for_elders" id="is_payable_employee_for_elders" value="1">
</div>
</div>
</div>
@ -1406,46 +1426,14 @@
$('#other_member_max_age').val(jsonObject[key].elders.max);
}
// if (key.includes("is_payable_employee")) {
// if (jsonObject[key]) {
// if (jsonObject[key].self == 0) {
// $('#is_payable_employee_for_self').prop('checked',
// false);
// } else {
// $('#is_payable_employee_for_self').prop('checked',
// true);
// }
// if (jsonObject[key].spouse == 0) {
// $('#is_payable_employee_for_spouse').prop('checked',
// false);
// } else {
// $('#is_payable_employee_for_spouse').prop('checked',
// true);
// }
// if (jsonObject[key].childern == 0) {
// $('#is_payable_employee_for_child').prop('checked',
// false);
// } else {
// $('#is_payable_employee_for_child').prop('checked',
// true);
// }
// // if (jsonObject[key].elders == 0) {
// // $('#is_payable_employee_for_elders').prop('checked',
// // false);
// // } else {
// // $('#is_payable_employee_for_elders').prop('checked',
// // true);
// // }
// }
// }
if (key.includes("is_payable_employee")) {
if (jsonObject[key]) {
$('#is_payable_employee_for_self').prop('checked', jsonObject[key].self == 1);
$('#is_payable_employee_for_spouse').prop('checked', jsonObject[key].spouse == 1);
$('#is_payable_employee_for_child').prop('checked', jsonObject[key].childern == 1);
$('#is_payable_employee_for_elders').prop('checked', jsonObject[key].elders == 1);
}
}
if (key.includes("enrollment_display_key") && key != "") {

View File

@ -0,0 +1,283 @@
# Dependent Approval APIs
Base path: `/employeeRest/`
Auth: JWT + App-Signature (`authJWT`, `appSignature`)
---
## 1. Get Employee Policy (Dependent Add UI)
| | |
|---|---|
| **Method** | `GET` |
| **URL** | `/employeeRest/getEmployeePolicy` |
| **Handler** | `EmployeeRestController::getEmployeeByPolicyv2` |
> **Note:** Route currently maps to `getEmployeePolicyv2` (method missing). Intended handler is `getEmployeeByPolicyv2`.
### Query Params
| Param | Required | Description |
|---|---|---|
| `id` | Yes | Self employee id |
| `emp_code` | Yes | Employee code |
| `client_id` | Yes | Client id |
| `client_branch_id` | Yes | Branch id |
| `client_policy_id` | Yes | GMC client policy id |
### Success Response
```json
{
"status": "success",
"code": 200,
"message": "Employee policy and dependent details fetched successfully",
"data": {
"...": "policy details, mapped_family_floaters, allowed_relationships"
},
"EmployeePolicy": [],
"add_button_show": true
}
```
### Error Responses
| code | message |
|---|---|
| 400 | Required parameters missing |
| 400 | Only GMC policy is supported for dependent add |
| 404 | Employee policy not found |
| 500 | Something went wrong while fetching employee policy details |
---
## 2. Add / Update Dependent (Pending Approval)
| | |
|---|---|
| **Method** | `POST` |
| **URL** | `/employeeRest/addEmployeeAndDependence` |
| **Handler** | `EmployeeRestController::addEmployeeAndDependencev2` |
| **Content-Type** | `application/json` |
### Request Body
JSON **array** of dependent objects:
```json
[
{
"emp_code": "E001",
"client_id": 10,
"client_branch_id": 5,
"client_policy_id": 100,
"name": "John Doe",
"relationship": "Spouse",
"dob": "01/01/1990",
"basic_cover_si": 500000,
"dependent_effective_date": "01/08/2026",
"hr_id": 12
}
]
```
### Fields
| Field | Required | Notes |
|---|---|---|
| `emp_code` | Yes | Parent employee code |
| `client_id` | Yes | Client id |
| `client_branch_id` | Yes | Used for premium update |
| `client_policy_id` | Yes | Client policy id |
| `name` | Yes | Dependent name |
| `relationship` | Yes | e.g. Spouse / Son / Daughter |
| `dob` | Yes | Converted to `Y-m-d` |
| `basic_cover_si` | Yes | Sum insured |
| `dependent_effective_date` | Yes (new) | Coverage start date |
| `hr_id` | No | If set → created by HR; else USER |
| `id` | No | If set → update existing dependent |
### Behavior
- Creates/updates dependent with `emp_status = pending_approval`
- Creates employee policy with `status = pending_approval`
- Recalculates premium for the family
### Success Response
```json
{
"status": "success",
"code": 200,
"data": []
}
```
### Error Responses
| code | data |
|---|---|
| 404 | Requested parameters are required |
| 404 | No Matches |
| 500 | Exception message |
---
## 3. Delete Dependent
| | |
|---|---|
| **Method** | `GET` |
| **URL** | `/employeeRest/deleteDependence` |
| **Handler** | `EmployeeRestController::deleteDependencev2` |
### Query Params
| Param | Required | Description |
|---|---|---|
| `id` | Yes | Dependent employee id |
### Behavior
Soft-deletes employee and policy:
- `emp_status` / `status``truncated`
- `is_active``0`
### Success Response
```json
{
"status": "success",
"code": 200,
"data": []
}
```
### Error Responses
| code | data |
|---|---|
| 404 | `[]` (missing `id`) |
| 500 | Exception message |
---
## 4. List Pending Approval Dependents
| | |
|---|---|
| **Method** | `GET` |
| **URL** | `/employeeRest/getPendingApprovalDependents` |
| **Handler** | `EmployeeRestController::getPendingApprovalDependents` |
### Query Params
| Param | Required | Description |
|---|---|---|
| `client_id` | No | Client filter |
| `client_branch_id` / `branch_id` | No | Branch filter |
| `client_policy_id` / `policy_id` | No | Policy filter |
| `search` | No | Name / emp_code search |
### Success Response
```json
{
"status": "success",
"code": 200,
"message": "Pending approval dependents fetched successfully",
"data": []
}
```
### Error Responses
| code | message |
|---|---|
| 404 | No pending approval dependents found |
| 500 | Exception message |
---
## 5. Approve / Reject Dependent
| | |
|---|---|
| **Method** | `POST` |
| **URL** | `/employeeRest/processDependentAdd` |
| **Handler** | `EmployeeRestController::processDependentAdd` |
| **Content-Type** | `application/json` |
### Request Body
```json
{
"employee_id": 123,
"client_policy_id": 100,
"status": "approved",
"hr_id": 12
}
```
### Fields
| Field | Required | Notes |
|---|---|---|
| `employee_id` | Yes | Dependent employee id |
| `client_policy_id` | Yes | Policy id |
| `status` | Yes | `approved` or `rejected` (default: `approved`) |
| `hr_id` | No | If set → approved by HR; else ACM |
### Behavior
| status | Employee | Policy |
|---|---|---|
| `approved` | `emp_status = active` | `status = active` |
| `rejected` | `emp_status = rejected`, `is_active = 0` | `status = rejected`, `is_active = 0` |
### Success Response
```json
{
"status": "success",
"code": 200,
"data": []
}
```
### Error Responses
| code | data |
|---|---|
| 400 | Request body is required |
| 400 | employee_id is required |
| 400 | client_policy_id is required |
| 400 | status is invalid |
| 400 | Dependent is already approved |
| 400 | Dependent is not pending approval |
| 404 | Employee not found or inactive |
| 404 | Employee policy not found or inactive |
| 500 | Failed to update employee/policy status |
---
## Typical Flow
```
1. getEmployeePolicy
→ show family slots / add buttons
2. addEmployeeAndDependence
→ submit dependent → pending_approval
3. getPendingApprovalDependents
→ HR / ACM review list
4. processDependentAdd
→ approve or reject
5. deleteDependence
→ soft-delete if needed
```