diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 255275dc..406a099e 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -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) { diff --git a/app/Controllers/ClaimReportDashboardController.php b/app/Controllers/ClaimReportDashboardController.php index acbe8e8d..c57b97df 100644 --- a/app/Controllers/ClaimReportDashboardController.php +++ b/app/Controllers/ClaimReportDashboardController.php @@ -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 + */ + 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)); + } + } diff --git a/app/Controllers/EmployeeController.php b/app/Controllers/EmployeeController.php index 0e6e5f93..b60561a2 100755 --- a/app/Controllers/EmployeeController.php +++ b/app/Controllers/EmployeeController.php @@ -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 { diff --git a/app/Controllers/EmployeeRestController.php b/app/Controllers/EmployeeRestController.php index 7d30450c..6586990e 100755 --- a/app/Controllers/EmployeeRestController.php +++ b/app/Controllers/EmployeeRestController.php @@ -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); + } + } + } diff --git a/app/Database/Migrations/2026-08-01-043000_AddDependentApprovalTrackingColumns.php b/app/Database/Migrations/2026-08-01-043000_AddDependentApprovalTrackingColumns.php new file mode 100644 index 00000000..be63ed22 --- /dev/null +++ b/app/Database/Migrations/2026-08-01-043000_AddDependentApprovalTrackingColumns.php @@ -0,0 +1,81 @@ +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'); + } + } +} diff --git a/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php b/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php index 533394b1..adda4795 100644 --- a/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php +++ b/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php @@ -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; } /** diff --git a/app/Models/ClaimReportModel.php b/app/Models/ClaimReportModel.php index df809eb1..86e0b74c 100644 --- a/app/Models/ClaimReportModel.php +++ b/app/Models/ClaimReportModel.php @@ -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 $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 $ticket + * @return array + */ + 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; + } } diff --git a/app/Models/EmployeeModel.php b/app/Models/EmployeeModel.php index 9f2663d7..bf869d82 100755 --- a/app/Models/EmployeeModel.php +++ b/app/Models/EmployeeModel.php @@ -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(); } diff --git a/app/Models/EmployeePolicyModel.php b/app/Models/EmployeePolicyModel.php index 51392f54..1ffaf8d0 100755 --- a/app/Models/EmployeePolicyModel.php +++ b/app/Models/EmployeePolicyModel.php @@ -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. diff --git a/app/Views/employee_data_list.php b/app/Views/employee_data_list.php index 2e15874e..e5947d4a 100755 --- a/app/Views/employee_data_list.php +++ b/app/Views/employee_data_list.php @@ -317,6 +317,9 @@ $gst_total = 0; case 'enrolled': echo '' . ucfirst($employee['status']) . ''; break; + case 'pending_approval': + echo 'Pending Approval'; + break; default: echo $employee['status']; break; diff --git a/app/Views/employee_pending_approvals_list.php b/app/Views/employee_pending_approvals_list.php new file mode 100644 index 00000000..7aabf9cf --- /dev/null +++ b/app/Views/employee_pending_approvals_list.php @@ -0,0 +1,406 @@ + + +
+
+
+
+
+

+ Filter + + + +

+
+
+
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+
+
+
+ Submit +
+
+
+
+
+
+
+
+
+ +
+ + + diff --git a/app/Views/layout/header.php b/app/Views/layout/header.php index ebc63494..0d32c339 100755 --- a/app/Views/layout/header.php +++ b/app/Views/layout/header.php @@ -2005,6 +2005,9 @@ body[data-sidebar-size="condensed"] .footer {
  • View Members
  • +
  • + View Pending Approvals +
  • View Endorsement
  • diff --git a/app/Views/policy_gmc_terms.php b/app/Views/policy_gmc_terms.php index 7fbabc3e..a9b65060 100755 --- a/app/Views/policy_gmc_terms.php +++ b/app/Views/policy_gmc_terms.php @@ -229,6 +229,11 @@ style="width: 35%;;" type="number" name="self_max_age" id="self_max_age" onchange="lowerIsEighteen(this)"> + +
    + Is Payable by employee: + +
    @@ -253,6 +258,11 @@ style="width: 35%;;" type="number" name="spouse_max_age" id="spouse_max_age" onchange="lowerIsEighteen(this)">
    + +
    + Is Payable by employee: + +
    @@ -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)">
    + +
    + Is Payable by employee: + +
    @@ -326,6 +341,11 @@ onchange="lowerIsEighteen(this)">
    +
    + Is Payable by employee: + +
    + @@ -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 != "") { diff --git a/docs/dependent-approval-apis.md b/docs/dependent-approval-apis.md new file mode 100644 index 00000000..1756789a --- /dev/null +++ b/docs/dependent-approval-apis.md @@ -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 +```