From 4d8edca3b1321e58b4ad549bc526af69a815634f Mon Sep 17 00:00:00 2001 From: Venkatesh Date: Tue, 4 Aug 2026 12:42:16 +0530 Subject: [PATCH] CHANGE_DEPENDENT_ADD_CHANGE --- app/Controllers/EmployeeController.php | 6 +- app/Controllers/EmployeeRestController.php | 46 +++--- ...00_AddDependentApprovalTrackingColumns.php | 22 +-- ...ectReasonToEmployeesAndEmployeePolices.php | 45 ++++++ ...4-065000_RenameApprovedByToProcessedBy.php | 66 ++++++++ app/Models/EmployeeModel.php | 3 +- app/Models/EmployeePolicyModel.php | 36 ++++- .../employee_pending_approvals_data_list.php | 141 ++++++++++++++++++ app/Views/employee_pending_approvals_list.php | 90 +++++++---- 9 files changed, 389 insertions(+), 66 deletions(-) create mode 100644 app/Database/Migrations/2026-08-04-064500_AddRejectReasonToEmployeesAndEmployeePolices.php create mode 100644 app/Database/Migrations/2026-08-04-065000_RenameApprovedByToProcessedBy.php create mode 100644 app/Views/employee_pending_approvals_data_list.php diff --git a/app/Controllers/EmployeeController.php b/app/Controllers/EmployeeController.php index b60561a2..34480720 100755 --- a/app/Controllers/EmployeeController.php +++ b/app/Controllers/EmployeeController.php @@ -180,19 +180,19 @@ class EmployeeController extends AdminController 'branch_id' => $filterData['branch_id'] ?? '0', 'emp_code' => '', 'emp_name' => '', - 'status' => ['pending_approval'], + 'status' => ['pending_approval', 'active', 'rejected'], ]; // AJAX filter submit → return table HTML only if (count($this->request->getGet())) { - $html = view('employee_data_list', $data); + $html = view('employee_pending_approvals_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); + $data['default_table_html'] = view('employee_pending_approvals_data_list', $data); $this->loadLayout('employee_pending_approvals_list', $data); } diff --git a/app/Controllers/EmployeeRestController.php b/app/Controllers/EmployeeRestController.php index 6586990e..874be0c1 100755 --- a/app/Controllers/EmployeeRestController.php +++ b/app/Controllers/EmployeeRestController.php @@ -6728,9 +6728,10 @@ class EmployeeRestController extends AdminController $client_policy_id = $data['client_policy_id'] ?? null; $hr_id = $data['hr_id'] ?? null; $status = $data['status'] ?? "approved"; + $reject_reason = trim((string) ($data['reject_reason'] ?? '')); $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'; + // Processor role: HR (app) or ACM (internal portal session user) — used for approve and reject + $processed_by_role = ! empty($hr_id) ? 'HR' : 'ACM'; if (empty($employee_id)) { return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'employee_id is required'], 200); @@ -6748,6 +6749,10 @@ class EmployeeRestController extends AdminController return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'status is invalid'], 200); } + if ($status === 'rejected' && $reject_reason === '') { + return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'reject_reason is required'], 200); + } + $employee = $this->employeeModel ->where('id', $employee_id) ->where('is_active', 1) @@ -6777,27 +6782,29 @@ class EmployeeRestController extends AdminController if ($status === 'approved') { $employee_update_data = [ - 'updated_by' => $updated_by, - 'approved_by' => $approved_by_role, - 'emp_status' => 'active', + 'updated_by' => $updated_by, + 'processed_by' => $processed_by_role, + 'emp_status' => 'active', ]; $policy_update_data = [ - 'updated_by' => $updated_by, - 'approved_by' => $approved_by_role, - 'status' => 'active', + 'updated_by' => $updated_by, + 'processed_by' => $processed_by_role, + 'status' => 'active', ]; } else { $employee_update_data = [ - 'updated_by' => $updated_by, - 'approved_by' => $approved_by_role, - 'emp_status' => 'rejected', - 'is_active' => 0, + 'updated_by' => $updated_by, + 'processed_by' => $processed_by_role, + 'reject_reason' => $reject_reason, + 'emp_status' => 'rejected', + 'is_active' => 0, ]; $policy_update_data = [ - 'updated_by' => $updated_by, - 'approved_by' => $approved_by_role, - 'status' => 'rejected', - 'is_active' => 0, + 'updated_by' => $updated_by, + 'processed_by' => $processed_by_role, + 'reject_reason' => $reject_reason, + 'status' => 'rejected', + 'is_active' => 0, ]; } @@ -6830,7 +6837,8 @@ class EmployeeRestController extends AdminController } /** - * List pending_approval dependents (API + mobile). + * List dependent-add workflow members (API + mobile). + * Returns only pending_approval, approved, and rejected dependents (not all members). * Filters: client_id, client_branch_id / branch_id, client_policy_id / policy_id, optional search. */ public function getPendingApprovalDependents() @@ -6852,7 +6860,7 @@ class EmployeeRestController extends AdminController return $this->respond([ 'status' => 'success', 'code' => 200, - 'message' => 'Pending approval dependents fetched successfully', + 'message' => 'Dependent approval list fetched successfully', 'data' => $empData, ], 200); } @@ -6860,7 +6868,7 @@ class EmployeeRestController extends AdminController return $this->respond([ 'status' => 'failed', 'code' => 404, - 'message' => 'No pending approval dependents found', + 'message' => 'No dependent approval records found', 'data' => [], ], 200); } catch (\Exception $e) { diff --git a/app/Database/Migrations/2026-08-01-043000_AddDependentApprovalTrackingColumns.php b/app/Database/Migrations/2026-08-01-043000_AddDependentApprovalTrackingColumns.php index be63ed22..115c7419 100644 --- a/app/Database/Migrations/2026-08-01-043000_AddDependentApprovalTrackingColumns.php +++ b/app/Database/Migrations/2026-08-01-043000_AddDependentApprovalTrackingColumns.php @@ -22,20 +22,20 @@ class AddDependentApprovalTrackingColumns extends Migration ]); } - if (! $this->db->fieldExists('approved_by', 'employees')) { + if (! $this->db->fieldExists('processed_by', 'employees')) { $this->forge->addColumn('employees', [ - 'approved_by' => [ + 'processed_by' => [ 'type' => 'VARCHAR', 'constraint' => 50, 'null' => true, 'default' => null, 'after' => 'emp_created_by', - 'comment' => 'Approver role: HR / ACM', + 'comment' => 'Processor role: HR / ACM (approve or reject)', ], ]); } - // employee_polices: who created the policy row, who approved (HR / ACM) + // employee_polices: who created the policy row, who processed (HR / ACM) if (! $this->db->fieldExists('emp_policy_created_by', 'employee_polices')) { $this->forge->addColumn('employee_polices', [ 'emp_policy_created_by' => [ @@ -49,15 +49,15 @@ class AddDependentApprovalTrackingColumns extends Migration ]); } - if (! $this->db->fieldExists('approved_by', 'employee_polices')) { + if (! $this->db->fieldExists('processed_by', 'employee_polices')) { $this->forge->addColumn('employee_polices', [ - 'approved_by' => [ + 'processed_by' => [ 'type' => 'VARCHAR', 'constraint' => 50, 'null' => true, 'default' => null, 'after' => 'emp_policy_created_by', - 'comment' => 'Approver role: HR / ACM', + 'comment' => 'Processor role: HR / ACM (approve or reject)', ], ]); } @@ -65,14 +65,14 @@ class AddDependentApprovalTrackingColumns extends Migration public function down() { - if ($this->db->fieldExists('approved_by', 'employees')) { - $this->forge->dropColumn('employees', 'approved_by'); + if ($this->db->fieldExists('processed_by', 'employees')) { + $this->forge->dropColumn('employees', 'processed_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('processed_by', 'employee_polices')) { + $this->forge->dropColumn('employee_polices', 'processed_by'); } if ($this->db->fieldExists('emp_policy_created_by', 'employee_polices')) { $this->forge->dropColumn('employee_polices', 'emp_policy_created_by'); diff --git a/app/Database/Migrations/2026-08-04-064500_AddRejectReasonToEmployeesAndEmployeePolices.php b/app/Database/Migrations/2026-08-04-064500_AddRejectReasonToEmployeesAndEmployeePolices.php new file mode 100644 index 00000000..80c495c3 --- /dev/null +++ b/app/Database/Migrations/2026-08-04-064500_AddRejectReasonToEmployeesAndEmployeePolices.php @@ -0,0 +1,45 @@ +db->fieldExists('reject_reason', 'employees')) { + $this->forge->addColumn('employees', [ + 'reject_reason' => [ + 'type' => 'TEXT', + 'null' => true, + 'default' => null, + 'after' => 'processed_by', + 'comment' => 'Reason when dependent addition is rejected', + ], + ]); + } + + if (! $this->db->fieldExists('reject_reason', 'employee_polices')) { + $this->forge->addColumn('employee_polices', [ + 'reject_reason' => [ + 'type' => 'TEXT', + 'null' => true, + 'default' => null, + 'after' => 'processed_by', + 'comment' => 'Reason when dependent addition is rejected', + ], + ]); + } + } + + public function down() + { + if ($this->db->fieldExists('reject_reason', 'employees')) { + $this->forge->dropColumn('employees', 'reject_reason'); + } + if ($this->db->fieldExists('reject_reason', 'employee_polices')) { + $this->forge->dropColumn('employee_polices', 'reject_reason'); + } + } +} diff --git a/app/Database/Migrations/2026-08-04-065000_RenameApprovedByToProcessedBy.php b/app/Database/Migrations/2026-08-04-065000_RenameApprovedByToProcessedBy.php new file mode 100644 index 00000000..e0b127bf --- /dev/null +++ b/app/Database/Migrations/2026-08-04-065000_RenameApprovedByToProcessedBy.php @@ -0,0 +1,66 @@ +db->fieldExists('approved_by', 'employees') && ! $this->db->fieldExists('processed_by', 'employees')) { + $this->forge->modifyColumn('employees', [ + 'approved_by' => [ + 'name' => 'processed_by', + 'type' => 'VARCHAR', + 'constraint' => 50, + 'null' => true, + 'default' => null, + 'comment' => 'Processor role: HR / ACM (approve or reject)', + ], + ]); + } + + if ($this->db->fieldExists('approved_by', 'employee_polices') && ! $this->db->fieldExists('processed_by', 'employee_polices')) { + $this->forge->modifyColumn('employee_polices', [ + 'approved_by' => [ + 'name' => 'processed_by', + 'type' => 'VARCHAR', + 'constraint' => 50, + 'null' => true, + 'default' => null, + 'comment' => 'Processor role: HR / ACM (approve or reject)', + ], + ]); + } + } + + public function down() + { + if ($this->db->fieldExists('processed_by', 'employees') && ! $this->db->fieldExists('approved_by', 'employees')) { + $this->forge->modifyColumn('employees', [ + 'processed_by' => [ + 'name' => 'approved_by', + 'type' => 'VARCHAR', + 'constraint' => 50, + 'null' => true, + 'default' => null, + 'comment' => 'Approver role: HR / ACM', + ], + ]); + } + + if ($this->db->fieldExists('processed_by', 'employee_polices') && ! $this->db->fieldExists('approved_by', 'employee_polices')) { + $this->forge->modifyColumn('employee_polices', [ + 'processed_by' => [ + 'name' => 'approved_by', + 'type' => 'VARCHAR', + 'constraint' => 50, + 'null' => true, + 'default' => null, + 'comment' => 'Approver role: HR / ACM', + ], + ]); + } + } +} diff --git a/app/Models/EmployeeModel.php b/app/Models/EmployeeModel.php index bf869d82..a7146d73 100755 --- a/app/Models/EmployeeModel.php +++ b/app/Models/EmployeeModel.php @@ -32,7 +32,8 @@ class EmployeeModel extends Model "family_floater_key", "emp_status", "emp_created_by", - "approved_by", + "processed_by", + "reject_reason", "created_by", "updated_by", "updated_at", diff --git a/app/Models/EmployeePolicyModel.php b/app/Models/EmployeePolicyModel.php index 6e3d70ee..c500495f 100755 --- a/app/Models/EmployeePolicyModel.php +++ b/app/Models/EmployeePolicyModel.php @@ -17,7 +17,8 @@ class EmployeePolicyModel extends Model "batch_id", "status", "emp_policy_created_by", - "approved_by", + "processed_by", + "reject_reason", "pre_existing_alignments", "date_of_exit", "reason_for_exit", @@ -378,7 +379,8 @@ class EmployeePolicyModel extends Model } /** - * Pending-approval dependents (excludes Self) for a client / branch / policy. + * Dependent-add workflow list (excludes Self). + * Includes only: pending_approval, approved (active + processed_by), rejected. */ public function getPendingApprovalDependents($client_id = 0, $policy_id = 0, $branch_id = 0, $search = '') { @@ -417,6 +419,8 @@ class EmployeePolicyModel extends Model 'emp.gender', 'emp.emp_status', 'emp.is_active as emp_is_active', + 'emp.reject_reason as emp_reject_reason', + 'emp.processed_by as emp_processed_by', 'emp.mobile as mobile', 'emp.doj', 'emp.basic_pay', @@ -455,11 +459,31 @@ class EmployeePolicyModel extends Model ->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) + ->groupStart() + // Pending approval + ->groupStart() + ->where('emp.emp_status', 'pending_approval') + ->where('employee_polices.status', 'pending_approval') + ->where('emp.is_active', 1) + ->where('employee_polices.is_active', 1) + ->groupEnd() + // Approved via dependent-add process (processed_by set; excludes normal inception actives) + ->orGroupStart() + ->where('emp.emp_status', 'active') + ->where('employee_polices.status', 'active') + ->where('emp.is_active', 1) + ->where('employee_polices.is_active', 1) + ->where('employee_polices.processed_by IS NOT NULL', null, false) + ->where("TRIM(employee_polices.processed_by) != ''", null, false) + ->groupEnd() + // Rejected (soft-deleted) + ->orGroupStart() + ->where('emp.emp_status', 'rejected') + ->where('employee_polices.status', 'rejected') + ->groupEnd() + ->groupEnd() + ->orderBy('employee_polices.updated_at', 'DESC') ->orderBy('emp.emp_code', 'ASC') ->orderBy('employee_polices.employee_id', 'ASC'); diff --git a/app/Views/employee_pending_approvals_data_list.php b/app/Views/employee_pending_approvals_data_list.php new file mode 100644 index 00000000..0ea20681 --- /dev/null +++ b/app/Views/employee_pending_approvals_data_list.php @@ -0,0 +1,141 @@ + + + +
+
+
+
+
+
+

