MERGE_LIVE_BUG_FIXES
This commit is contained in:
commit
02ede5d348
3
.gitignore
vendored
3
.gitignore
vendored
@ -28,6 +28,9 @@ writable/**/*.sqlite
|
||||
writable/tmp/*
|
||||
!writable/tmp/.gitkeep
|
||||
|
||||
writable/insurer_claim_form/*
|
||||
!writable/insurer_claim_form/Claim_Form_IRDAI.pdf
|
||||
|
||||
vendor/
|
||||
build/
|
||||
composer.lock
|
||||
|
||||
@ -133,3 +133,5 @@ define('UPLOAD_EXT_EXCEL', ['xls', 'xlsx', 'ods', 'csv']);
|
||||
define('UPLOAD_EXT_MAIL_ATTACHMENTS', ['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx', 'xls', 'xlsx']);
|
||||
define('UPLOAD_EXT_NON_EB_RACK_RATE', ['pdf', 'xls', 'xlsx']);
|
||||
define('UPLOAD_EXT_ASSET_FILES', ['pdf', 'xls', 'xlsx', 'csv']);
|
||||
define('UPLOAD_EXT_INSURER_CLAIM_FORM', ['pdf', 'doc', 'docx']);
|
||||
define('INSURER_DEFAULT_CLAIM_FORM_FILE', 'Claim_Form_IRDAI.pdf');
|
||||
|
||||
@ -257,6 +257,7 @@ $routes->group("/master", ["filter" => "authMVC"], function ($routes) {
|
||||
$routes->get("create", "MasterController::insurerOnboarding");
|
||||
$routes->post("createpost", "MasterController::createInsurerGeneralInfo");
|
||||
$routes->post("edit", "MasterController::editInsurerGeneralInfo");
|
||||
$routes->get("download-claim-form/(:num)", "MasterController::downloadInsurerClaimForm/$1");
|
||||
$routes->get("list/(:any)", "MasterController::editInsurerOnboarding/$1");
|
||||
|
||||
$routes->group("branch", ["filter" => "authMVC"], function ($routes) {
|
||||
@ -464,6 +465,8 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) {
|
||||
$routes->get('insertSampleTpaApiData/(:any)', 'TestingController::insertSampleTpaApiData/$1');
|
||||
$routes->get('listEmployeeCountByClientPolicy', 'TestingController::listEmployeeCountByClientPolicy');
|
||||
$routes->get('testMediAssistWellness','TestingController::testMediAssistWellness');
|
||||
$routes->match(['get', 'post'], 'decryptVisitSso', 'ApiServiceController::decryptVisitSso'); // disabled for now do not remove this
|
||||
$routes->get("getInsurerClaimFormDownloadUrl", "EmployeeRestController::getInsurerClaimFormDownloadUrl");
|
||||
|
||||
$routes->group('claims-collection-v2', static function ($routes) {
|
||||
$routes->get('preview', 'ClaimsCollectionV2DashboardController::preview');
|
||||
@ -750,6 +753,7 @@ $routes->group("employeeRest", ["filter" => ['GlobalPostFileUploadGuard', 'ratel
|
||||
$routes->get("cdTransactionData", "EmployeeRestController::cdTransactionData");
|
||||
$routes->match( ['get', 'post'], 'claimsSearch','EmployeeRestController::claimsSearch');
|
||||
$routes->get("claimView", "EmployeeRestController::claimView");
|
||||
$routes->get("getInsurerClaimFormDownloadUrl", "EmployeeRestController::getInsurerClaimFormDownloadUrl");
|
||||
$routes->get("exportCashDepositData", "EmployeeRestController::exportCashDepositData");
|
||||
$routes->get("hrFileList", "EmployeeRestController::hrFileList");
|
||||
$routes->get("hrFileUploadMasters", "EmployeeRestController::hrFileUploadMasters");
|
||||
|
||||
@ -580,6 +580,86 @@ class ApiServiceController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt Visit SSO userParams (reverse of Visit encryption in getWellnessUrl).
|
||||
* Accepts encrypted string or full SSO URL via POST/GET as userParams or encrypted_sso.
|
||||
*/
|
||||
public function decryptVisitSso()
|
||||
{
|
||||
$encrypted = $this->request->getGet('encrypted_sso');
|
||||
|
||||
if (empty($encrypted)) {
|
||||
return $this->response->setJSON([
|
||||
'status' => false,
|
||||
'message' => 'userParams or encrypted_sso is required',
|
||||
]);
|
||||
}
|
||||
|
||||
$result = $this->decryptVisitSsoParams($encrypted);
|
||||
|
||||
if ($result === false) {
|
||||
return $this->response->setJSON([
|
||||
'status' => false,
|
||||
'message' => 'Decryption failed. Check encrypted value and VISIT_SECRET_KEY / VISIT_IV.',
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => true,
|
||||
'data' => $result['params'],
|
||||
'plainText' => $result['plainText'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt Visit SSO payload using the same key/IV as getWellnessUrl Visit branch.
|
||||
*
|
||||
* @param string $encrypted Base64URL userParams value or full SSO URL containing userParams=
|
||||
* @return array{plainText: string, params: array}|false
|
||||
*/
|
||||
private function decryptVisitSsoParams(string $encrypted)
|
||||
{
|
||||
if (strpos($encrypted, 'userParams=') !== false) {
|
||||
$query = [];
|
||||
parse_str(parse_url($encrypted, PHP_URL_QUERY) ?? '', $query);
|
||||
$encrypted = $query['userParams'] ?? '';
|
||||
}
|
||||
|
||||
$encrypted = trim($encrypted);
|
||||
if ($encrypted === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$base64 = strtr($encrypted, '-_', '+/');
|
||||
$base64 .= str_repeat('=', (4 - strlen($base64) % 4) % 4);
|
||||
|
||||
$cipherRaw = base64_decode($base64, true);
|
||||
if ($cipherRaw === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$derivedKey = hash('sha256', env('VISIT_SECRET_KEY'), true);
|
||||
$plainText = openssl_decrypt(
|
||||
$cipherRaw,
|
||||
'aes-256-cbc',
|
||||
$derivedKey,
|
||||
OPENSSL_RAW_DATA,
|
||||
env('VISIT_IV')
|
||||
);
|
||||
|
||||
if ($plainText === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$params = [];
|
||||
parse_str($plainText, $params);
|
||||
|
||||
return [
|
||||
'plainText' => $plainText,
|
||||
'params' => $params,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Manual TPA Claim Push - accepts claim_id via POST and delegates to pushClaims().
|
||||
* Returns the response from pushClaims() as the API response.
|
||||
|
||||
@ -1037,7 +1037,7 @@ class ClientController extends AdminController
|
||||
$editData['client_branch'] = $this->clientBranchModel->where(['client_id' => $id, 'is_active' => 1])->findAll();
|
||||
$editData['client_relation'] = $this->clientRMModel->where(['client_id' => $id, 'is_active' => 1])->findAll();
|
||||
$editData['client_branch']['role'] = get_role_id();
|
||||
$editData['lead_data'] = $this->leadsModel->getLeadForInsertClientList(null, $id, 'update');
|
||||
$editData['lead_data'] = $this->leadsModel->getLeadForInsertClientList([1,3,2], $id, 'update');
|
||||
$clientPoliceData = $this->clientPolicyModel->getClientPolicyByClientId($id);
|
||||
|
||||
foreach ($clientPoliceData as $key => $value) {
|
||||
|
||||
@ -5941,4 +5941,196 @@ class EmployeeRestController extends AdminController
|
||||
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to sent mail.', 'result' => $result], 200);
|
||||
}
|
||||
|
||||
public function getInsurerClaimFormDownloadUrl()
|
||||
{
|
||||
$clientPolicyId = trim((string) ($this->request->getGet('client_policy_id') ?? ''));
|
||||
|
||||
if ($clientPolicyId === '') {
|
||||
$errorContext = [
|
||||
'api' => 'getInsurerClaimFormDownloadUrl',
|
||||
'error' => 'client_policy_id is required',
|
||||
'client_policy_id' => null,
|
||||
'request_uri' => (string) current_url(),
|
||||
'request_method' => $this->request->getMethod(),
|
||||
];
|
||||
log_message('error', '[getInsurerClaimFormDownloadUrl] Missing required parameter | ' . json_encode($errorContext));
|
||||
$this->myLogger->logme('error', '[getInsurerClaimFormDownloadUrl] Missing required parameter | ' . json_encode($errorContext));
|
||||
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 400,
|
||||
'message' => 'client_policy_id is required',
|
||||
'data' => [],
|
||||
], 200);
|
||||
}
|
||||
|
||||
if (! ctype_digit($clientPolicyId) || (int) $clientPolicyId <= 0) {
|
||||
$errorContext = [
|
||||
'api' => 'getInsurerClaimFormDownloadUrl',
|
||||
'error' => 'Invalid client_policy_id',
|
||||
'client_policy_id' => $clientPolicyId,
|
||||
'request_uri' => (string) current_url(),
|
||||
];
|
||||
log_message('error', '[getInsurerClaimFormDownloadUrl] Invalid client_policy_id | ' . json_encode($errorContext));
|
||||
$this->myLogger->logme('error', '[getInsurerClaimFormDownloadUrl] Invalid client_policy_id | ' . json_encode($errorContext));
|
||||
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 400,
|
||||
'message' => 'client_policy_id must be a valid positive number',
|
||||
'data' => [],
|
||||
], 200);
|
||||
}
|
||||
|
||||
try {
|
||||
$clientPolicy = $this->clientPolicyModel
|
||||
->select('client_policy.id, client_policy.insurer_id, client_policy.policy_no, client_policy.is_active, insurers.id as insurer_pk, insurers.name as insurer_name, insurers.short_name as insurer_short_name, insurers.is_active as insurer_is_active, insurers.insurer_claim_form, insurers.insurer_claim_form_original_name')
|
||||
->join('insurers', 'insurers.id = client_policy.insurer_id', 'left')
|
||||
->where('client_policy.id', (int) $clientPolicyId)
|
||||
->first();
|
||||
|
||||
if (empty($clientPolicy)) {
|
||||
$errorContext = [
|
||||
'api' => 'getInsurerClaimFormDownloadUrl',
|
||||
'error' => 'Client policy not found',
|
||||
'client_policy_id' => (int) $clientPolicyId,
|
||||
'request_uri' => (string) current_url(),
|
||||
];
|
||||
log_message('error', '[getInsurerClaimFormDownloadUrl] Client policy not found | ' . json_encode($errorContext));
|
||||
$this->myLogger->logme('error', '[getInsurerClaimFormDownloadUrl] Client policy not found | ' . json_encode($errorContext));
|
||||
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 404,
|
||||
'message' => 'Client policy not found for the given client_policy_id',
|
||||
'data' => [],
|
||||
], 200);
|
||||
}
|
||||
|
||||
if ((int) ($clientPolicy['is_active'] ?? 0) !== 1) {
|
||||
$errorContext = [
|
||||
'api' => 'getInsurerClaimFormDownloadUrl',
|
||||
'error' => 'Client policy is inactive',
|
||||
'client_policy_id' => (int) $clientPolicyId,
|
||||
'policy_no' => $clientPolicy['policy_no'] ?? null,
|
||||
];
|
||||
log_message('error', '[getInsurerClaimFormDownloadUrl] Inactive client policy | ' . json_encode($errorContext));
|
||||
$this->myLogger->logme('error', '[getInsurerClaimFormDownloadUrl] Inactive client policy | ' . json_encode($errorContext));
|
||||
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 404,
|
||||
'message' => 'Client policy is inactive',
|
||||
'data' => [],
|
||||
], 200);
|
||||
}
|
||||
|
||||
if (empty($clientPolicy['insurer_id'])) {
|
||||
$errorContext = [
|
||||
'api' => 'getInsurerClaimFormDownloadUrl',
|
||||
'error' => 'Insurer is not mapped to client policy',
|
||||
'client_policy_id' => (int) $clientPolicyId,
|
||||
'policy_no' => $clientPolicy['policy_no'] ?? null,
|
||||
];
|
||||
log_message('error', '[getInsurerClaimFormDownloadUrl] Insurer not mapped | ' . json_encode($errorContext));
|
||||
$this->myLogger->logme('error', '[getInsurerClaimFormDownloadUrl] Insurer not mapped | ' . json_encode($errorContext));
|
||||
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 404,
|
||||
'message' => 'Insurer is not mapped to this client policy',
|
||||
'data' => [],
|
||||
], 200);
|
||||
}
|
||||
|
||||
if ((int) ($clientPolicy['insurer_is_active'] ?? 0) !== 1) {
|
||||
$errorContext = [
|
||||
'api' => 'getInsurerClaimFormDownloadUrl',
|
||||
'error' => 'Insurer is inactive',
|
||||
'client_policy_id' => (int) $clientPolicyId,
|
||||
'insurer_id' => (int) $clientPolicy['insurer_id'],
|
||||
'insurer_name' => $clientPolicy['insurer_name'] ?? null,
|
||||
];
|
||||
log_message('error', '[getInsurerClaimFormDownloadUrl] Inactive insurer | ' . json_encode($errorContext));
|
||||
$this->myLogger->logme('error', '[getInsurerClaimFormDownloadUrl] Inactive insurer | ' . json_encode($errorContext));
|
||||
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 404,
|
||||
'message' => 'Insurer is inactive for this client policy',
|
||||
'data' => [],
|
||||
], 200);
|
||||
}
|
||||
|
||||
$insurerData = [
|
||||
'id' => $clientPolicy['insurer_id'],
|
||||
'name' => $clientPolicy['insurer_name'] ?? '',
|
||||
'short_name' => $clientPolicy['insurer_short_name'] ?? '',
|
||||
'insurer_claim_form' => $clientPolicy['insurer_claim_form'] ?? null,
|
||||
'insurer_claim_form_original_name'=> $clientPolicy['insurer_claim_form_original_name'] ?? null,
|
||||
];
|
||||
|
||||
$claimFormAvailability = resolve_insurer_claim_form_file($insurerData);
|
||||
|
||||
if (empty($claimFormAvailability['available'])) {
|
||||
$errorContext = [
|
||||
'api' => 'getInsurerClaimFormDownloadUrl',
|
||||
'error' => 'Insurer claim form file not found',
|
||||
'client_policy_id' => (int) $clientPolicyId,
|
||||
'insurer_id' => (int) $clientPolicy['insurer_id'],
|
||||
'insurer_name' => $clientPolicy['insurer_name'] ?? null,
|
||||
'insurer_short_name' => $clientPolicy['insurer_short_name'] ?? null,
|
||||
'stored_claim_form' => $clientPolicy['insurer_claim_form'] ?? null,
|
||||
'checked_paths' => $claimFormAvailability['checked_paths'] ?? [],
|
||||
];
|
||||
log_message('error', '[getInsurerClaimFormDownloadUrl] Claim form file not found | ' . json_encode($errorContext));
|
||||
$this->myLogger->logme('error', '[getInsurerClaimFormDownloadUrl] Claim form file not found | ' . json_encode($errorContext));
|
||||
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 404,
|
||||
'message' => 'Insurer claim form is not available for this client policy',
|
||||
'data' => [],
|
||||
], 200);
|
||||
}
|
||||
|
||||
$downloadUrl = base_url('claim-form-download/' . md5((string) $clientPolicy['insurer_id']));
|
||||
|
||||
log_message('info', '[getInsurerClaimFormDownloadUrl] Success | ' . json_encode([
|
||||
'client_policy_id' => (int) $clientPolicyId,
|
||||
'insurer_id' => (int) $clientPolicy['insurer_id'],
|
||||
'file_source' => $claimFormAvailability['source'],
|
||||
'file_name' => $claimFormAvailability['file_name'],
|
||||
'download_url' => $downloadUrl,
|
||||
]));
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'code' => 200,
|
||||
'message' => 'Insurer claim form download URL fetched successfully',
|
||||
'data' => [
|
||||
'download_url' => $downloadUrl,
|
||||
],
|
||||
], 200);
|
||||
} catch (\Throwable $th) {
|
||||
$errorContext = [
|
||||
'api' => 'getInsurerClaimFormDownloadUrl',
|
||||
'error' => $th->getMessage(),
|
||||
'client_policy_id' => $clientPolicyId,
|
||||
'file' => $th->getFile(),
|
||||
'line' => $th->getLine(),
|
||||
'trace' => $th->getTraceAsString(),
|
||||
];
|
||||
log_message('error', '[getInsurerClaimFormDownloadUrl] Exception | ' . json_encode($errorContext));
|
||||
$this->myLogger->logme('error', '[getInsurerClaimFormDownloadUrl] Exception | ' . json_encode($errorContext));
|
||||
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 500,
|
||||
'message' => 'Unable to fetch insurer claim form download URL. Please try again later.',
|
||||
'data' => [],
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -4539,6 +4539,14 @@ class LeadsController extends BaseController
|
||||
return $this->respond(['status' => false, 'message' => 'Failed to create policy', 'data' => null], 200);
|
||||
}
|
||||
|
||||
if (empty($client_id) && ! empty($data['client_id'])) {
|
||||
$client_id = $data['client_id'];
|
||||
}
|
||||
|
||||
if (empty($branch_id) && ! empty($data['client_branch_id'])) {
|
||||
$branch_id = $data['client_branch_id'];
|
||||
}
|
||||
|
||||
$result = $this->createClientPolicyWithLeadData($data, $client_id, $branch_id);
|
||||
// print_r($result); die;
|
||||
|
||||
|
||||
@ -331,6 +331,13 @@ class MasterController extends AdminController
|
||||
'ext_in' => 'Allowed file types: jpg, jpeg, png',
|
||||
]
|
||||
],
|
||||
'insurer_claim_form' => [
|
||||
'rules' => 'if_exist|max_size[insurer_claim_form,5120]|ext_in[insurer_claim_form,pdf,doc,docx]',
|
||||
'errors' => [
|
||||
'max_size' => 'Claim form file size should not exceed 5 MB',
|
||||
'ext_in' => 'Allowed file types: pdf, doc, docx',
|
||||
]
|
||||
],
|
||||
];
|
||||
// $rules = [
|
||||
// 'name' => [
|
||||
@ -368,6 +375,14 @@ class MasterController extends AdminController
|
||||
|
||||
$uploadFilePath = ROOTPATH . 'public/uploads/logo/';
|
||||
$file_name = file_Upload($this->request->getFile('insurer_logo'), $uploadFilePath, UPLOAD_EXT_IMAGES);
|
||||
|
||||
$claimFormUploadPath = WRITEPATH . 'insurer_claim_form/';
|
||||
if (!is_dir($claimFormUploadPath)) {
|
||||
mkdir($claimFormUploadPath, 0777, true);
|
||||
}
|
||||
$insurerShortName = trim((string) ($this->request->getPost('short_name') ?? ''));
|
||||
$claimFormUpload = file_Upload_random_name($this->request->getFile('insurer_claim_form'), $claimFormUploadPath, UPLOAD_EXT_INSURER_CLAIM_FORM, $insurerShortName);
|
||||
|
||||
$data = $this->request->getPost();
|
||||
$sanitized_post_data = sanitizeInputArrayAdvanced($data);
|
||||
|
||||
@ -376,6 +391,10 @@ class MasterController extends AdminController
|
||||
$sanitized_post_data['is_multi_event'] = (!empty($sanitized_post_data['is_multi_event'])) ? 1 : 0;
|
||||
$sanitized_post_data['created_by'] = get_session_userid();
|
||||
$sanitized_post_data['insurer_logo'] = $file_name;
|
||||
if (!empty($claimFormUpload['stored_name'])) {
|
||||
$sanitized_post_data['insurer_claim_form'] = $claimFormUpload['stored_name'];
|
||||
$sanitized_post_data['insurer_claim_form_original_name'] = $claimFormUpload['original_name'];
|
||||
}
|
||||
|
||||
$insert = $this->insurerModel->insert($sanitized_post_data);
|
||||
|
||||
@ -573,6 +592,13 @@ class MasterController extends AdminController
|
||||
'ext_in' => 'Only JPG, JPEG, and PNG files are allowed.',
|
||||
]
|
||||
],
|
||||
'insurer_claim_form' => [
|
||||
'rules' => 'permit_empty|max_size[insurer_claim_form,5120]|ext_in[insurer_claim_form,pdf,doc,docx]',
|
||||
'errors' => [
|
||||
'max_size' => 'Claim form file size should not exceed 5 MB',
|
||||
'ext_in' => 'Allowed file types: pdf, doc, docx',
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
if (!$this->validateData($postData, $rules)) {
|
||||
@ -594,6 +620,18 @@ class MasterController extends AdminController
|
||||
|
||||
$uploadFilePath = ROOTPATH . 'public/uploads/logo/';
|
||||
$file_name = file_Upload($this->request->getFile('insurer_logo'), $uploadFilePath, UPLOAD_EXT_IMAGES);
|
||||
|
||||
$claimFormUploadPath = WRITEPATH . 'insurer_claim_form/';
|
||||
if (!is_dir($claimFormUploadPath)) {
|
||||
mkdir($claimFormUploadPath, 0777, true);
|
||||
}
|
||||
$insurerShortName = trim((string) ($this->request->getPost('short_name') ?? ''));
|
||||
if ($insurerShortName === '' && !empty($this->request->getPost('PrimaryKey'))) {
|
||||
$existingInsurerForShortName = $this->insurerModel->find($this->request->getPost('PrimaryKey'));
|
||||
$insurerShortName = trim((string) ($existingInsurerForShortName['short_name'] ?? ''));
|
||||
}
|
||||
$claimFormUpload = file_Upload_random_name($this->request->getFile('insurer_claim_form'), $claimFormUploadPath, UPLOAD_EXT_INSURER_CLAIM_FORM, $insurerShortName);
|
||||
|
||||
$data = $this->request->getPost();
|
||||
$sanitized_post_data = sanitizeInputArrayAdvanced($data);
|
||||
$id = $sanitized_post_data['PrimaryKey'];
|
||||
@ -607,6 +645,18 @@ class MasterController extends AdminController
|
||||
$sanitized_post_data['insurer_logo'] = $file_name;
|
||||
}
|
||||
|
||||
if (!empty($claimFormUpload['stored_name'])) {
|
||||
$existingInsurer = $this->insurerModel->find($id);
|
||||
if (!empty($existingInsurer['insurer_claim_form'])) {
|
||||
$oldClaimFormPath = $claimFormUploadPath . $existingInsurer['insurer_claim_form'];
|
||||
if (is_file($oldClaimFormPath)) {
|
||||
unlink($oldClaimFormPath);
|
||||
}
|
||||
}
|
||||
$sanitized_post_data['insurer_claim_form'] = $claimFormUpload['stored_name'];
|
||||
$sanitized_post_data['insurer_claim_form_original_name'] = $claimFormUpload['original_name'];
|
||||
}
|
||||
|
||||
$update = $this->insurerModel->update($id,$sanitized_post_data);
|
||||
|
||||
if($update){
|
||||
@ -616,6 +666,24 @@ class MasterController extends AdminController
|
||||
}
|
||||
}
|
||||
|
||||
public function downloadInsurerClaimForm($id = null)
|
||||
{
|
||||
$insurer = $this->insurerModel->where(['id' => (int) $id, 'is_active' => 1])->first();
|
||||
|
||||
if (empty($insurer)) {
|
||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound('Claim form file not found');
|
||||
}
|
||||
|
||||
$claimFormFile = resolve_insurer_claim_form_file($insurer);
|
||||
|
||||
if (empty($claimFormFile['available']) || empty($claimFormFile['file_path'])) {
|
||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound('Claim form file not found');
|
||||
}
|
||||
|
||||
return $this->response->download($claimFormFile['file_path'], null)
|
||||
->setFileName($claimFormFile['file_name']);
|
||||
}
|
||||
|
||||
public function editInsurerGet($id = null)
|
||||
{
|
||||
$this->myLogger->logme('error','Edit TPA Onboarding function called');
|
||||
@ -3084,6 +3152,7 @@ class MasterController extends AdminController
|
||||
'files' => WRITEPATH . 'uploads/commission/files',
|
||||
'rules' => WRITEPATH . 'uploads/commission/rules',
|
||||
'claim_sample_forms' => ROOTPATH . 'public/claim_sample_forms/',
|
||||
'insurer_claim_form' => WRITEPATH . 'insurer_claim_form/',
|
||||
'tmp' => ROOTPATH . 'public/tmp/',
|
||||
'bds_dump_excel' => WRITEPATH . 'uploads/bds_dump_excel/',
|
||||
'claims_mis' => WRITEPATH . 'uploads/claims_mis/',
|
||||
@ -3942,204 +4011,10 @@ class MasterController extends AdminController
|
||||
*/
|
||||
public function cronCdLowBalanceAlert()
|
||||
{
|
||||
log_message('error', 'cronCdLowBalanceAlert: started');
|
||||
$response = \App\Helpers\CdLowBalanceAlertHelper::runCron();
|
||||
$statusCode = ($response['status'] ?? false) ? 200 : 500;
|
||||
|
||||
try {
|
||||
$cdMasters = $this->CDMasterModel->getCdMastersWithLowAlertThreshold();
|
||||
log_message('error', 'cronCdLowBalanceAlert: CD masters with alert threshold count = ' . count($cdMasters));
|
||||
|
||||
$lowBalanceAccounts = [];
|
||||
|
||||
foreach ($cdMasters as $cdMaster) {
|
||||
$cdAcPk = (int) ($cdMaster['id'] ?? 0);
|
||||
$threshold = (float) ($cdMaster['cd_balance_low_alert_amount'] ?? 0);
|
||||
|
||||
if ($cdAcPk <= 0 || $threshold <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lastDeposit = $this->clientDepositModel
|
||||
->select('balance')
|
||||
->where('cd_ac_pk', $cdAcPk)
|
||||
->where('is_active', 1)
|
||||
->orderBy('id', 'DESC')
|
||||
->first();
|
||||
|
||||
if (! empty($lastDeposit)) {
|
||||
$currentBalance = (float) ($lastDeposit['balance'] ?? 0);
|
||||
} else {
|
||||
$currentBalance = (float) ($cdMaster['opening_bal'] ?? 0);
|
||||
}
|
||||
|
||||
if ($currentBalance >= $threshold) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lowBalanceAccounts[] = array_merge($cdMaster, [
|
||||
'current_balance' => $currentBalance,
|
||||
'alert_amount' => $threshold,
|
||||
]);
|
||||
}
|
||||
|
||||
if (empty($lowBalanceAccounts)) {
|
||||
$response = [
|
||||
'status' => true,
|
||||
'message' => 'No CD accounts below the configured low-balance alert amount.',
|
||||
'low_balance_count' => 0,
|
||||
'account_manager_mails_sent' => 0,
|
||||
'data' => [],
|
||||
'errors' => [],
|
||||
];
|
||||
log_message('error', 'cronCdLowBalanceAlert: ' . json_encode($response));
|
||||
return $this->respond($response, 200);
|
||||
}
|
||||
|
||||
$accountManagerMailsSent = 0;
|
||||
$mailErrors = [];
|
||||
$data = [];
|
||||
|
||||
foreach ($lowBalanceAccounts as $account) {
|
||||
$result = $this->sendCdLowBalanceAccountManagerAlert($account);
|
||||
$accountManagerMailsSent += (int) ($result['sent'] ?? 0);
|
||||
if (! empty($result['errors'])) {
|
||||
$mailErrors = array_merge($mailErrors, $result['errors']);
|
||||
}
|
||||
if (! empty($result['data'])) {
|
||||
$data = array_merge($data, $result['data']);
|
||||
}
|
||||
}
|
||||
|
||||
$response = [
|
||||
'status' => true,
|
||||
'message' => 'CD low balance alert cron completed.',
|
||||
'low_balance_count' => count($lowBalanceAccounts),
|
||||
'account_manager_mails_sent' => $accountManagerMailsSent,
|
||||
'data' => $data,
|
||||
'errors' => $mailErrors,
|
||||
];
|
||||
|
||||
log_message('error', 'cronCdLowBalanceAlert: ' . json_encode($response));
|
||||
return $this->respond($response, 200);
|
||||
} catch (\Throwable $e) {
|
||||
$response = [
|
||||
'status' => false,
|
||||
'message' => 'CD low balance alert cron failed.',
|
||||
'low_balance_count' => 0,
|
||||
'account_manager_mails_sent' => 0,
|
||||
'data' => [],
|
||||
'errors' => [$e->getMessage()],
|
||||
];
|
||||
log_message('error', 'cronCdLowBalanceAlert Exception: ' . $e->getMessage());
|
||||
log_message('error', 'cronCdLowBalanceAlert: ' . json_encode($response));
|
||||
return $this->respond($response, 500);
|
||||
}
|
||||
}
|
||||
|
||||
private function sendCdLowBalanceAccountManagerAlert(array $account): array
|
||||
{
|
||||
$clientId = (int) ($account['client_id'] ?? 0);
|
||||
$cdAcPk = (int) ($account['id'] ?? 0);
|
||||
$cdAcNo = (string) ($account['cd_ac_no'] ?? '');
|
||||
|
||||
if ($clientId <= 0) {
|
||||
log_message('error', 'cronCdLowBalanceAlert ACM mail: skipped, invalid client_id for cd_ac_pk=' . $cdAcPk);
|
||||
return ['sent' => 0, 'errors' => [], 'data' => []];
|
||||
}
|
||||
|
||||
$recipients = $this->getAccountManagerRecipients($clientId);
|
||||
if (empty($recipients)) {
|
||||
$error = "Client {$clientId}: No account manager found.";
|
||||
log_message('error', 'cronCdLowBalanceAlert ACM mail: ' . $error);
|
||||
return ['sent' => 0, 'errors' => [$error], 'data' => []];
|
||||
}
|
||||
|
||||
$sent = 0;
|
||||
$errors = [];
|
||||
$data = [];
|
||||
$subject = 'CD Low Balance Alert - ' . ($account['client_name'] ?? 'Client');
|
||||
|
||||
foreach ($recipients as $recipient) {
|
||||
$acmEmail = (string) ($recipient['email'] ?? '');
|
||||
$acmName = (string) ($recipient['name'] ?? 'Account Manager');
|
||||
$message = $this->buildCdLowBalanceAccountManagerMailContent($account, $acmName);
|
||||
|
||||
$mailResponse = MailHelper::send_email([
|
||||
'mail' => $acmEmail,
|
||||
'subject' => $subject,
|
||||
'message' => $message,
|
||||
'common' => [
|
||||
'mail_type' => 'account_manager_cd_low_balance_alert',
|
||||
'client_id' => $clientId,
|
||||
],
|
||||
]);
|
||||
|
||||
$decoded = is_string($mailResponse) ? json_decode($mailResponse, true) : $mailResponse;
|
||||
$mailStatus = is_array($decoded) && ($decoded['status'] ?? '') === 'success' ? 'success' : 'failed';
|
||||
|
||||
$data[] = [
|
||||
'client_id' => $clientId,
|
||||
'cd_ac_pk' => $cdAcPk,
|
||||
'cd_ac_no' => $cdAcNo,
|
||||
'acm_name' => $acmName,
|
||||
'acm_email' => $acmEmail,
|
||||
'mail_status' => $mailStatus,
|
||||
];
|
||||
|
||||
if ($mailStatus === 'success') {
|
||||
$sent++;
|
||||
} else {
|
||||
$errors[] = "Client {$clientId}: Failed to send alert to {$acmEmail}.";
|
||||
}
|
||||
}
|
||||
|
||||
return ['sent' => $sent, 'errors' => $errors, 'data' => $data];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{name: string, email: string}>
|
||||
*/
|
||||
private function getAccountManagerRecipients(int $clientId): array
|
||||
{
|
||||
$rows = $this->insurerRMModel
|
||||
->select('user_profiles.first_name, user_profiles.email')
|
||||
->join('user_profiles', 'user_profiles.id = client_rm.user_id')
|
||||
->where('client_rm.client_id', $clientId)
|
||||
->where('client_rm.level', 3)
|
||||
->where('client_rm.is_active', 1)
|
||||
->where('user_profiles.is_active', 1)
|
||||
->findAll();
|
||||
|
||||
$recipients = [];
|
||||
$seenEmails = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$email = strtolower(trim((string) ($row['email'] ?? '')));
|
||||
if ($email === '' || isset($seenEmails[$email])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$seenEmails[$email] = true;
|
||||
$recipients[] = [
|
||||
'name' => trim((string) ($row['first_name'] ?? 'Account Manager')) ?: 'Account Manager',
|
||||
'email' => (string) $row['email'],
|
||||
];
|
||||
}
|
||||
|
||||
return $recipients;
|
||||
}
|
||||
|
||||
private function buildCdLowBalanceAccountManagerMailContent(array $account, string $recipientName): string
|
||||
{
|
||||
return '<p>Dear ' . esc($recipientName) . ',</p>'
|
||||
. '<p>The CD account balance for <strong>' . esc((string) ($account['client_name'] ?? '')) . '</strong> has fallen below the configured alert threshold.</p>'
|
||||
. '<table border="1" cellpadding="8" cellspacing="0" style="border-collapse:collapse;">'
|
||||
. '<tr><td><strong>Insurer</strong></td><td>' . esc((string) ($account['insurer_name'] ?? '')) . '</td></tr>'
|
||||
. '<tr><td><strong>Insurer Branch</strong></td><td>' . esc((string) ($account['insurer_branch_name'] ?? '')) . '</td></tr>'
|
||||
. '<tr><td><strong>CD Account No</strong></td><td>' . esc((string) ($account['cd_ac_no'] ?? '')) . '</td></tr>'
|
||||
. '<tr><td><strong>Current Balance</strong></td><td>' . esc(number_format((float) ($account['current_balance'] ?? 0), 2, '.', '')) . '</td></tr>'
|
||||
. '<tr><td><strong>Alert Amount</strong></td><td>' . esc(number_format((float) ($account['alert_amount'] ?? 0), 2, '.', '')) . '</td></tr>'
|
||||
. '</table>'
|
||||
. '<p>Please review the CD account and take the required action.</p>';
|
||||
return $this->respond($response, $statusCode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -3412,18 +3412,19 @@ class TicketController extends BaseController
|
||||
if (!$insurer_data) {
|
||||
$data['message'] = 'The Physical File Not Found';
|
||||
echo view('errors/404', $data);
|
||||
return;
|
||||
}
|
||||
|
||||
$fileName = $insurer_data['short_name'] . '.pdf';
|
||||
$filePath = ROOTPATH . 'public/claim_sample_forms/' . $fileName;
|
||||
$claimFormFile = resolve_insurer_claim_form_file($insurer_data);
|
||||
|
||||
try {
|
||||
if (file_exists($filePath)) {
|
||||
return $this->response->download($filePath, null);
|
||||
} else {
|
||||
$data['message'] = 'The Physical File Not Found';
|
||||
echo view('errors/404', $data);
|
||||
if (!empty($claimFormFile['available']) && !empty($claimFormFile['file_path'])) {
|
||||
return $this->response->download($claimFormFile['file_path'], null)
|
||||
->setFileName($claimFormFile['file_name']);
|
||||
}
|
||||
|
||||
$data['message'] = 'The Physical File Not Found';
|
||||
echo view('errors/404', $data);
|
||||
} catch (\Exception $e) {
|
||||
$this->myLogger->logme('error', $e->getMessage());
|
||||
return $this->response->setStatusCode(500)->setBody('An error occurred while downloading the file.');
|
||||
|
||||
5
app/Database/insurers_add_claim_form_column.sql
Normal file
5
app/Database/insurers_add_claim_form_column.sql
Normal file
@ -0,0 +1,5 @@
|
||||
ALTER TABLE insurers
|
||||
ADD COLUMN insurer_claim_form VARCHAR(255) NULL DEFAULT NULL AFTER insurer_logo;
|
||||
|
||||
ALTER TABLE insurers
|
||||
ADD COLUMN insurer_claim_form_original_name VARCHAR(255) NULL DEFAULT NULL AFTER insurer_claim_form;
|
||||
274
app/Helpers/CdLowBalanceAlertHelper.php
Normal file
274
app/Helpers/CdLowBalanceAlertHelper.php
Normal file
@ -0,0 +1,274 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
use App\Models\CDMasterModel;
|
||||
use App\Models\ClientDepositModel;
|
||||
use App\Models\ClientRMModel;
|
||||
|
||||
class CdLowBalanceAlertHelper
|
||||
{
|
||||
/**
|
||||
* Check one CD account after a balance reduction and notify account managers if below threshold.
|
||||
*/
|
||||
public static function triggerAfterBalanceReduction(?int $cdAcPk, float $currentBalance): void
|
||||
{
|
||||
if ($cdAcPk === null || $cdAcPk <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$account = self::buildLowBalanceAccount($cdAcPk, $currentBalance);
|
||||
if ($account === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$result = self::sendAccountManagerAlert($account);
|
||||
log_message('error', 'CdLowBalanceAlertHelper triggerAfterBalanceReduction cd_ac_pk=' . $cdAcPk . ': ' . json_encode($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cron entry: scan all CD masters with alert threshold configured.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function runCron(): array
|
||||
{
|
||||
log_message('error', 'cronCdLowBalanceAlert: started');
|
||||
|
||||
try {
|
||||
$cdMasterModel = new CDMasterModel();
|
||||
$cdMasters = $cdMasterModel->getCdMastersWithLowAlertThreshold();
|
||||
log_message('error', 'cronCdLowBalanceAlert: CD masters with alert threshold count = ' . count($cdMasters));
|
||||
|
||||
$depositModel = new ClientDepositModel();
|
||||
$lowBalanceAccounts = [];
|
||||
|
||||
foreach ($cdMasters as $cdMaster) {
|
||||
$cdAcPk = (int) ($cdMaster['id'] ?? 0);
|
||||
$threshold = (float) ($cdMaster['cd_balance_low_alert_amount'] ?? 0);
|
||||
|
||||
if ($cdAcPk <= 0 || $threshold <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$currentBalance = self::resolveCurrentBalance($depositModel, $cdAcPk, (float) ($cdMaster['opening_bal'] ?? 0));
|
||||
if ($currentBalance >= $threshold) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lowBalanceAccounts[] = array_merge($cdMaster, [
|
||||
'current_balance' => $currentBalance,
|
||||
'alert_amount' => $threshold,
|
||||
]);
|
||||
}
|
||||
|
||||
if (empty($lowBalanceAccounts)) {
|
||||
$response = [
|
||||
'status' => true,
|
||||
'message' => 'No CD accounts below the configured low-balance alert amount.',
|
||||
'low_balance_count' => 0,
|
||||
'account_manager_mails_sent' => 0,
|
||||
'data' => [],
|
||||
'errors' => [],
|
||||
];
|
||||
log_message('error', 'cronCdLowBalanceAlert: ' . json_encode($response));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$accountManagerMailsSent = 0;
|
||||
$mailErrors = [];
|
||||
$data = [];
|
||||
|
||||
foreach ($lowBalanceAccounts as $account) {
|
||||
$result = self::sendAccountManagerAlert($account);
|
||||
$accountManagerMailsSent += (int) ($result['sent'] ?? 0);
|
||||
if (! empty($result['errors'])) {
|
||||
$mailErrors = array_merge($mailErrors, $result['errors']);
|
||||
}
|
||||
if (! empty($result['data'])) {
|
||||
$data = array_merge($data, $result['data']);
|
||||
}
|
||||
}
|
||||
|
||||
$response = [
|
||||
'status' => true,
|
||||
'message' => 'CD low balance alert cron completed.',
|
||||
'low_balance_count' => count($lowBalanceAccounts),
|
||||
'account_manager_mails_sent' => $accountManagerMailsSent,
|
||||
'data' => $data,
|
||||
'errors' => $mailErrors,
|
||||
];
|
||||
log_message('error', 'cronCdLowBalanceAlert: ' . json_encode($response));
|
||||
|
||||
return $response;
|
||||
} catch (\Throwable $e) {
|
||||
$response = [
|
||||
'status' => false,
|
||||
'message' => 'CD low balance alert cron failed.',
|
||||
'low_balance_count' => 0,
|
||||
'account_manager_mails_sent' => 0,
|
||||
'data' => [],
|
||||
'errors' => [$e->getMessage()],
|
||||
];
|
||||
log_message('error', 'cronCdLowBalanceAlert Exception: ' . $e->getMessage());
|
||||
log_message('error', 'cronCdLowBalanceAlert: ' . json_encode($response));
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private static function buildLowBalanceAccount(int $cdAcPk, float $currentBalance): ?array
|
||||
{
|
||||
$cdMasterModel = new CDMasterModel();
|
||||
$cdMaster = $cdMasterModel->getCdMasterWithLowAlertThresholdById($cdAcPk);
|
||||
if ($cdMaster === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$threshold = (float) ($cdMaster['cd_balance_low_alert_amount'] ?? 0);
|
||||
if ($threshold <= 0 || $currentBalance >= $threshold) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_merge($cdMaster, [
|
||||
'current_balance' => $currentBalance,
|
||||
'alert_amount' => $threshold,
|
||||
]);
|
||||
}
|
||||
|
||||
private static function resolveCurrentBalance(ClientDepositModel $depositModel, int $cdAcPk, float $openingBalance): float
|
||||
{
|
||||
$lastDeposit = $depositModel
|
||||
->select('balance')
|
||||
->where('cd_ac_pk', $cdAcPk)
|
||||
->where('is_active', 1)
|
||||
->orderBy('id', 'DESC')
|
||||
->first();
|
||||
|
||||
if (! empty($lastDeposit)) {
|
||||
return (float) ($lastDeposit['balance'] ?? 0);
|
||||
}
|
||||
|
||||
return $openingBalance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $account
|
||||
* @return array{sent: int, errors: array<int, string>, data: array<int, array<string, mixed>>}
|
||||
*/
|
||||
private static function sendAccountManagerAlert(array $account): array
|
||||
{
|
||||
$clientId = (int) ($account['client_id'] ?? 0);
|
||||
$cdAcPk = (int) ($account['id'] ?? 0);
|
||||
$cdAcNo = (string) ($account['cd_ac_no'] ?? '');
|
||||
|
||||
if ($clientId <= 0) {
|
||||
log_message('error', 'cronCdLowBalanceAlert ACM mail: skipped, invalid client_id for cd_ac_pk=' . $cdAcPk);
|
||||
|
||||
return ['sent' => 0, 'errors' => [], 'data' => []];
|
||||
}
|
||||
|
||||
$recipients = self::getAccountManagerRecipients($clientId);
|
||||
if (empty($recipients)) {
|
||||
$error = "Client {$clientId}: No account manager found.";
|
||||
log_message('error', 'cronCdLowBalanceAlert ACM mail: ' . $error);
|
||||
|
||||
return ['sent' => 0, 'errors' => [$error], 'data' => []];
|
||||
}
|
||||
|
||||
$sent = 0;
|
||||
$errors = [];
|
||||
$data = [];
|
||||
$subject = 'CD Low Balance Alert - ' . ($account['client_name'] ?? 'Client');
|
||||
|
||||
foreach ($recipients as $recipient) {
|
||||
$acmEmail = (string) ($recipient['email'] ?? '');
|
||||
$acmName = (string) ($recipient['name'] ?? 'Account Manager');
|
||||
$message = self::buildAccountManagerMailContent($account, $acmName);
|
||||
|
||||
$mailResponse = MailHelper::send_email([
|
||||
'mail' => $acmEmail,
|
||||
'subject' => $subject,
|
||||
'message' => $message,
|
||||
'common' => [
|
||||
'mail_type' => 'account_manager_cd_low_balance_alert',
|
||||
'client_id' => $clientId,
|
||||
],
|
||||
]);
|
||||
|
||||
$decoded = is_string($mailResponse) ? json_decode($mailResponse, true) : $mailResponse;
|
||||
$mailStatus = is_array($decoded) && ($decoded['status'] ?? '') === 'success' ? 'success' : 'failed';
|
||||
|
||||
$data[] = [
|
||||
'client_id' => $clientId,
|
||||
'cd_ac_pk' => $cdAcPk,
|
||||
'cd_ac_no' => $cdAcNo,
|
||||
'acm_name' => $acmName,
|
||||
'acm_email' => $acmEmail,
|
||||
'mail_status' => $mailStatus,
|
||||
];
|
||||
|
||||
if ($mailStatus === 'success') {
|
||||
$sent++;
|
||||
} else {
|
||||
$errors[] = "Client {$clientId}: Failed to send alert to {$acmEmail}.";
|
||||
}
|
||||
}
|
||||
|
||||
return ['sent' => $sent, 'errors' => $errors, 'data' => $data];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{name: string, email: string}>
|
||||
*/
|
||||
private static function getAccountManagerRecipients(int $clientId): array
|
||||
{
|
||||
$rows = (new ClientRMModel())
|
||||
->select('user_profiles.first_name, user_profiles.email')
|
||||
->join('user_profiles', 'user_profiles.id = client_rm.user_id')
|
||||
->where('client_rm.client_id', $clientId)
|
||||
->where('client_rm.level', 3)
|
||||
->where('client_rm.is_active', 1)
|
||||
->where('user_profiles.is_active', 1)
|
||||
->findAll();
|
||||
|
||||
$recipients = [];
|
||||
$seenEmails = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$email = strtolower(trim((string) ($row['email'] ?? '')));
|
||||
if ($email === '' || isset($seenEmails[$email])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$seenEmails[$email] = true;
|
||||
$recipients[] = [
|
||||
'name' => trim((string) ($row['first_name'] ?? 'Account Manager')) ?: 'Account Manager',
|
||||
'email' => (string) $row['email'],
|
||||
];
|
||||
}
|
||||
|
||||
return $recipients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $account
|
||||
*/
|
||||
private static function buildAccountManagerMailContent(array $account, string $recipientName): string
|
||||
{
|
||||
return '<p>Dear ' . esc($recipientName) . ',</p>'
|
||||
. '<p>The CD account balance for <strong>' . esc((string) ($account['client_name'] ?? '')) . '</strong> has fallen below the configured alert threshold.</p>'
|
||||
. '<table border="1" cellpadding="8" cellspacing="0" style="border-collapse:collapse;">'
|
||||
. '<tr><td><strong>Insurer</strong></td><td>' . esc((string) ($account['insurer_name'] ?? '')) . '</td></tr>'
|
||||
. '<tr><td><strong>Insurer Branch</strong></td><td>' . esc((string) ($account['insurer_branch_name'] ?? '')) . '</td></tr>'
|
||||
. '<tr><td><strong>CD Account No</strong></td><td>' . esc((string) ($account['cd_ac_no'] ?? '')) . '</td></tr>'
|
||||
. '<tr><td><strong>Current Balance</strong></td><td>' . esc(number_format((float) ($account['current_balance'] ?? 0), 2, '.', '')) . '</td></tr>'
|
||||
. '<tr><td><strong>Alert Amount</strong></td><td>' . esc(number_format((float) ($account['alert_amount'] ?? 0), 2, '.', '')) . '</td></tr>'
|
||||
. '</table>'
|
||||
. '<p>Please review the CD account and take the required action.</p>';
|
||||
}
|
||||
}
|
||||
@ -76,6 +76,11 @@ class DepositHelper
|
||||
|
||||
// Return the response
|
||||
if ($insertId) {
|
||||
if (($data['transaction_type'] ?? 'Credit') !== 'Credit') {
|
||||
$cdAcPk = self::resolveCdAcPk($data);
|
||||
CdLowBalanceAlertHelper::triggerAfterBalanceReduction($cdAcPk, $newBalance);
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Transaction saved successfully',
|
||||
@ -126,6 +131,24 @@ class DepositHelper
|
||||
|
||||
return $lastBalance;
|
||||
}
|
||||
|
||||
private static function resolveCdAcPk(array $data): ?int
|
||||
{
|
||||
if (! empty($data['cd_ac_pk'])) {
|
||||
return (int) $data['cd_ac_pk'];
|
||||
}
|
||||
|
||||
if (empty($data['cd_ac_no'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cdMaster = (new CDMasterModel())
|
||||
->where('cd_ac_no', $data['cd_ac_no'])
|
||||
->where('is_active', 1)
|
||||
->first();
|
||||
|
||||
return $cdMaster ? (int) $cdMaster['id'] : null;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@ -99,6 +99,92 @@ if (! function_exists('file_Upload')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('file_Upload_random_name')) {
|
||||
function file_Upload_random_name($fileToUpload, $filepath, array $allowedExtensions = UPLOAD_ALLOWED_EXTENSIONS, string $namePrefix = ''): array
|
||||
{
|
||||
$empty = ['stored_name' => '', 'original_name' => ''];
|
||||
|
||||
if ($fileToUpload === null || ! $fileToUpload->isValid() || $fileToUpload->hasMoved()) {
|
||||
return $empty;
|
||||
}
|
||||
|
||||
if (! validate_upload_extension($fileToUpload, $allowedExtensions)) {
|
||||
return $empty;
|
||||
}
|
||||
|
||||
$originalName = sanitize_upload_filename($fileToUpload->getClientName() ?: $fileToUpload->getName());
|
||||
$ext = strtolower($fileToUpload->getClientExtension() ?: pathinfo($originalName, PATHINFO_EXTENSION));
|
||||
$namePrefix = preg_replace('/[^a-zA-Z0-9\-_]/', '', $namePrefix);
|
||||
$prefix = $namePrefix !== '' ? $namePrefix . '_' : '';
|
||||
$storedName = $prefix . time() . '_' . bin2hex(random_bytes(8)) . ($ext !== '' ? '.' . $ext : '');
|
||||
$fileToUpload->move($filepath, $storedName);
|
||||
|
||||
return ['stored_name' => $storedName, 'original_name' => $originalName];
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('resolve_insurer_claim_form_file')) {
|
||||
function resolve_insurer_claim_form_file(array $insurer): array
|
||||
{
|
||||
$checkedPaths = [];
|
||||
$claimFormDir = WRITEPATH . 'insurer_claim_form/';
|
||||
|
||||
if (!empty($insurer['insurer_claim_form'])) {
|
||||
$storedFileName = basename($insurer['insurer_claim_form']);
|
||||
$uploadedPath = $claimFormDir . $storedFileName;
|
||||
$checkedPaths[] = $uploadedPath;
|
||||
|
||||
if (is_file($uploadedPath)) {
|
||||
return [
|
||||
'available' => true,
|
||||
'file_path' => $uploadedPath,
|
||||
'file_name' => !empty($insurer['insurer_claim_form_original_name'])
|
||||
? basename($insurer['insurer_claim_form_original_name'])
|
||||
: $storedFileName,
|
||||
'source' => 'uploaded',
|
||||
'checked_paths' => $checkedPaths,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$defaultPath = $claimFormDir . INSURER_DEFAULT_CLAIM_FORM_FILE;
|
||||
$checkedPaths[] = $defaultPath;
|
||||
|
||||
if (is_file($defaultPath)) {
|
||||
return [
|
||||
'available' => true,
|
||||
'file_path' => $defaultPath,
|
||||
'file_name' => INSURER_DEFAULT_CLAIM_FORM_FILE,
|
||||
'source' => 'irdai_default',
|
||||
'checked_paths' => $checkedPaths,
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($insurer['short_name'])) {
|
||||
$legacyPath = ROOTPATH . 'public/claim_sample_forms/' . $insurer['short_name'] . '.pdf';
|
||||
$checkedPaths[] = $legacyPath;
|
||||
|
||||
if (is_file($legacyPath)) {
|
||||
return [
|
||||
'available' => true,
|
||||
'file_path' => $legacyPath,
|
||||
'file_name' => $insurer['short_name'] . '.pdf',
|
||||
'source' => 'legacy',
|
||||
'checked_paths' => $checkedPaths,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'available' => false,
|
||||
'file_path' => null,
|
||||
'file_name' => null,
|
||||
'source' => null,
|
||||
'checked_paths' => $checkedPaths,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('file_Upload_for_lead')) {
|
||||
function file_Upload_for_lead($fileToUpload, $filepath, array $allowedExtensions = UPLOAD_ALLOWED_EXTENSIONS)
|
||||
{
|
||||
|
||||
@ -118,6 +118,34 @@ class CDMasterModel extends Model
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function getCdMastersWithLowAlertThreshold(): array
|
||||
{
|
||||
return $this->buildCdMastersWithLowAlertThresholdQuery()
|
||||
->orderBy('cd_master.id', 'desc')
|
||||
->get()
|
||||
->getResultArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function getCdMasterWithLowAlertThresholdById(int $cdAcPk): ?array
|
||||
{
|
||||
if ($cdAcPk <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$row = $this->buildCdMastersWithLowAlertThresholdQuery()
|
||||
->where('cd_master.id', $cdAcPk)
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \CodeIgniter\Database\BaseBuilder
|
||||
*/
|
||||
private function buildCdMastersWithLowAlertThresholdQuery()
|
||||
{
|
||||
return $this->db->table('cd_master')
|
||||
->select('cd_master.*, clients.client_name, clients.short_name, insurers.name AS insurer_name, insurer_branch.branch_name AS insurer_branch_name')
|
||||
@ -128,9 +156,6 @@ class CDMasterModel extends Model
|
||||
->where('clients.is_active', 1)
|
||||
->where('insurers.is_active', 1)
|
||||
->where('cd_master.cd_balance_low_alert_amount IS NOT NULL', null, false)
|
||||
->where('cd_master.cd_balance_low_alert_amount >', 0)
|
||||
->orderBy('cd_master.id', 'desc')
|
||||
->get()
|
||||
->getResultArray();
|
||||
->where('cd_master.cd_balance_low_alert_amount >', 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,8 @@ class InsurerModel extends Model
|
||||
"name",
|
||||
"short_name",
|
||||
"insurer_logo",
|
||||
"insurer_claim_form",
|
||||
"insurer_claim_form_original_name",
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"is_active",
|
||||
|
||||
@ -190,7 +190,7 @@ class LeadsModel extends Model
|
||||
public function getLeadForInsertClientList($type = null, $client_id = null, $from = null)
|
||||
{
|
||||
$query = $this->db->table('leads')
|
||||
->select('leads.*, user_profiles.first_name as user_name, policy_type.allocg')
|
||||
->select('leads.*, user_profiles.first_name as user_name, policy_type.allocg, policy_type.policy_type as policy_type_name')
|
||||
->join('user_profiles', 'leads.created_by = user_profiles.id')
|
||||
->join('policy_type', 'leads.policy_type_id = policy_type.id', 'left')
|
||||
->where('leads.is_active', 1)
|
||||
@ -200,13 +200,24 @@ class LeadsModel extends Model
|
||||
$query->where("(leads.is_policy_created = '' OR leads.is_policy_created IS NULL)");
|
||||
|
||||
if ($client_id) {
|
||||
$query->join('clients', 'clients.id = ' . (int) $client_id)
|
||||
->join('client_branch', 'client_branch.client_id = clients.id AND client_branch.is_active = 1')
|
||||
->where('leads.client_name = clients.client_name', null, false)
|
||||
->where('leads.client_short_name = clients.short_name', null, false)
|
||||
->where('leads.branch_name = client_branch.branch_name', null, false)
|
||||
->where('leads.branch_code = client_branch.branch_code', null, false)
|
||||
->groupBy('leads.id');
|
||||
$clientId = (int) $client_id;
|
||||
$query->groupStart()
|
||||
->where('leads.client_id', $clientId)
|
||||
->orWhere(
|
||||
"EXISTS (
|
||||
SELECT 1
|
||||
FROM clients c
|
||||
INNER JOIN client_branch cb ON cb.client_id = c.id AND cb.is_active = 1
|
||||
WHERE c.id = {$clientId}
|
||||
AND leads.client_name = c.client_name
|
||||
AND leads.client_short_name = c.short_name
|
||||
AND leads.branch_name = cb.branch_name
|
||||
AND leads.branch_code = cb.branch_code
|
||||
)",
|
||||
null,
|
||||
false
|
||||
)
|
||||
->groupEnd();
|
||||
}
|
||||
} else {
|
||||
$query->groupStart()
|
||||
@ -214,14 +225,16 @@ class LeadsModel extends Model
|
||||
->orWhere("(leads.is_policy_created = '' OR leads.is_policy_created IS NULL)")
|
||||
->groupEnd();
|
||||
|
||||
if ($type) {
|
||||
$query->whereIn('leads.lead_type', $type);
|
||||
}
|
||||
|
||||
if ($client_id) {
|
||||
$query->where('leads.client_id', $client_id);
|
||||
}
|
||||
}
|
||||
|
||||
if ($type) {
|
||||
$query->whereIn('leads.lead_type', $type);
|
||||
}
|
||||
|
||||
$query->groupStart()
|
||||
->where("policy_type.allocg != 'EB'")
|
||||
->orWhere("(policy_type.allocg = 'EB' AND leads.proposel_data IS NOT NULL AND leads.proposel_data <> '')")
|
||||
|
||||
@ -244,7 +244,7 @@ table.dataTable thead th {
|
||||
<option value="">Select Opportunity</option>
|
||||
<?php if(isset($lead_data)) { ?>
|
||||
<?php foreach ($lead_data as $value) { ?>
|
||||
<option value="<?= $value['id']?>"><?= $value['client_name'] ?> - <?= $value['branch_name'] ?> - <?= $value['user_name'] ?></option>
|
||||
<option value="<?= $value['id']?>"><?= $value['client_name'] ?> - <?= $value['branch_name'] ?> - <?= $value['policy_type_name'] ?? 'N/A' ?> - <?= $value['user_name'] ?></option>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</select>
|
||||
|
||||
@ -396,7 +396,7 @@ input:checked + .slider_blue::before {
|
||||
<option value="">Select Opportunity</option>
|
||||
<?php if(isset($lead_data)) { ?>
|
||||
<?php foreach ($lead_data as $value) { ?>
|
||||
<option value="<?= $value['id']?>" data-clientid="<?= $value['client_id']?>", data-branchid="<?= $value['client_branch_id']?>"><?= $value['client_name'] ?> - <?= $value['branch_name'] ?> - <?= $value['user_name'] ?></option>
|
||||
<option value="<?= $value['id']?>" data-clientid="<?= $value['client_id'] ?>" data-branchid="<?= $value['client_branch_id'] ?>"><?= $value['client_name'] ?> - <?= $value['branch_name'] ?> - <?= $value['policy_type_name'] ?? 'N/A' ?> - <?= $value['user_name'] ?></option>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</select>
|
||||
|
||||
@ -187,6 +187,39 @@ input:checked + .slider:before {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Insurer Claim Form -->
|
||||
<div class="form-group row">
|
||||
<label for="insurer_claim_form" class="col-md-4 col-form-label">Insurer Claim Form</label>
|
||||
<div class="col-md-5">
|
||||
<div class="input-icon">
|
||||
<input type="file" class="form-control" id="insurer_claim_form" name="insurer_claim_form"
|
||||
accept=".pdf,.doc,.docx,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
data-parsley-insurer-claim-max-file-size="5120"
|
||||
data-parsley-insurer-claim-file-extension="pdf,doc,docx"
|
||||
data-parsley-insurer-claim-max-file-size-message="File size should not exceed 5MB."
|
||||
data-parsley-insurer-claim-file-extension-message="Only PDF, DOC, and DOCX files are allowed."
|
||||
data-parsley-errors-container="#claim_form_error_container">
|
||||
<i class="mdi mdi-upload additional-icon"></i>
|
||||
</div>
|
||||
<div id="claim_form_error_container"></div>
|
||||
<small class="text-muted">Allowed: PDF, DOC, DOCX. Max size: 5MB. If no insurer form is uploaded, <?= esc(INSURER_DEFAULT_CLAIM_FORM_FILE) ?> will be used.</small>
|
||||
<?php if (!empty($insurer['id'])): ?>
|
||||
<div class="mt-2">
|
||||
<a href="<?= base_url('master/insurer/download-claim-form/' . $insurer['id']) ?>" class="btn btn-sm btn-outline-primary" target="_blank">
|
||||
<i class="mdi mdi-download"></i> Download Claim Form
|
||||
</a>
|
||||
<small class="text-muted d-block mt-1">
|
||||
<?= !empty($insurer['insurer_claim_form_original_name'])
|
||||
? esc($insurer['insurer_claim_form_original_name'])
|
||||
: (!empty($insurer['insurer_claim_form'])
|
||||
? esc($insurer['insurer_claim_form'])
|
||||
: 'Default: ' . esc(INSURER_DEFAULT_CLAIM_FORM_FILE)) ?>
|
||||
</small>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12 text-right">
|
||||
@ -240,10 +273,47 @@ input:checked + .slider:before {
|
||||
}
|
||||
});
|
||||
|
||||
// Custom Parsley validator for insurer claim form file size
|
||||
window.Parsley.addValidator('insurerClaimMaxFileSize', {
|
||||
validateString: function(value, maxSize, parsleyInstance) {
|
||||
if (!window.FileReader) {
|
||||
return true;
|
||||
}
|
||||
var files = parsleyInstance.$element[0].files;
|
||||
if (files.length == 0) {
|
||||
return true;
|
||||
}
|
||||
return files[0].size <= maxSize * 1024;
|
||||
},
|
||||
requirementType: 'integer',
|
||||
messages: {
|
||||
en: 'This file should not be larger than %s KB.'
|
||||
}
|
||||
});
|
||||
|
||||
// Custom Parsley validator for insurer claim form file extension
|
||||
window.Parsley.addValidator('insurerClaimFileExtension', {
|
||||
validateString: function(value, requirement, parsleyInstance) {
|
||||
var file = parsleyInstance.$element[0].files[0];
|
||||
if (!file) { return true; }
|
||||
var extensions = requirement.split(',').map(function(ext) { return ext.trim().toLowerCase(); });
|
||||
var fileExtension = file.name.split('.').pop().toLowerCase();
|
||||
return extensions.indexOf(fileExtension) !== -1;
|
||||
},
|
||||
requirementType: 'string',
|
||||
messages: {
|
||||
en: 'Allowed extensions are: %s.'
|
||||
}
|
||||
});
|
||||
|
||||
$('#insurer_logo').on('change', function() {
|
||||
PreviewImage();
|
||||
});
|
||||
|
||||
$('#insurer_claim_form').on('change', function() {
|
||||
validateInsurerClaimFormFile(true);
|
||||
});
|
||||
|
||||
$("#insurer_general_form").submit(function(events) {
|
||||
|
||||
events.preventDefault();
|
||||
@ -252,6 +322,10 @@ input:checked + .slider:before {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateInsurerClaimFormFile(true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// var isValid = $('#insurer_General_PrimaryKey').parsley().validate();
|
||||
var isValid = $('#insurer_general_form').parsley().validate();
|
||||
var PrimaryKey = $('#insurer_General_PrimaryKey').val();
|
||||
@ -430,6 +504,45 @@ input:checked + .slider:before {
|
||||
$('#logo_error_container').empty();
|
||||
return true;
|
||||
}
|
||||
|
||||
function resetInsurerClaimFormInput() {
|
||||
var fileInput = document.getElementById("insurer_claim_form");
|
||||
fileInput.value = "";
|
||||
$('#claim_form_error_container').empty();
|
||||
}
|
||||
|
||||
function validateInsurerClaimFormFile(showToastr) {
|
||||
var fileInput = document.getElementById("insurer_claim_form");
|
||||
if (!fileInput.files || !fileInput.files[0]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var file = fileInput.files[0];
|
||||
var fileName = file.name.toLowerCase();
|
||||
var parts = fileName.split('.');
|
||||
var allowedExtensions = /(\.pdf|\.doc|\.docx)$/i;
|
||||
|
||||
if (!allowedExtensions.exec(fileName) || parts.length > 2) {
|
||||
$('#claim_form_error_container').html('<ul class="parsley-errors-list filled"><li class="parsley-required">Only PDF, DOC, and DOCX files are allowed.</li></ul>');
|
||||
if (showToastr) {
|
||||
toastr.warning("Only PDF, DOC, and DOCX files are allowed.", "Invalid file type");
|
||||
}
|
||||
resetInsurerClaimFormInput();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((file.size / 1024) > 5120) {
|
||||
$('#claim_form_error_container').html('<ul class="parsley-errors-list filled"><li class="parsley-required">File size should not exceed 5MB.</li></ul>');
|
||||
if (showToastr) {
|
||||
toastr.warning("File size should not exceed 5MB.", "Invalid file size");
|
||||
}
|
||||
resetInsurerClaimFormInput();
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#claim_form_error_container').empty();
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
196
tests/smoke_cd_low_balance_alert.php
Normal file
196
tests/smoke_cd_low_balance_alert.php
Normal file
@ -0,0 +1,196 @@
|
||||
<?php
|
||||
/**
|
||||
* Smoke test: CD low balance alert on debit (DepositHelper + CdLowBalanceAlertHelper)
|
||||
*
|
||||
* Run:
|
||||
* php tests/smoke_cd_low_balance_alert.php [cd_ac_pk] # dry-run: show state only
|
||||
* php tests/smoke_cd_low_balance_alert.php [cd_ac_pk] --debit 1 # live: post 1 INR test debit + alert check
|
||||
* php tests/smoke_cd_low_balance_alert.php --cron # run cron scan only
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
ob_start();
|
||||
|
||||
define('FCPATH', __DIR__ . '/../public/');
|
||||
chdir(FCPATH);
|
||||
|
||||
require FCPATH . '../app/Config/Paths.php';
|
||||
$paths = new Config\Paths();
|
||||
require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php';
|
||||
require_once SYSTEMPATH . 'Config/DotEnv.php';
|
||||
(new CodeIgniter\Config\DotEnv(ROOTPATH))->load();
|
||||
|
||||
defined('ENVIRONMENT') || define('ENVIRONMENT', env('CI_ENVIRONMENT', 'development'));
|
||||
|
||||
$boot = APPPATH . 'Config/Boot/' . ENVIRONMENT . '.php';
|
||||
if (is_file($boot)) {
|
||||
require_once $boot;
|
||||
}
|
||||
|
||||
use App\Helpers\CdLowBalanceAlertHelper;
|
||||
use App\Helpers\DepositHelper;
|
||||
use App\Models\CDMasterModel;
|
||||
use App\Models\ClientDepositModel;
|
||||
use App\Models\ClientRMModel;
|
||||
|
||||
$pass = 0;
|
||||
$fail = 0;
|
||||
$results = [];
|
||||
|
||||
function ok(string $label, bool $cond, string $detail = ''): void
|
||||
{
|
||||
global $pass, $fail, $results;
|
||||
if ($cond) {
|
||||
$pass++;
|
||||
$results[] = '[PASS] ' . $label . ($detail ? " — {$detail}" : '');
|
||||
} else {
|
||||
$fail++;
|
||||
$results[] = '[FAIL] ' . $label . ($detail ? " — {$detail}" : '');
|
||||
}
|
||||
}
|
||||
|
||||
function argValue(string $flag): ?string
|
||||
{
|
||||
global $argv;
|
||||
$idx = array_search($flag, $argv, true);
|
||||
if ($idx === false || ! isset($argv[$idx + 1])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string) $argv[$idx + 1];
|
||||
}
|
||||
|
||||
function resolveCurrentBalance(ClientDepositModel $depositModel, int $cdAcPk, float $openingBalance): float
|
||||
{
|
||||
$lastDeposit = $depositModel
|
||||
->select('balance')
|
||||
->where('cd_ac_pk', $cdAcPk)
|
||||
->where('is_active', 1)
|
||||
->orderBy('id', 'DESC')
|
||||
->first();
|
||||
|
||||
if (! empty($lastDeposit)) {
|
||||
return (float) ($lastDeposit['balance'] ?? 0);
|
||||
}
|
||||
|
||||
return $openingBalance;
|
||||
}
|
||||
|
||||
$db = db_connect('default');
|
||||
$cdMasterModel = new CDMasterModel();
|
||||
$depositModel = new ClientDepositModel();
|
||||
$runCron = in_array('--cron', $argv, true);
|
||||
$debitAmount = argValue('--debit');
|
||||
$cdAcPkArg = null;
|
||||
|
||||
foreach (array_slice($argv, 1) as $arg) {
|
||||
if (str_starts_with($arg, '--')) {
|
||||
continue;
|
||||
}
|
||||
if (ctype_digit($arg)) {
|
||||
$cdAcPkArg = (int) $arg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
echo PHP_EOL . '=== smoke_cd_low_balance_alert ===' . PHP_EOL;
|
||||
|
||||
if ($runCron) {
|
||||
$response = CdLowBalanceAlertHelper::runCron();
|
||||
ok('cron status', ($response['status'] ?? false) === true, (string) ($response['message'] ?? ''));
|
||||
ok('cron response has data key', array_key_exists('data', $response));
|
||||
echo 'Cron response: ' . json_encode($response, JSON_PRETTY_PRINT) . PHP_EOL;
|
||||
} else {
|
||||
$configured = $cdMasterModel->getCdMastersWithLowAlertThreshold();
|
||||
ok('at least one CD master has alert threshold', count($configured) > 0, 'count=' . count($configured));
|
||||
|
||||
if ($cdAcPkArg === null && ! empty($configured)) {
|
||||
$cdAcPkArg = (int) ($configured[0]['id'] ?? 0);
|
||||
}
|
||||
|
||||
ok('cd_ac_pk resolved', $cdAcPkArg !== null && $cdAcPkArg > 0, 'cd_ac_pk=' . ($cdAcPkArg ?? 'none'));
|
||||
|
||||
if ($cdAcPkArg > 0) {
|
||||
$cdMaster = $cdMasterModel->getCdMasterWithLowAlertThresholdById($cdAcPkArg);
|
||||
ok('CD master found with alert threshold', $cdMaster !== null, 'cd_ac_pk=' . $cdAcPkArg);
|
||||
|
||||
if ($cdMaster !== null) {
|
||||
$threshold = (float) ($cdMaster['cd_balance_low_alert_amount'] ?? 0);
|
||||
$currentBalance = resolveCurrentBalance($depositModel, $cdAcPkArg, (float) ($cdMaster['opening_bal'] ?? 0));
|
||||
$isLow = $currentBalance < $threshold;
|
||||
|
||||
echo PHP_EOL . 'Account snapshot:' . PHP_EOL;
|
||||
echo ' client_id : ' . ($cdMaster['client_id'] ?? '') . ' (' . ($cdMaster['client_name'] ?? '') . ')' . PHP_EOL;
|
||||
echo ' cd_ac_pk : ' . $cdAcPkArg . PHP_EOL;
|
||||
echo ' cd_ac_no : ' . ($cdMaster['cd_ac_no'] ?? '') . PHP_EOL;
|
||||
echo ' balance : ' . number_format($currentBalance, 2) . PHP_EOL;
|
||||
echo ' alert amt : ' . number_format($threshold, 2) . PHP_EOL;
|
||||
echo ' below alert : ' . ($isLow ? 'yes' : 'no') . PHP_EOL;
|
||||
|
||||
$acmRows = (new ClientRMModel())
|
||||
->select('user_profiles.first_name, user_profiles.email')
|
||||
->join('user_profiles', 'user_profiles.id = client_rm.user_id')
|
||||
->where('client_rm.client_id', (int) $cdMaster['client_id'])
|
||||
->where('client_rm.level', 3)
|
||||
->where('client_rm.is_active', 1)
|
||||
->where('user_profiles.is_active', 1)
|
||||
->findAll();
|
||||
ok('account manager exists for client', count($acmRows) > 0, 'count=' . count($acmRows));
|
||||
|
||||
foreach ($acmRows as $row) {
|
||||
echo ' ACM : ' . ($row['first_name'] ?? '') . ' <' . ($row['email'] ?? '') . '>' . PHP_EOL;
|
||||
}
|
||||
|
||||
if ($debitAmount !== null) {
|
||||
$amount = (float) $debitAmount;
|
||||
ok('debit amount > 0', $amount > 0, 'amount=' . $amount);
|
||||
|
||||
$beforeBalance = $currentBalance;
|
||||
$afterBalance = $beforeBalance - $amount;
|
||||
|
||||
echo PHP_EOL . 'Posting test debit of ' . number_format($amount, 2) . ' ...' . PHP_EOL;
|
||||
|
||||
$saveResult = DepositHelper::saveDeposit([
|
||||
'amount' => $amount,
|
||||
'sub_type_id' => 4,
|
||||
'client_id' => (int) $cdMaster['client_id'],
|
||||
'client_policy_id' => 0,
|
||||
'endorsement_no' => 'SMOKE-TEST-' . date('YmdHis'),
|
||||
'cd_ac_no' => (string) ($cdMaster['cd_ac_no'] ?? ''),
|
||||
'insurer_id' => (int) ($cdMaster['insurer_id'] ?? 0),
|
||||
'unit' => 'SMOKE',
|
||||
'description' => 'Smoke test debit for CD low balance alert',
|
||||
'transaction_type' => 'Debit',
|
||||
'updated_by' => 1,
|
||||
'event_name' => 'smoke_test',
|
||||
'is_active' => 1,
|
||||
'cd_ac_pk' => $cdAcPkArg,
|
||||
], 1);
|
||||
|
||||
ok('saveDeposit succeeded', ($saveResult['success'] ?? false) === true, json_encode($saveResult));
|
||||
|
||||
$newBalance = resolveCurrentBalance($depositModel, $cdAcPkArg, (float) ($cdMaster['opening_bal'] ?? 0));
|
||||
ok('balance reduced after debit', $newBalance < $beforeBalance, "before={$beforeBalance}, after={$newBalance}");
|
||||
|
||||
if ($afterBalance < $threshold) {
|
||||
ok('balance now below alert threshold', $newBalance < $threshold);
|
||||
echo 'Check writable/logs/log-' . date('Y-m-d') . '.log for CdLowBalanceAlertHelper triggerAfterBalanceReduction' . PHP_EOL;
|
||||
echo 'ACM mail should be sent if mail is configured.' . PHP_EOL;
|
||||
} else {
|
||||
echo 'Balance still above alert threshold after debit; no mail expected.' . PHP_EOL;
|
||||
echo 'Tip: use a larger --debit amount or lower cd_balance_low_alert_amount for this account.' . PHP_EOL;
|
||||
}
|
||||
} else {
|
||||
echo PHP_EOL . 'Dry run only (no debit posted).' . PHP_EOL;
|
||||
echo 'To test live debit + alert: php tests/smoke_cd_low_balance_alert.php ' . $cdAcPkArg . ' --debit 1' . PHP_EOL;
|
||||
echo 'To test cron scan: php tests/smoke_cd_low_balance_alert.php --cron' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo PHP_EOL . implode(PHP_EOL, $results) . PHP_EOL;
|
||||
echo PHP_EOL . "Summary: {$pass} passed, {$fail} failed" . PHP_EOL;
|
||||
|
||||
exit($fail > 0 ? 1 : 0);
|
||||
185
tests/smoke_get_insurer_claim_form_download_url.php
Normal file
185
tests/smoke_get_insurer_claim_form_download_url.php
Normal file
@ -0,0 +1,185 @@
|
||||
<?php
|
||||
/**
|
||||
* Smoke test: EmployeeRestController::getInsurerClaimFormDownloadUrl
|
||||
* Run: php tests/smoke_get_insurer_claim_form_download_url.php [client_policy_id]
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
ob_start();
|
||||
|
||||
define('FCPATH', __DIR__ . '/../public/');
|
||||
chdir(FCPATH);
|
||||
|
||||
require FCPATH . '../app/Config/Paths.php';
|
||||
$paths = new Config\Paths();
|
||||
require rtrim($paths->systemDirectory, '\\/ ') . DIRECTORY_SEPARATOR . 'bootstrap.php';
|
||||
require_once SYSTEMPATH . 'Config/DotEnv.php';
|
||||
(new CodeIgniter\Config\DotEnv(ROOTPATH))->load();
|
||||
|
||||
defined('ENVIRONMENT') || define('ENVIRONMENT', env('CI_ENVIRONMENT', 'development'));
|
||||
|
||||
$boot = APPPATH . 'Config/Boot/' . ENVIRONMENT . '.php';
|
||||
if (is_file($boot)) {
|
||||
require_once $boot;
|
||||
}
|
||||
|
||||
use App\Controllers\EmployeeRestController;
|
||||
use CodeIgniter\HTTP\IncomingRequest;
|
||||
use CodeIgniter\HTTP\URI;
|
||||
use Config\Services;
|
||||
|
||||
$pass = 0;
|
||||
$fail = 0;
|
||||
$results = [];
|
||||
|
||||
function ok(string $label, bool $cond, string $detail = ''): void
|
||||
{
|
||||
global $pass, $fail, $results;
|
||||
if ($cond) {
|
||||
$pass++;
|
||||
$results[] = '[PASS] ' . $label . ($detail ? " — {$detail}" : '');
|
||||
} else {
|
||||
$fail++;
|
||||
$results[] = '[FAIL] ' . $label . ($detail ? " — {$detail}" : '');
|
||||
}
|
||||
}
|
||||
|
||||
function invokeApi(EmployeeRestController $controller, array $query): array
|
||||
{
|
||||
$uri = new URI('http://localhost/nhance_v2/employeeRest/getInsurerClaimFormDownloadUrl');
|
||||
if ($query !== []) {
|
||||
$uri->setQuery(http_build_query($query));
|
||||
}
|
||||
|
||||
$request = new IncomingRequest(
|
||||
config('App'),
|
||||
$uri,
|
||||
null,
|
||||
new \CodeIgniter\HTTP\UserAgent()
|
||||
);
|
||||
$request->setGlobal('get', $query);
|
||||
|
||||
$response = Services::response();
|
||||
$logger = Services::logger();
|
||||
$controller->initController($request, $response, $logger);
|
||||
|
||||
$response = $controller->getInsurerClaimFormDownloadUrl();
|
||||
$body = json_decode($response->getBody(), true);
|
||||
|
||||
return is_array($body) ? $body : [];
|
||||
}
|
||||
|
||||
$db = db_connect('default');
|
||||
$controller = new EmployeeRestController();
|
||||
|
||||
$legacyPolicy = $db->query(
|
||||
"SELECT cp.id AS client_policy_id, i.short_name
|
||||
FROM client_policy cp
|
||||
JOIN insurers i ON i.id = cp.insurer_id
|
||||
WHERE cp.is_active = 1
|
||||
AND i.is_active = 1
|
||||
AND i.short_name = 'ICICIPRU'
|
||||
LIMIT 1"
|
||||
)->getRowArray();
|
||||
|
||||
$uploadedPolicy = $db->query(
|
||||
"SELECT cp.id AS client_policy_id, i.short_name, i.insurer_claim_form
|
||||
FROM client_policy cp
|
||||
JOIN insurers i ON i.id = cp.insurer_id
|
||||
WHERE cp.is_active = 1
|
||||
AND i.is_active = 1
|
||||
AND i.insurer_claim_form IS NOT NULL
|
||||
AND i.insurer_claim_form <> ''
|
||||
LIMIT 1"
|
||||
)->getRowArray();
|
||||
|
||||
$inactivePolicy = $db->query(
|
||||
'SELECT id AS client_policy_id FROM client_policy WHERE is_active = 0 LIMIT 1'
|
||||
)->getRowArray();
|
||||
|
||||
$missingParam = invokeApi($controller, []);
|
||||
ok('Missing client_policy_id returns 400', ($missingParam['code'] ?? null) === 400, json_encode($missingParam));
|
||||
|
||||
$invalidParam = invokeApi($controller, ['client_policy_id' => 'abc']);
|
||||
ok('Invalid client_policy_id returns 400', ($invalidParam['code'] ?? null) === 400, json_encode($invalidParam));
|
||||
|
||||
$notFound = invokeApi($controller, ['client_policy_id' => '999999999']);
|
||||
ok('Unknown client_policy_id returns 404', ($notFound['code'] ?? null) === 404, json_encode($notFound));
|
||||
|
||||
if (!empty($inactivePolicy['client_policy_id'])) {
|
||||
$inactive = invokeApi($controller, ['client_policy_id' => (string) $inactivePolicy['client_policy_id']]);
|
||||
ok('Inactive client policy returns 404', ($inactive['code'] ?? null) === 404, json_encode($inactive));
|
||||
}
|
||||
|
||||
if (!empty($legacyPolicy['client_policy_id'])) {
|
||||
$legacyPath = ROOTPATH . 'public/claim_sample_forms/' . $legacyPolicy['short_name'] . '.pdf';
|
||||
$legacy = invokeApi($controller, ['client_policy_id' => (string) $legacyPolicy['client_policy_id']]);
|
||||
$legacyResolved = resolve_insurer_claim_form_file(['short_name' => $legacyPolicy['short_name']]);
|
||||
ok(
|
||||
'No uploaded form falls back to IRDAI default (ICICIPRU policy)',
|
||||
($legacy['status'] ?? false) === true
|
||||
&& ($legacy['code'] ?? null) === 200
|
||||
&& !empty($legacy['data']['download_url'])
|
||||
&& count($legacy['data']) === 1
|
||||
&& ($legacyResolved['source'] ?? '') === 'irdai_default',
|
||||
'legacy_file_exists=' . (is_file($legacyPath) ? 'yes' : 'no') . ' | ' . json_encode($legacy)
|
||||
);
|
||||
}
|
||||
|
||||
$irdaiDefaultPolicy = $db->query(
|
||||
"SELECT cp.id AS client_policy_id, i.short_name
|
||||
FROM client_policy cp
|
||||
JOIN insurers i ON i.id = cp.insurer_id
|
||||
WHERE cp.is_active = 1
|
||||
AND i.is_active = 1
|
||||
AND (i.insurer_claim_form IS NULL OR i.insurer_claim_form = '')
|
||||
AND i.short_name NOT IN ('ICICIPRU', 'DIGITLIF', 'MAXLIFE', 'NIA', 'OIC', 'PIFL', 'RELIANCE')
|
||||
LIMIT 1"
|
||||
)->getRowArray();
|
||||
|
||||
if (!empty($irdaiDefaultPolicy['client_policy_id'])) {
|
||||
$irdai = invokeApi($controller, ['client_policy_id' => (string) $irdaiDefaultPolicy['client_policy_id']]);
|
||||
$irdaiResolved = resolve_insurer_claim_form_file(['short_name' => $irdaiDefaultPolicy['short_name']]);
|
||||
ok(
|
||||
'IRDAI default claim form fallback',
|
||||
($irdai['status'] ?? false) === true
|
||||
&& ($irdai['code'] ?? null) === 200
|
||||
&& !empty($irdai['data']['download_url'])
|
||||
&& count($irdai['data']) === 1
|
||||
&& ($irdaiResolved['source'] ?? '') === 'irdai_default',
|
||||
json_encode($irdai)
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($uploadedPolicy['client_policy_id'])) {
|
||||
$uploadedPath = WRITEPATH . 'insurer_claim_form/' . basename((string) $uploadedPolicy['insurer_claim_form']);
|
||||
$uploaded = invokeApi($controller, ['client_policy_id' => (string) $uploadedPolicy['client_policy_id']]);
|
||||
$uploadedResolved = resolve_insurer_claim_form_file([
|
||||
'insurer_claim_form' => $uploadedPolicy['insurer_claim_form'],
|
||||
'short_name' => $uploadedPolicy['short_name'] ?? '',
|
||||
]);
|
||||
ok(
|
||||
'Uploaded claim form success',
|
||||
($uploaded['status'] ?? false) === true
|
||||
&& ($uploaded['code'] ?? null) === 200
|
||||
&& !empty($uploaded['data']['download_url'])
|
||||
&& count($uploaded['data']) === 1
|
||||
&& ($uploadedResolved['source'] ?? '') === 'uploaded',
|
||||
'file_exists=' . (is_file($uploadedPath) ? 'yes' : 'no') . ' | ' . json_encode($uploaded)
|
||||
);
|
||||
}
|
||||
|
||||
$cliPolicyId = $argv[1] ?? ($legacyPolicy['client_policy_id'] ?? null);
|
||||
if ($cliPolicyId) {
|
||||
$manual = invokeApi($controller, ['client_policy_id' => (string) $cliPolicyId]);
|
||||
echo "\nManual test client_policy_id={$cliPolicyId}\n";
|
||||
echo json_encode($manual, JSON_PRETTY_PRINT) . "\n";
|
||||
}
|
||||
|
||||
echo "\nSmoke test summary: {$pass} passed, {$fail} failed\n";
|
||||
foreach ($results as $line) {
|
||||
echo $line . "\n";
|
||||
}
|
||||
|
||||
exit($fail > 0 ? 1 : 0);
|
||||
BIN
writable/insurer_claim_form/Claim_Form_IRDAI.pdf
Normal file
BIN
writable/insurer_claim_form/Claim_Form_IRDAI.pdf
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user