FIX_DEPENDENT_ADDITION

This commit is contained in:
VENKATESHWARAN 2026-08-07 17:59:33 +05:30
parent ca0eee9625
commit 779e20a648

View File

@ -7004,8 +7004,11 @@ class EmployeeRestController extends AdminController
{
try {
$data = $this->request->getJSON(true);
log_message('error', '[processDependentAdd] Step 1: Request received | payload=' . json_encode($data));
$this->myLogger->logme('error', '[processDependentAdd] Step 1: Request received | payload=' . json_encode($data));
if (empty($data)) {
log_message('error', '[processDependentAdd] Step 1 failed: Request body is required');
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'Request body is required'], 200);
}
@ -7018,75 +7021,118 @@ class EmployeeRestController extends AdminController
// Processor role: HR (app) or ACM (internal portal session user) — used for approve and reject
$processed_by_role = ! empty($hr_id) ? 'HR' : 'ACM';
log_message('error', '[processDependentAdd] Step 2: Parsed params | employee_id=' . $employee_id
. ' | client_policy_id=' . $client_policy_id
. ' | status=' . $status
. ' | hr_id=' . ($hr_id ?? 'null')
. ' | updated_by=' . ($updated_by ?? 'null')
. ' | processed_by_role=' . $processed_by_role);
$this->myLogger->logme('error', '[processDependentAdd] Step 2: Parsed params | employee_id=' . $employee_id
. ' | client_policy_id=' . $client_policy_id
. ' | status=' . $status
. ' | processed_by_role=' . $processed_by_role);
if (empty($employee_id)) {
log_message('error', '[processDependentAdd] Step 2 failed: employee_id is required');
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'employee_id is required'], 200);
}
if (empty($client_policy_id)) {
log_message('error', '[processDependentAdd] Step 2 failed: client_policy_id is required');
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'client_policy_id is required'], 200);
}
if (empty($status)) {
log_message('error', '[processDependentAdd] Step 2 failed: status is required');
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'status is required'], 200);
}
if ($status !== 'approved' && $status !== 'rejected') {
log_message('error', '[processDependentAdd] Step 2 failed: status is invalid | status=' . $status);
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'status is invalid'], 200);
}
if ($status === 'rejected' && $reject_reason === '') {
log_message('error', '[processDependentAdd] Step 2 failed: reject_reason is required for rejection');
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'reject_reason is required'], 200);
}
log_message('error', '[processDependentAdd] Step 3: Fetching employee | employee_id=' . $employee_id);
$employee = $this->employeeModel
->where('id', $employee_id)
->first();
if (!$employee) {
log_message('error', '[processDependentAdd] Step 3 failed: Employee not found | employee_id=' . $employee_id);
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Employee not found'], 200);
}
$emp_status = strtolower((string) ($employee['emp_status'] ?? ''));
log_message('error', '[processDependentAdd] Step 3 success: Employee found | emp_code=' . ($employee['emp_code'] ?? '')
. ' | relationship=' . ($employee['relationship'] ?? '')
. ' | emp_status=' . $emp_status
. ' | family_floater_key=' . ($employee['family_floater_key'] ?? ''));
if ($emp_status === 'active') {
log_message('error', '[processDependentAdd] Step 4 failed: Dependent is already approved | employee_id=' . $employee_id);
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'Dependent is already approved'], 200);
}
if ($status === 'approved') {
if (! in_array($emp_status, ['pending_approval', 'rejected'], true)) {
log_message('error', '[processDependentAdd] Step 4 failed: Dependent cannot be approved | emp_status=' . $emp_status);
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'Dependent cannot be approved'], 200);
}
} elseif ($emp_status !== 'pending_approval') {
log_message('error', '[processDependentAdd] Step 4 failed: Only pending dependents can be rejected | emp_status=' . $emp_status);
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'Only pending dependents can be rejected'], 200);
}
log_message('error', '[processDependentAdd] Step 4: Employee status validation passed | emp_status=' . $emp_status);
log_message('error', '[processDependentAdd] Step 5: Fetching employee policy | employee_id=' . $employee_id
. ' | client_policy_id=' . $client_policy_id);
$employee_policy = $this->employeePolicyModel
->where('employee_id', $employee_id)
->where('client_policy_id', $client_policy_id)
->first();
if (!$employee_policy) {
log_message('error', '[processDependentAdd] Step 5 failed: Employee policy not found');
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'Employee policy not found'], 200);
}
$policy_status = strtolower((string) ($employee_policy['status'] ?? ''));
log_message('error', '[processDependentAdd] Step 5 success: Employee policy found | policy_status=' . $policy_status);
if ($status === 'approved') {
if (! in_array($policy_status, ['pending_approval', 'rejected'], true)) {
log_message('error', '[processDependentAdd] Step 6 failed: Employee policy cannot be approved | policy_status=' . $policy_status);
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'Employee policy cannot be approved'], 200);
}
} elseif ($policy_status !== 'pending_approval') {
log_message('error', '[processDependentAdd] Step 6 failed: Only pending employee policy can be rejected | policy_status=' . $policy_status);
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'Only pending employee policy can be rejected'], 200);
}
log_message('error', '[processDependentAdd] Step 6: Policy status validation passed | policy_status=' . $policy_status);
if ($status === 'approved') {
log_message('error', '[processDependentAdd] Step 7: Checking family floater spouse/child limit');
$floaterLimitCheck = $this->checkFamilyFloaterDependentLimit(
$employee,
(int) $client_policy_id,
(int) $employee_id
);
log_message('error', '[processDependentAdd] Step 7 result: family floater check | '
. json_encode($floaterLimitCheck));
$this->myLogger->logme('error', '[processDependentAdd] Step 7 result: family floater check | '
. json_encode($floaterLimitCheck));
if ($floaterLimitCheck['allowed'] === false) {
log_message('error', '[processDependentAdd] Step 7 failed: Limit exceeded | message='
. ($floaterLimitCheck['message'] ?? ''));
return $this->respond([
'status' => 'failed',
'code' => 400,
@ -7108,6 +7154,7 @@ class EmployeeRestController extends AdminController
'is_active' => 1,
'reject_reason' => null,
];
log_message('error', '[processDependentAdd] Step 8: Prepared approve update data');
} else {
$employee_update_data = [
'updated_by' => $updated_by,
@ -7123,14 +7170,22 @@ class EmployeeRestController extends AdminController
'status' => 'rejected',
'is_active' => 0,
];
log_message('error', '[processDependentAdd] Step 8: Prepared reject update data | reject_reason=' . $reject_reason);
}
log_message('error', '[processDependentAdd] Step 9: Updating employee | employee_id=' . $employee_id
. ' | data=' . json_encode($employee_update_data));
$employee_updated = $this->employeeModel->update($employee_id, $employee_update_data);
if (!$employee_updated) {
log_message('error', '[processDependentAdd] Step 9 failed: Failed to update employee status');
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => 'Failed to update employee status'], 200);
}
log_message('error', '[processDependentAdd] Step 9 success: Employee updated');
log_message('error', '[processDependentAdd] Step 10: Updating employee policy | employee_id=' . $employee_id
. ' | client_policy_id=' . $client_policy_id
. ' | data=' . json_encode($policy_update_data));
$policy_updated = $this->employeePolicyModel
->where('employee_id', $employee_id)
->where('client_policy_id', $client_policy_id)
@ -7138,31 +7193,60 @@ class EmployeeRestController extends AdminController
->update();
if (!$policy_updated) {
log_message('error', '[processDependentAdd] Step 10 failed: Failed to update employee policy status');
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => 'Failed to update employee policy status'], 200);
}
log_message('error', '[processDependentAdd] Step 10 success: Employee policy updated');
$truncatedDependentIds = [];
if ($status === 'approved') {
log_message('error', '[processDependentAdd] Step 11: Truncating excess pending dependents if limit exceeded');
$truncatedDependentIds = $this->truncateExcessPendingDependents(
$employee,
(int) $client_policy_id,
(int) $employee_id,
$updated_by,
$processed_by_role
);
log_message('error', '[processDependentAdd] Step 11 result: truncated_dependent_ids='
. json_encode($truncatedDependentIds));
$this->myLogger->logme('error', '[processDependentAdd] Step 11 result: truncated_dependent_ids='
. json_encode($truncatedDependentIds));
}
if ($status === 'rejected') {
log_message('error', '[processDependentAdd] Step 12: Sending dependent rejected notification mail');
$this->sendDependentRejectedNotificationMail(
employee: $employee,
employeePolicy: $employee_policy,
rejectReason: $reject_reason,
processedByRole: $processed_by_role
);
log_message('error', '[processDependentAdd] Step 12 done: Reject notification mail triggered');
}
log_message('error', '[processDependentAdd] Step 13: Success | status=' . $status
. ' | employee_id=' . $employee_id
. ' | truncated_dependent_ids=' . json_encode($truncatedDependentIds));
$this->myLogger->logme('error', '[processDependentAdd] Step 13: Success | status=' . $status
. ' | employee_id=' . $employee_id
. ' | truncated_dependent_ids=' . json_encode($truncatedDependentIds));
return $this->respond([
'status' => 'success',
'code' => 200,
'message' => $status === 'approved' ? 'Dependent approved successfully' : 'Dependent rejected successfully',
'data' => [
'employee_id' => $employee_id,
'client_policy_id' => $client_policy_id,
'status' => $status,
'reject_reason' => $status === 'rejected' ? $reject_reason : null,
'employee_id' => $employee_id,
'client_policy_id' => $client_policy_id,
'status' => $status,
'reject_reason' => $status === 'rejected' ? $reject_reason : null,
'truncated_dependent_ids' => $truncatedDependentIds,
],
], 200);
} catch (\Exception $e) {
log_message('error', 'Error in processDependentAdd: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine());
$this->myLogger->logme('error', 'Error in processDependentAdd: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine());
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
}
}
@ -7174,6 +7258,12 @@ class EmployeeRestController extends AdminController
*/
private function checkFamilyFloaterDependentLimit(array $employee, int $clientPolicyId, int $employeeId): array
{
log_message('error', '[checkFamilyFloaterDependentLimit] Start | employee_id=' . $employeeId
. ' | client_policy_id=' . $clientPolicyId
. ' | emp_code=' . ($employee['emp_code'] ?? '')
. ' | relationship=' . ($employee['relationship'] ?? '')
. ' | family_floater_key=' . ($employee['family_floater_key'] ?? ''));
$relationship = strtolower(trim((string) ($employee['relationship'] ?? '')));
$floaterKey = ! empty($employee['family_floater_key'])
? preg_replace('/\d/', '', strtolower(trim((string) $employee['family_floater_key'])))
@ -7182,18 +7272,26 @@ class EmployeeRestController extends AdminController
$isSpouse = ($relationship === 'spouse' || $floaterKey === 'spouse');
$isChild = in_array($relationship, ['son', 'daughter'], true) || $floaterKey === 'child';
log_message('error', '[checkFamilyFloaterDependentLimit] Resolved type | relationship=' . $relationship
. ' | floater_key=' . $floaterKey
. ' | is_spouse=' . ($isSpouse ? '1' : '0')
. ' | is_child=' . ($isChild ? '1' : '0'));
// Only spouse / child are validated against family_floaters for dependent-add approve
if (! $isSpouse && ! $isChild) {
log_message('error', '[checkFamilyFloaterDependentLimit] Skip: not spouse/child, allowing');
return ['allowed' => true, 'message' => null];
}
$clientPolicy = $this->clientPolicyModel->select('policy_terms')->where('id', $clientPolicyId)->first();
if (empty($clientPolicy['policy_terms'])) {
log_message('error', '[checkFamilyFloaterDependentLimit] Skip: policy_terms empty, allowing');
return ['allowed' => true, 'message' => null];
}
$policyTerms = json_decode($clientPolicy['policy_terms'], true);
if (! is_array($policyTerms) || empty($policyTerms['family_floaters'])) {
log_message('error', '[checkFamilyFloaterDependentLimit] Skip: family_floaters missing, allowing');
return ['allowed' => true, 'message' => null];
}
@ -7204,6 +7302,10 @@ class EmployeeRestController extends AdminController
$allowedSpouseCount = (int) ($familyFloaters['spouse'] ?? 0);
$allowedChildCount = (int) ($familyFloaters['childrens'] ?? 0);
log_message('error', '[checkFamilyFloaterDependentLimit] Allowed counts | spouse=' . $allowedSpouseCount
. ' | childrens=' . $allowedChildCount
. ' | family_floaters=' . json_encode($familyFloaters));
$existingFamily = $this->employeeModel
->select('employees.id, employees.relationship, employees.family_floater_key')
->join('employee_polices', 'employee_polices.employee_id = employees.id')
@ -7217,6 +7319,9 @@ class EmployeeRestController extends AdminController
->where('employees.id !=', $employeeId)
->findAll();
log_message('error', '[checkFamilyFloaterDependentLimit] Active family members found | count='
. count($existingFamily) . ' | members=' . json_encode($existingFamily));
$activeSpouseCount = 0;
$activeChildCount = 0;
@ -7233,29 +7338,201 @@ class EmployeeRestController extends AdminController
}
}
log_message('error', '[checkFamilyFloaterDependentLimit] Active counts | spouse=' . $activeSpouseCount
. ' | child=' . $activeChildCount);
if ($isSpouse) {
if (($activeSpouseCount + 1) > $allowedSpouseCount) {
$message = 'Spouse count exceeds policy family floater limit. Allowed: '
. $allowedSpouseCount . ', existing active: ' . $activeSpouseCount;
log_message('error', '[checkFamilyFloaterDependentLimit] Blocked: ' . $message);
return [
'allowed' => false,
'message' => 'Spouse count exceeds policy family floater limit. Allowed: '
. $allowedSpouseCount . ', existing active: ' . $activeSpouseCount,
'message' => $message,
];
}
}
if ($isChild) {
if (($activeChildCount + 1) > $allowedChildCount) {
$message = 'Child count exceeds policy family floater limit. Allowed: '
. $allowedChildCount . ', existing active: ' . $activeChildCount;
log_message('error', '[checkFamilyFloaterDependentLimit] Blocked: ' . $message);
return [
'allowed' => false,
'message' => 'Child count exceeds policy family floater limit. Allowed: '
. $allowedChildCount . ', existing active: ' . $activeChildCount,
'message' => $message,
];
}
}
log_message('error', '[checkFamilyFloaterDependentLimit] Allowed: within family floater limit');
return ['allowed' => true, 'message' => null];
}
/**
* After approving a spouse/child: if family_floaters slots are full (or pending would exceed),
* mark excess remaining pending_approval dependents as truncate + is_active = 0.
*
* @return int[] Truncated employee ids
*/
private function truncateExcessPendingDependents(
array $employee,
int $clientPolicyId,
int $approvedEmployeeId,
$updatedBy,
string $processedByRole
): array {
log_message('error', '[truncateExcessPendingDependents] Start | approved_employee_id=' . $approvedEmployeeId
. ' | client_policy_id=' . $clientPolicyId
. ' | emp_code=' . ($employee['emp_code'] ?? '')
. ' | relationship=' . ($employee['relationship'] ?? '')
. ' | updated_by=' . ($updatedBy ?? 'null')
. ' | processed_by_role=' . $processedByRole);
$relationship = strtolower(trim((string) ($employee['relationship'] ?? '')));
$floaterKey = ! empty($employee['family_floater_key'])
? preg_replace('/\d/', '', strtolower(trim((string) $employee['family_floater_key'])))
: strtolower((string) ($this->RelationshipMap($employee['relationship'] ?? '') ?? ''));
$isSpouse = ($relationship === 'spouse' || $floaterKey === 'spouse');
$isChild = in_array($relationship, ['son', 'daughter'], true) || $floaterKey === 'child';
log_message('error', '[truncateExcessPendingDependents] Resolved type | is_spouse=' . ($isSpouse ? '1' : '0')
. ' | is_child=' . ($isChild ? '1' : '0'));
if (! $isSpouse && ! $isChild) {
log_message('error', '[truncateExcessPendingDependents] Skip: not spouse/child');
return [];
}
$clientPolicy = $this->clientPolicyModel->select('policy_terms')->where('id', $clientPolicyId)->first();
if (empty($clientPolicy['policy_terms'])) {
log_message('error', '[truncateExcessPendingDependents] Skip: policy_terms empty');
return [];
}
$policyTerms = json_decode($clientPolicy['policy_terms'], true);
if (! is_array($policyTerms) || empty($policyTerms['family_floaters'])) {
log_message('error', '[truncateExcessPendingDependents] Skip: family_floaters missing');
return [];
}
$familyFloaters = is_array($policyTerms['family_floaters'])
? $policyTerms['family_floaters']
: (array) $policyTerms['family_floaters'];
$allowedCount = $isSpouse
? (int) ($familyFloaters['spouse'] ?? 0)
: (int) ($familyFloaters['childrens'] ?? 0);
log_message('error', '[truncateExcessPendingDependents] Allowed count for type | allowed=' . $allowedCount
. ' | family_floaters=' . json_encode($familyFloaters));
$familyMembers = $this->employeeModel
->select('employees.id, employees.relationship, employees.family_floater_key, employees.emp_status, employee_polices.status as policy_status')
->join('employee_polices', 'employee_polices.employee_id = employees.id')
->where('employees.emp_code', $employee['emp_code'])
->where('employees.client_id', $employee['client_id'])
->where('employee_polices.client_policy_id', $clientPolicyId)
->where('employees.id !=', $approvedEmployeeId)
->groupStart()
->groupStart()
->where('employees.emp_status', 'active')
->where('employee_polices.status', 'active')
->where('employees.is_active', 1)
->where('employee_polices.is_active', 1)
->groupEnd()
->orGroupStart()
->where('employees.emp_status', 'pending_approval')
->where('employee_polices.status', 'pending_approval')
->groupEnd()
->groupEnd()
->orderBy('employees.id', 'ASC')
->findAll();
log_message('error', '[truncateExcessPendingDependents] Family members fetched | count='
. count($familyMembers) . ' | members=' . json_encode($familyMembers));
$activeCount = 1; // just-approved dependent already counts toward the limit
$pendingIds = [];
foreach ($familyMembers as $member) {
$memberRelationship = strtolower(trim((string) ($member['relationship'] ?? '')));
$memberFloaterKey = ! empty($member['family_floater_key'])
? preg_replace('/\d/', '', strtolower(trim((string) $member['family_floater_key'])))
: '';
$sameType = $isSpouse
? ($memberRelationship === 'spouse' || $memberFloaterKey === 'spouse')
: (in_array($memberRelationship, ['son', 'daughter'], true) || $memberFloaterKey === 'child');
if (! $sameType) {
continue;
}
$memberEmpStatus = strtolower((string) ($member['emp_status'] ?? ''));
$memberPolicyStatus = strtolower((string) ($member['policy_status'] ?? ''));
if ($memberEmpStatus === 'active' && $memberPolicyStatus === 'active') {
$activeCount++;
} elseif ($memberEmpStatus === 'pending_approval' && $memberPolicyStatus === 'pending_approval') {
$pendingIds[] = (int) $member['id'];
}
}
log_message('error', '[truncateExcessPendingDependents] Counts | active_including_approved=' . $activeCount
. ' | pending_ids=' . json_encode($pendingIds));
if (empty($pendingIds)) {
log_message('error', '[truncateExcessPendingDependents] No pending dependents to truncate');
return [];
}
$remainingSlots = max(0, $allowedCount - $activeCount);
// Keep only as many pending as remaining slots; truncate the rest (excess)
$idsToTruncate = array_slice($pendingIds, $remainingSlots);
log_message('error', '[truncateExcessPendingDependents] Slot calc | allowed=' . $allowedCount
. ' | active=' . $activeCount
. ' | remaining_slots=' . $remainingSlots
. ' | ids_to_truncate=' . json_encode($idsToTruncate));
if (empty($idsToTruncate)) {
log_message('error', '[truncateExcessPendingDependents] No excess pending; nothing to truncate');
return [];
}
log_message('error', '[truncateExcessPendingDependents] Updating employees to truncate | ids='
. json_encode($idsToTruncate));
$this->employeeModel
->whereIn('id', $idsToTruncate)
->set([
'emp_status' => 'truncate',
'is_active' => 0,
'updated_by' => $updatedBy,
'processed_by' => $processedByRole,
])
->update();
log_message('error', '[truncateExcessPendingDependents] Updating employee_polices to truncate | ids='
. json_encode($idsToTruncate) . ' | client_policy_id=' . $clientPolicyId);
$this->employeePolicyModel
->whereIn('employee_id', $idsToTruncate)
->where('client_policy_id', $clientPolicyId)
->set([
'status' => 'truncate',
'is_active' => 0,
'updated_by' => $updatedBy,
'processed_by' => $processedByRole,
])
->update();
log_message('error', '[truncateExcessPendingDependents] Done | truncated_ids=' . json_encode($idsToTruncate));
$this->myLogger->logme('error', '[truncateExcessPendingDependents] Done | truncated_ids=' . json_encode($idsToTruncate));
return $idsToTruncate;
}
/**
* List dependent-add workflow members (API + mobile).
* Returns only pending_approval, approved, and rejected dependents (not all members).