Pending Approvals

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $employee) { + $pro_rata_total += (float) ($employee['rata_premimum'] ?? 0); + $gst_total += (float) ($employee['gst'] ?? 0); + ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
S.NoBranchNameEMP CodeRelationshipGenderEmailMobileDate of BirthPolicy nameInsurer nameTPA IDRisk IDPolicy statusProcessed ByReject Reason(₹)Sum Insured(₹)Premium(₹)Pro Rata Premium(₹)GSTAction
+ Pending Approval'; + } elseif ($rowStatus === 'active') { + echo 'Approved'; + } elseif ($rowStatus === 'rejected') { + echo 'Rejected'; + } else { + echo esc($employee['status'] ?? ''); + } + ?> + + + + + - + +
Total
+
+
+
+
+
+ + diff --git a/app/Views/employee_pending_approvals_list.php b/app/Views/employee_pending_approvals_list.php index 7aabf9cf..d89014ba 100644 --- a/app/Views/employee_pending_approvals_list.php +++ b/app/Views/employee_pending_approvals_list.php @@ -360,6 +360,34 @@ function processPendingDependent(employeeId, clientPolicyId, status) { var actionLabel = status === 'approved' ? 'approve' : 'reject'; + if (status === 'rejected') { + Swal.fire({ + title: 'Reject Dependent', + html: '

Please enter the reject reason.

', + input: 'textarea', + inputPlaceholder: 'Reject reason...', + inputAttributes: { + 'aria-label': 'Reject reason', + 'maxlength': 1000 + }, + icon: 'warning', + showCancelButton: true, + confirmButtonText: 'Reject', + cancelButtonText: 'Cancel', + inputValidator: (value) => { + if (!value || !String(value).trim()) { + return 'Reject reason is required'; + } + } + }).then((result) => { + if (!result.isConfirmed) { + return; + } + submitPendingDependent(employeeId, clientPolicyId, status, String(result.value).trim()); + }); + return; + } + Swal.fire({ title: 'Are you sure?', text: 'Do you want to ' + actionLabel + ' this dependent?', @@ -371,36 +399,46 @@ if (!result.isConfirmed) { return; } + submitPendingDependent(employeeId, clientPolicyId, status, ''); + }); + } - $('.loader').fadeIn(); - $('.loader-mask').fadeIn(); + function submitPendingDependent(employeeId, clientPolicyId, status, rejectReason) { + var actionLabel = status === 'approved' ? 'approve' : 'reject'; + var payload = { + employee_id: employeeId, + client_policy_id: clientPolicyId, + status: status + }; - $.ajax({ - url: '', - 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 (status === 'rejected') { + payload.reject_reason = rejectReason; + } - 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'); + $('.loader').fadeIn(); + $('.loader-mask').fadeIn(); + + $.ajax({ + url: '', + method: 'POST', + contentType: 'application/json', + data: JSON.stringify(payload), + 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'); + } }); }