MERGE_TEST_BUG_FIXES

This commit is contained in:
Ubuntu 2026-08-04 12:42:54 +05:30
commit 31869e3d27
9 changed files with 389 additions and 66 deletions

View File

@ -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);
}

View File

@ -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) {

View File

@ -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');

View File

@ -0,0 +1,45 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddRejectReasonToEmployeesAndEmployeePolices extends Migration
{
public function up()
{
if (! $this->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');
}
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class RenameApprovedByToProcessedBy extends Migration
{
public function up()
{
if ($this->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',
],
]);
}
}
}

View File

@ -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",

View File

@ -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');

View File

@ -0,0 +1,141 @@
<?php
$getData = $getData ?? [];
$pro_rata_total = 0;
$gst_total = 0;
?>
<style>
.badge2 { display: inline-block; padding: .25em .4em; font-size: 75%; font-weight: 700; line-height: 1; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25rem; }
.badge2-secondary2 { color: #fff; background-color: #187607; }
.table-responsive { overflow-x: auto; }
</style>
<div class="row" id="client_list">
<div class="col-12">
<div class="card">
<div class="card-body">
<div class="row" style="padding-bottom: 10px;">
<div class="col-6" style="align-self: center;">
<h4 style="position: relative;">Pending Approvals</h4>
</div>
</div>
<div class="table-responsive">
<table class="table table-hover m-0 table-centered nowrap w-100" cellspacing="0" id="employee-data-list-table">
<thead class="bg-light">
<tr>
<th>S.No</th>
<?php if (($getData['client_id'] ?? '0') != '0' && ($getData['branch_id'] ?? '0') == '0') { ?>
<th>Branch</th>
<?php } ?>
<th>Name</th>
<th>EMP Code</th>
<th>Relationship</th>
<th>Gender</th>
<th>Email</th>
<th>Mobile</th>
<th>Date of Birth</th>
<th>Policy name</th>
<th>Insurer name</th>
<th>TPA ID</th>
<th>Risk ID</th>
<th>Policy status</th>
<th>Processed By</th>
<th>Reject Reason</th>
<th>()Sum Insured</th>
<th>()Premium</th>
<th>()Pro Rata Premium</th>
<th>()GST</th>
<th>Action</th>
</tr>
</thead>
<tbody class="font-12">
<?php if (! empty($employees)) {
foreach ($employees as $key => $employee) {
$pro_rata_total += (float) ($employee['rata_premimum'] ?? 0);
$gst_total += (float) ($employee['gst'] ?? 0);
?>
<tr>
<td><b><?= $key + 1 ?></b></td>
<?php if (($getData['client_id'] ?? '0') != '0' && ($getData['branch_id'] ?? '0') == '0') { ?>
<td><?= esc($employee['client_branch_name'] ?? '') ?></td>
<?php } ?>
<td><?= esc($employee['name'] ?? '') ?></td>
<td><?= esc($employee['emp_code'] ?? '') ?></td>
<td><?= esc($employee['relationship'] ?? '') ?></td>
<td><?= esc($employee['gender'] ?? '') ?></td>
<td><?= esc($employee['email_corporate'] ?? '') ?></td>
<td><?= esc($employee['mobile'] ?? '') ?></td>
<td><?= ! empty($employee['dob']) ? date('d/m/Y', strtotime($employee['dob'])) : '-' ?></td>
<td><?= esc(($employee['policy_type'] ?? '') . ' - ' . ($employee['policy_no'] ?? '')) ?></td>
<td><?= esc($employee['insurer_short_name'] ?? '') ?></td>
<td><?= esc($employee['tpa_id'] ?? '') ?></td>
<td><?= esc($employee['uhid'] ?? '') ?></td>
<td>
<?php
$rowStatus = strtolower((string) ($employee['status'] ?? ''));
if ($rowStatus === 'pending_approval') {
echo '<span class="badge badge-info">Pending Approval</span>';
} elseif ($rowStatus === 'active') {
echo '<span class="badge badge-success">Approved</span>';
} elseif ($rowStatus === 'rejected') {
echo '<span class="badge badge-danger">Rejected</span>';
} else {
echo esc($employee['status'] ?? '');
}
?>
</td>
<td><?= esc($employee['processed_by'] ?? $employee['emp_processed_by'] ?? '-') ?></td>
<td><?= esc($employee['reject_reason'] ?? $employee['emp_reject_reason'] ?? '-') ?></td>
<td><?= format_indian_number($employee['basic_cover_si'] ?? 0) ?></td>
<td><?= format_indian_number($employee['premium'] ?? 0) ?></td>
<td><?= format_indian_number($employee['rata_premimum'] ?? 0) ?></td>
<td><?= format_indian_number($employee['gst'] ?? 0) ?></td>
<td>
<?php if ($rowStatus === 'pending_approval') { ?>
<div class="btn-group dropdown">
<a href="javascript:void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown" aria-expanded="false">
<i class="mdi mdi-dots-horizontal"></i>
</a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="javascript:void(0);"
onclick="processPendingDependent('<?= $employee['employee_id'] ?>', '<?= $employee['client_policy_id'] ?>', 'approved')">
<i class="mdi mdi-check-circle mr-2 text-success font-18 vertical-middle"></i>Approve
</a>
<a class="dropdown-item" href="javascript:void(0);"
onclick="processPendingDependent('<?= $employee['employee_id'] ?>', '<?= $employee['client_policy_id'] ?>', 'rejected')">
<i class="mdi mdi-close-circle mr-2 text-danger font-18 vertical-middle"></i>Reject
</a>
</div>
</div>
<?php } else { ?>
-
<?php } ?>
</td>
</tr>
<?php }
} ?>
</tbody>
<tfoot>
<tr>
<th colspan="<?= (($getData['client_id'] ?? '0') != '0' && ($getData['branch_id'] ?? '0') == '0') ? 18 : 17 ?>"></th>
<th>Total</th>
<th><?= format_indian_number($pro_rata_total) ?></th>
<th><?= format_indian_number($gst_total) ?></th>
<th></th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
if ($.fn.DataTable && !$.fn.DataTable.isDataTable('#employee-data-list-table')) {
$('#employee-data-list-table').DataTable({
scrollX: true,
order: [],
pageLength: 25
});
}
</script>

View File

@ -360,6 +360,34 @@
function processPendingDependent(employeeId, clientPolicyId, status) {
var actionLabel = status === 'approved' ? 'approve' : 'reject';
if (status === 'rejected') {
Swal.fire({
title: 'Reject Dependent',
html: '<p class="mb-2">Please enter the reject reason.</p>',
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: '<?= 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 (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: '<?= base_url('processDependentAdd') ?>',
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');
}
});
}
</script>