Merge branch 'dev' of bitbucket.org:jubilian/nhance into dev

This commit is contained in:
VENKATESHWARAN 2026-04-03 16:59:09 +05:30
commit f438a51af7
14 changed files with 612 additions and 120 deletions

View File

@ -122,7 +122,7 @@ class Acl
// ===================== POLICY TRANSACTION / BDS =====================
'#^/policy_tranction#' => [
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
],
'#^/dmsSearch#' => [

View File

@ -609,7 +609,7 @@ $routes->group("employeeRest/api/v1", ["filter" => ['ratelimit', 'authJWT']], fu
$routes->post('create', 'Api\NonEbClaimApiController::createClaim');
$routes->post('list', 'Api\NonEbClaimApiController::listClaims');
$routes->get('history/(:num)', 'Api\NonEbClaimApiController::claimHistory/$1');
$routes->post('(:num)/upload-required-doc', 'Api\NonEbClaimApiController::uploadRequiredDoc/$1');
$routes->post('upload-required-doc', 'Api\NonEbClaimApiController::uploadRequiredDoc');
$routes->get('statuses', 'Api\NonEbClaimApiController::listClaimStatuses');
$routes->post('policies', 'Api\NonEbClaimApiController::listPolicies');
});

View File

@ -260,7 +260,7 @@ class NonEbClaimApiController extends BaseController
->first();
if (!$acm) {
$this->myLogger->logme('error', "[NON_EB_API] No ACM found for client_id: {$cp['client_id']}");
$this->myLogger->logme('error', "[NON_EB_API][createClaim] No ACM found for client_id: {$cp['client_id']}");
}
// Auto-set first claim_status_id for this policy type
@ -349,7 +349,7 @@ class NonEbClaimApiController extends BaseController
$this->saveAssets($claim_id, $body);
$this->putHistoryAfterInsert($ticket_data, $claim_id);
$this->myLogger->logme('error', "[NON_EB_API] Claim created. ID: $claim_id, user: {$authUser['id']}");
$this->myLogger->logme('error', "[NON_EB_API][createClaim] Claim created. ID: $claim_id, user: {$authUser['id']}");
return $this->respond([
'status' => true,
@ -439,6 +439,8 @@ class NonEbClaimApiController extends BaseController
$data = $builder->get()->getResultArray();
$this->myLogger->logme('error', "[NON_EB_API][listClaims] user: {$authUser['id']}, page: $page, total: $total");
return $this->respond([
'status' => true,
'code' => 200,
@ -456,18 +458,61 @@ class NonEbClaimApiController extends BaseController
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
}
// Verify claim exists
$claim = $this->nonEbTicketModel
->select('id, client_id, policy_type_id')
->where('id', $claim_id)
->where('is_active', 1)
->first();
// Fetch full claim row with joins (client, insurer, status, policy type, ACM, branch)
$raw = $this->nonEbTicketModel->getTicketDataByTicketID($claim_id);
if (!$claim) {
if (!$raw) {
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Claim not found'], 404);
}
// Fetch only claim_status_id history rows, oldest first
// claims_data — major display fields only
$claims_data = [
'id' => $raw['id'],
'nhance_claim_ref_no' => $raw['nhance_claim_ref_no'],
'claim_number' => $raw['claim_number'],
'policy_no' => $raw['policy_no'],
'policy_type_name' => $raw['policy_type_name'],
'client_name' => $raw['client_name'],
'branch_name' => $raw['branch_name'],
'insurer_name' => $raw['insurer_name'],
'claim_status' => $raw['claim_status'],
'status_display_name' => $raw['status_display_name'],
'insured_contact_name' => $raw['insured_contact_name'],
'insured_contact_number' => $raw['insured_contact_number'],
'insured_contact_email' => $raw['insured_contact_email'],
'nature_of_loss' => $raw['nature_of_loss'],
'loss_location' => $raw['loss_location'],
'loss_date' => $raw['loss_date'],
'loss_description' => $raw['loss_description'],
'loss_estimate' => $raw['loss_estimate'],
'loss_assessed_value' => $raw['loss_assessed_value'],
'settled_amount' => $raw['settled_amount'],
'settlement_utr' => $raw['settlement_utr'],
'intimation_recd_date' => $raw['intimation_recd_date'],
'intimated_to_insurer_date'=> $raw['intimated_to_insurer_date'],
'surveyor_name' => $raw['surveyor_name'],
'surveyor_contact_person' => $raw['surveyor_contact_person'],
'surveyor_contact_number' => $raw['surveyor_contact_number'],
'acm' => $raw['acm'],
'acm_mobile' => $raw['acm_mobile'],
'asset_file' => $raw['asset_file'],
'priority' => $raw['priority'],
'created_at' => $raw['created_at'],
'updated_at' => $raw['updated_at'],
];
// required_docs — decoded separately
$required_docs = json_decode($raw['required_docs'] ?? '{}', true) ?: [];
// claims_files
$claims_files = $this->claimFilesModel
->select('id, doc_name, file_name, mime_type, file_type, docs_for_ir, created_at')
->where('ticket_id', $claim_id)
->where('ticket_type', 2)
->where('is_active', 1)
->findAll();
// ticket_data — claim status history, oldest first
$history_rows = $this->ticketHistoryModel
->select('new_value, created_at')
->where('ticket_id', $claim_id)
@ -476,14 +521,9 @@ class NonEbClaimApiController extends BaseController
->orderBy('created_at', 'ASC')
->findAll();
if (empty($history_rows)) {
return $this->respond(['status' => true, 'code' => 200, 'claim_id' => $claim_id, 'history' => []], 200);
}
// Build display_name map for this policy type — only statuses with a display_name are user-visible
$statuses = $this->claimStatusModel
->select('id, claim_status, display_name')
->where('ticket_type', $claim['policy_type_id'])
->where('ticket_type', $raw['policy_type_id'])
->where('display_name IS NOT NULL')
->where("display_name != ''")
->where('is_active', 1)
@ -491,24 +531,28 @@ class NonEbClaimApiController extends BaseController
$display_map = array_column($statuses, 'display_name', 'id');
// Filter and format — skip statuses with no display_name
$history = [];
$ticket_data = [];
foreach ($history_rows as $row) {
$status_id = (int)$row['new_value'];
$status_id = (int)$row['new_value'];
$display_name = $display_map[$status_id] ?? null;
if (!$display_name) continue;
$history[] = [
$ticket_data[] = [
'status' => $display_name,
'changed_at' => date('d-m-Y h:i A', strtotime($row['created_at'])),
];
}
$this->myLogger->logme('error', "[NON_EB_API][claimHistory] claim_id: $claim_id, steps: " . count($ticket_data) . ", user: {$authUser['id']}");
return $this->respond([
'status' => true,
'code' => 200,
'claim_id' => $claim_id,
'history' => $history,
'status' => true,
'code' => 200,
'claim_id' => $claim_id,
'claims_data' => $claims_data,
'required_docs' => $required_docs,
'claims_files' => $claims_files,
'ticket_data' => $ticket_data,
], 200);
}
@ -530,6 +574,8 @@ class NonEbClaimApiController extends BaseController
->orderBy('id', 'ASC')
->findAll();
$this->myLogger->logme('error', "[NON_EB_API][listClaimStatuses] user: {$authUser['id']}, count: " . count($statuses));
return $this->respond([
'status' => true,
'code' => 200,
@ -597,6 +643,8 @@ class NonEbClaimApiController extends BaseController
$policies = $builder->get()->getResultArray();
$this->myLogger->logme('error', "[NON_EB_API][listPolicies] user: {$authUser['id']}, client_md5: $client_id_md5, branch: $client_branch_id, count: " . count($policies));
return $this->respond([
'status' => true,
'code' => 200,
@ -605,7 +653,61 @@ class NonEbClaimApiController extends BaseController
], 200);
}
public function uploadRequiredDoc(int $claim_id)
public function uploadRequiredDoc()
{
$authUser = $this->getAuthUser();
if (!$authUser) {
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
}
$ticket_id = $this->request->getPost('ticket_id') ?? null;
$get_file_data = $this->request->getFiles('claim_docs') ?? null;
$get_docs_name = $this->request->getPost('claim_doc_names') ?? [];
$required_docs = $this->request->getPost('required_docs') ?? null;
if (is_string($get_docs_name)) {
$decoded = json_decode($get_docs_name, true);
$get_docs_name = json_last_error() === JSON_ERROR_NONE ? $decoded : [];
} elseif (!is_array($get_docs_name)) {
$get_docs_name = [];
}
$file_data = [];
if (!empty($get_file_data)) {
$file_path = WRITEPATH . 'uploads/claim_files/';
$file_data = multi_file_Upload($get_file_data, $file_path, $get_docs_name, UPLOAD_EXT_CLAIM_DOCS);
}
if (!empty($file_data) && !empty($ticket_id)) {
foreach ($file_data as $value) {
$this->claimFilesModel->insert([
'ticket_id' => $ticket_id,
'ticket_type' => 2,
'doc_name' => $value['doc_name'],
'file_name' => $value['file_name'],
'url' => $value['file_path'],
'file_type' => 2,
'mime_type' => getMimeTypeByFileName($value['file_name']),
'docs_for_ir' => 1,
'is_active' => 1,
'created_by' => self::API_SYSTEM_USER_ID,
]);
}
db_connect()->query(
'UPDATE non_eb_ticket_master SET required_docs = ? WHERE id = ?',
[$required_docs, $ticket_id]
);
$this->myLogger->logme('error', "[NON_EB_API][uploadRequiredDoc] Docs uploaded. ticket_id: $ticket_id, count: " . count($file_data) . ", user: {$authUser['id']}");
return $this->respond(['status' => true, 'code' => 200, 'message' => 'Files uploaded successfully'], 200);
}
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Failed to upload the file'], 400);
}
public function uploadRequiredDoc_v1(int $claim_id)
{
$authUser = $this->getAuthUser();
if (!$authUser) {
@ -702,12 +804,15 @@ class NonEbClaimApiController extends BaseController
$db->transComplete();
if (!$db->transStatus()) {
$this->myLogger->logme('error', "[NON_EB_API][uploadRequiredDoc] Transaction failed. claim_id: $claim_id, doc: $document_name, user: {$authUser['id']}");
return $this->respond(['status' => false, 'code' => 500, 'message' => 'Failed to save document. Please try again.'], 500);
}
$file_id = $this->claimFilesModel->insertID();
$download_url = base_url('downloadClaimFile/') . $file_id;
$this->myLogger->logme('error', "[NON_EB_API][uploadRequiredDoc] Doc uploaded. claim_id: $claim_id, doc: $document_name, file: $file_name, user: {$authUser['id']}");
return $this->respond([
'status' => true,
'code' => 200,

View File

@ -649,7 +649,7 @@ class ClientController extends AdminController
$headerData['page_name'] = 'Clients';
$data['entity'] = $this->kycEntityTypeModel->findAll();
$data['kyc_docs'] = $this->kycDocsModel->findAll();
$data['kyc_docs'] = $this->kycDocsModel->where('is_active', 1)->findAll();
$data['police'] = $this->policesModel->findAll();
$data['insurer'] = $this->insurerBranchModel->getInsurerBranchesWithInsurerNames();
$data['tpa'] = $this->tpaBranchModel->getTpaBranchesWithTpaNames();
@ -916,7 +916,7 @@ class ClientController extends AdminController
$editData['insurers'] = $this->insurerModel->where('is_active', 1)->findAll();
$editData['insurer_branch'] = $this->insurerBranchModel->where('is_active', 1)->findAll();
$editData['clients'] = $this->clientModel->where('is_active', 1)->findAll();
$editData['kyc_docs'] = $this->kycDocsModel->findAll();
$editData['kyc_docs'] = $this->kycDocsModel->where('is_active', 1)->findAll();
$editData['policyGridData'] = $this->policyGridModel->findAll();
$editData['policy_types'] = $this->policyTypeModel->findAll();
$editData['policy_type'] = ['1' => 'Base Policy', '2' => 'SI Topup', '3' => 'Dependent Addon'];
@ -931,7 +931,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(2, $id);
$editData['lead_data'] = $this->leadsModel->getLeadForInsertClientList(null, $id);
$clientPoliceData = $this->clientPolicyModel->getClientPolicyByClientId($id);
foreach ($clientPoliceData as $key => $value) {
@ -3761,6 +3761,8 @@ class ClientController extends AdminController
'left'
);
$builder->where('c.id', $client_id);
$builder->where('kd.is_active',1);
$builder->groupBy(['kd.id', 'ck.file_name']);
$query = $builder->get();

View File

@ -4083,9 +4083,16 @@ class LeadsController extends BaseController
private function prepareClientPolicyData($data, $client_id, $branch_id)
{
$proposel_data = json_decode($data['proposel_data'], true);
// print_r($proposel_data); die;
list($insurer_branch_id, $insurer_id) = explode('-', $proposel_data['insurer'], 2);
$proposel_data = json_decode($data['proposel_data'] ?? '', true) ?? [];
if ($data['lead_form_type'] == 1) {
// EB: insurer stored as "{insurer_branch_id}-{insurer_id}" inside proposel_data
list($insurer_branch_id, $insurer_id) = explode('-', $proposel_data['insurer'] ?? '-', 2);
} else {
// Non-EB: insurer IDs are direct columns on the lead row
$insurer_id = $data['insurer_id'] ?? null;
$insurer_branch_id = $data['insurer_branch_id'] ?? null;
}
$client_policy_data = [
'client_id' => $client_id,
@ -4315,7 +4322,7 @@ class LeadsController extends BaseController
public function getPlacementJson(array $data): ?string
{
$proposel_data = json_decode($data['proposel_data'], true);
$proposel_data = json_decode($data['proposel_data'] ?? '', true) ?? [];
$QCRData = $this->RFQModel
->where('is_active', 1)

View File

@ -5263,6 +5263,7 @@ class PolicyTransactionController extends BaseController
bds_dump_files.file_name,
bds_dump_files.status,
bds_dump_files.created_at,
bds_dump_files.created_by,
up.first_name as user_name
')
->join('user_profiles as up', 'bds_dump_files.created_by = up.id', 'left')
@ -5270,6 +5271,12 @@ class PolicyTransactionController extends BaseController
->orderBy('bds_dump_files.id', 'desc')
->findAll();
if (in_array(get_role_id(), [2, 3, 4])) {
$data['bds_dump_file_data'] = array_values(array_filter($data['bds_dump_file_data'], function ($file) {
return (int) ($file['created_by'] ?? 0) === (int) get_session_userid();
}));
}
return $this->loadLayout('bds_dump_file_list', $data);
} else {
@ -5313,7 +5320,8 @@ class PolicyTransactionController extends BaseController
$status = 'inprogress';
$insert_data = [
'file_name' => $filename,
'status' => $status
'status' => $status,
'created_by' => get_session_userid()
];
$file_id = $this->bdsDumpModel->insert($insert_data);

View File

@ -1307,7 +1307,13 @@ class TicketController extends BaseController
'approved_date' => ['label' => 'Approved Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Approved Date must be dd/mm/yyyy.']],
'approved_letter' => ['label' => 'Approved Letter','rules' => 'permit_empty','errors' => []],
'approved_description' => ['label' => 'Approved Description','rules' => 'permit_empty','errors' => []],
'utr_details' => ['label' => 'UTR Details','rules' => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'UTR Details contains invalid characters.']],
'utr_details' => [
'label' => 'UTR Details',
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]',
'errors' => [
'regex_match' => 'UTR Details allows only alphanumeric characters, spaces, and / _ -'
]
],
'settled_date' => ['label' => 'Settled Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Settled Date must be dd/mm/yyyy.']],
'settle_letter' => ['label' => 'Settle Letter','rules' => 'permit_empty','errors' => []],
'pay_initiate_date' => ['label' => 'Payment Initiate Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Payment Initiate Date must be dd/mm/yyyy.']],
@ -1418,7 +1424,13 @@ class TicketController extends BaseController
'approved_date' => ['label' => 'Approved Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Approved Date must be dd/mm/yyyy.']],
'approved_letter' => ['label' => 'Approved Letter','rules' => 'permit_empty','errors' => []],
'approved_description' => ['label' => 'Approved Description','rules' => 'permit_empty','errors' => []],
'utr_details' => ['label' => 'UTR Details','rules' => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'UTR Details contains invalid characters.']],
'utr_details' => [
'label' => 'UTR Details',
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]',
'errors' => [
'regex_match' => 'UTR Details allows only alphanumeric characters, spaces, and / _ -'
]
],
'settled_date' => ['label' => 'Settled Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Settled Date must be dd/mm/yyyy.']],
'settle_letter' => ['label' => 'Settle Letter','rules' => 'permit_empty','errors' => []],
'pay_initiate_date' => ['label' => 'Payment Initiate Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Payment Initiate Date must be dd/mm/yyyy.']],
@ -1672,7 +1684,13 @@ class TicketController extends BaseController
'approved_date' => ['label' => 'Approved Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Approved Date must be dd/mm/yyyy.']],
'approved_letter' => ['label' => 'Approved Letter','rules' => 'permit_empty','errors' => []],
'approved_description' => ['label' => 'Approved Description','rules' => 'permit_empty','errors' => []],
'utr_details' => ['label' => 'UTR Details','rules' => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'UTR Details contains invalid characters.']],
'utr_details' => [
'label' => 'UTR Details',
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]',
'errors' => [
'regex_match' => 'UTR Details allows only alphanumeric characters, spaces, and / _ -'
]
],
'settled_date' => ['label' => 'Settled Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Settled Date must be dd/mm/yyyy.']],
'settle_letter' => ['label' => 'Settle Letter','rules' => 'permit_empty','errors' => []],
'pay_initiate_date' => ['label' => 'Payment Initiate Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Payment Initiate Date must be dd/mm/yyyy.']],
@ -1783,7 +1801,13 @@ class TicketController extends BaseController
'approved_date' => ['label' => 'Approved Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Approved Date must be dd/mm/yyyy.']],
'approved_letter' => ['label' => 'Approved Letter','rules' => 'permit_empty','errors' => []],
'approved_description' => ['label' => 'Approved Description','rules' => 'permit_empty','errors' => []],
'utr_details' => ['label' => 'UTR Details','rules' => 'permit_empty|alpha_numeric_punct','errors' => ['alpha_numeric_punct' => 'UTR Details contains invalid characters.']],
'utr_details' => [
'label' => 'UTR Details',
'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9 \/_-]+$/]',
'errors' => [
'regex_match' => 'UTR Details allows only alphanumeric characters, spaces, and / _ -'
]
],
'settled_date' => ['label' => 'Settled Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Settled Date must be dd/mm/yyyy.']],
'settle_letter' => ['label' => 'Settle Letter','rules' => 'permit_empty','errors' => []],
'pay_initiate_date' => ['label' => 'Payment Initiate Date','rules' => 'permit_empty|regex_match[/^\d{2}\/\d{2}\/\d{4}$/]','errors' => ['regex_match' => 'Payment Initiate Date must be dd/mm/yyyy.']],

View File

@ -186,13 +186,19 @@ class LeadsModel extends Model
public function getLeadForInsertClientList($type = null, $client_id = null)
{
$query = $this->db->table('leads')
->select('leads.*, user_profiles.first_name as user_name')
->select('leads.*, user_profiles.first_name as user_name, policy_type.allocg')
->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)
->where('leads.status', 'won')
->where("leads.proposel_data IS NOT NULL AND leads.proposel_data <> ''")
->where("(leads.is_client_created = '' OR leads.is_client_created IS NULL)")
->where("(leads.is_policy_created = '' OR leads.is_policy_created IS NULL)");
->groupStart()
->where("(leads.is_client_created = '' OR leads.is_client_created IS NULL)")
->orWhere("(leads.is_policy_created = '' OR leads.is_policy_created IS NULL)")
->groupEnd()
->groupStart()
->where("policy_type.allocg != 'EB'")
->orWhere("(policy_type.allocg = 'EB' AND leads.proposel_data IS NOT NULL AND leads.proposel_data <> '')")
->groupEnd();
if ($type) {
$query->where('leads.lead_type', $type);

View File

@ -357,7 +357,7 @@ input:checked + .slider_blue::before {
</div>
<div class="form-group text-right m-b-0" id="hide_smbt_btn">
<button type="submit" class="btn btn-primary" onclick="featchClient()">Fetch Client</button>
<button type="submit" class="btn btn-primary" onclick="featchClient()">Fetch Data</button>
</div>
</div>
</div><!-- /.modal-content -->

View File

@ -2084,16 +2084,13 @@ body[data-sidebar-size="condensed"] .footer {
<div class="collapse" id="policyTransactionsSub">
<ul>
<?php if (in_array(get_role_id(), [1, 5]) || (in_array(MANAGEMENT_TEAM_ID, user_team()) || in_array(FINANCE_TEAM_ID, user_team()) || in_array(BUSINESS_TEAM_ID, user_team()) || in_array(POS_TEAM_ID, user_team()))) { ?>
<?php if (in_array(get_role_id(), [1, 2, 3, 4, 5]) || (in_array(MANAGEMENT_TEAM_ID, user_team()) || in_array(FINANCE_TEAM_ID, user_team()) || in_array(BUSINESS_TEAM_ID, user_team()) || in_array(POS_TEAM_ID, user_team()))) { ?>
<li>
<a href="<?= base_url('/policy_tranction/inception/list?view=1') ?>">Policy Add</a>
</li>
<li>
<a href="<?= base_url('/policy_tranction/inception/list') ?>">Policy List</a>
</li>
<!-- <li>
<a href="<?= base_url('/policy_tranction/inception/list2') ?>">Policy 2</a>
</li> -->
<li>
<a href="<?= base_url('/policy_tranction/endorsement/list') ?>">Endorsement</a>
</li>
@ -2108,10 +2105,16 @@ body[data-sidebar-size="condensed"] .footer {
</div>
</li>
<?php if ((get_role_id() == 1 || get_role_id() == 5) || (get_role_id() == 4 && in_array(FINANCE_TEAM_ID, user_team())) || (in_array(MANAGEMENT_TEAM_ID, user_team()) || in_array(FINANCE_TEAM_ID, user_team()) || in_array(BUSINESS_TEAM_ID, user_team()))) { ?>
<?php if (in_array(get_role_id(), [1, 5]) || (get_role_id() == 4 && in_array(FINANCE_TEAM_ID, user_team())) || in_array(MANAGEMENT_TEAM_ID, user_team())) { ?>
<?php if ((in_array(get_role_id(), [1, 5])) || ((in_array(get_role_id(), [2, 3, 4])) && in_array(FINANCE_TEAM_ID, user_team())) || (in_array(MANAGEMENT_TEAM_ID, user_team()) || in_array(FINANCE_TEAM_ID, user_team()) || in_array(BUSINESS_TEAM_ID, user_team()) || in_array(POS_TEAM_ID, user_team()))) { ?>
<?php if (
get_role_id() != 4 // Global Guard: Exclude Role 4
&& (
in_array(get_role_id(), [1, 5]) // Case 1: Role 1 or 5
|| in_array(MANAGEMENT_TEAM_ID, user_team()) // Case 2: Any Role + Management Team
|| in_array(POS_TEAM_ID, user_team()) // Case 3: Any Role + POS Team
)
) { ?>
<li>
<a href="#policyReports" data-toggle="collapse" class="waves-effect">
<i class="ri-file-chart-fill"></i>
@ -2119,10 +2122,19 @@ body[data-sidebar-size="condensed"] .footer {
</a>
<div class="collapse" id="policyReports">
<ul class="nav-third-level">
<li>
<a href="<?= base_url('/policy_tranction/report/list') ?>">BDS</a>
</li>
<?php if (in_array(get_role_id(), [1, 5]) || (get_role_id() == 4 && in_array(FINANCE_TEAM_ID, user_team())) || in_array(MANAGEMENT_TEAM_ID, user_team())) { ?>
<?php if (
in_array(POS_TEAM_ID, user_team()) // Case 4: Role Any + Must be in POS Team (global guard)
&& (
in_array(get_role_id(), [1, 5]) // Case 1: Role 1 or 5
|| (in_array(get_role_id(), [4]) && in_array(FINANCE_TEAM_ID, user_team())) // Case 2: Role 4 + Finance Team
|| in_array(MANAGEMENT_TEAM_ID, user_team()) // Case 3: Role Any + Management Team
)
) { ?>
<li>
<a href="<?= base_url('/policy_tranction/report/list') ?>">BDS</a>
</li>
<?php } ?>
<?php if (in_array(get_role_id(), [1, 5]) || ((in_array(get_role_id(), [2, 3])) && in_array(FINANCE_TEAM_ID, user_team())) || in_array(MANAGEMENT_TEAM_ID, user_team()) || in_array(POS_TEAM_ID, user_team())) { ?>
<li>
<a href="<?= base_url('/policy_tranction/report/report-varience-list') ?>">Variance</a>
</li>
@ -2142,7 +2154,8 @@ body[data-sidebar-size="condensed"] .footer {
</ul>
</div>
</li>
<?php } ?>
<?php if (in_array(get_role_id(), [1, 5]) || in_array(MANAGEMENT_TEAM_ID, user_team()) || in_array(POS_TEAM_ID, user_team())) { ?>
<li>
<a href="#policy_tat_report_type" data-toggle="collapse" class="waves-effect">
<i class="ri-file-chart-fill"></i>
@ -2245,8 +2258,7 @@ body[data-sidebar-size="condensed"] .footer {
</li>
<?php } ?>
<?php if (in_array(get_role_id(), [1, 5]) || (in_array(MANAGEMENT_TEAM_ID, user_team()) || in_array(FINANCE_TEAM_ID, user_team()) || in_array(BUSINESS_TEAM_ID, user_team()))) { ?>
<?php if (in_array(get_role_id(), [1,5])) { ?>
<li>
<a href="<?= base_url('/policy_tranction/inception/list2') ?>">
<i class="ri-barcode-line"></i>

View File

@ -236,7 +236,7 @@ table.dataTable tbody td {
<a href="#" class="dropdown-item btnEdit2 btnRfqSheetList" data-id="<?php echo $row['id']; ?>">
<i class="mdi mdi-note-text mr-2 text-muted font-18 vertical-middle"></i>RFQ
</a>
<?php if (! in_array($row['status'], ['queued', 'rfq_created', 'rfq_sent'])) {?>
<?php if (! in_array($row['status'], ['queued', 'rfq_created'])) {?>
<?php if (in_array(get_role_id(), [1, 5, 2, 3]) || in_array(BUSINESS_SUPPORT_TEAM_ID, user_team())) {?>
<a href="#" class="dropdown-item btnEdit3 btnQcrSheetList" data-id="<?php echo $row['id']; ?>">
<i class="mdi mdi-note-text mr-2 text-muted font-18 vertical-middle"></i>QCR
@ -262,7 +262,7 @@ table.dataTable tbody td {
<a href="<?php echo base_url('/rfq/list/') . $row['id'] . '/' . 1; ?>" class="dropdown-item btnEdit2" data-id="<?php echo $row['id']; ?>">
<i class="mdi mdi-note-text mr-2 text-muted font-18 vertical-middle"></i>RFQ
</a>
<?php if (! in_array($row['status'], ['queued', 'rfq_created', 'rfq_sent'])) {?>
<?php if (! in_array($row['status'], ['queued', 'rfq_created'])) {?>
<?php if (in_array(get_role_id(), [1, 5, 2, 3]) || in_array(BUSINESS_SUPPORT_TEAM_ID, user_team())) {?>
<a href="<?php echo base_url('/rfq/list/') . $row['id'] . '/' . 2; ?>" class="dropdown-item btnEdit3" data-id="<?php echo $row['id']; ?>">
<i class="mdi mdi-note-text mr-2 text-muted font-18 vertical-middle"></i>QCR

73
dev_logs/2026-04-01.md Normal file
View File

@ -0,0 +1,73 @@
# Dev Log — 2026-04-01
## NonEbClaimApiController — Bug Fix, Enhancements & API Docs
---
### 1. Fix: JSON Parse 500 Error on `createClaim`
- **Problem:** `getJSON(true)` throws a `500 HTTPException` (Syntax error) when the request body is multipart/form-data (file upload), not JSON.
- **Fix:** Replaced `getJSON(true)` with `json_decode(getBody()) ?? getPost()` so multipart POST data is read correctly without throwing.
### 2. Fix: JSON Parse 500 Error on `listClaims` and `listPolicies`
- **Problem:** Same `getJSON(true)` issue — any malformed or missing JSON body causes a 500 instead of a graceful 400.
- **Fix:** Wrapped `getJSON(true)` calls in `try/catch (\Throwable)` returning a proper `400 Invalid JSON body` response.
### 3. Add: Logging to all API endpoints
Added `myLogger->logme('error', ...)` calls to all 6 endpoints under `employeeRest/api/v1/non-eb-claim` that previously had no log coverage:
| Endpoint | Log event |
|---|---|
| `createClaim` | No ACM found, claim created with ID + user |
| `listClaims` | User + page + total count |
| `claimHistory` | Claim ID + history steps + user |
| `listClaimStatuses` | User + status count |
| `listPolicies` | User + client_md5 + branch + count |
| `uploadRequiredDoc` | Transaction failure + successful upload with doc/file/user |
All log tags follow the pattern `[NON_EB_API][methodName]` for easy log filtering.
### 4. Enhance: `claimHistory` endpoint — full claim detail response
Previously returned only status history array. Now returns 4 keys:
| Key | Source | Description |
|---|---|---|
| `claims_data` | `non_eb_ticket_master` + joins | Major display fields (name, policy, status, surveyor, etc.) |
| `required_docs` | `non_eb_ticket_master.required_docs` (decoded) | IR documents checklist |
| `claims_files` | `claim_files` table | Uploaded documents for this claim |
| `ticket_data` | `ticket_history` table | Claim status change history (renamed from `history`) |
Also removed early-return on empty history so all 4 keys are always present in the response.
---
### Files Changed
- `app/Controllers/Api/NonEbClaimApiController.php`
### API Docs
- `dev_logs/non_eb_claim_api.md`
---
### 5. Change: `uploadRequiredDoc` — rewritten to mirror `uploadIRDocs`
**Old URL:** `POST /api/v1/non-eb-claim/{claim_id}/upload-required-doc`
**New URL:** `POST /api/v1/non-eb-claim/upload-required-doc`
Old function copied as `uploadRequiredDoc_v1` (preserved for reference).
**What changed:**
- Removed `claim_id` from URL; `ticket_id` now comes from POST body
- Switched from single-file (`file` field) to multi-file (`claim_docs[]` field) using `multi_file_Upload`
- Added `claim_doc_names[]` — index-matched names for uploaded files
- `required_docs` JSON string now sent by client and stored directly to `non_eb_ticket_master.required_docs` (no server-side checklist merging)
- Each inserted `claim_files` row now has `docs_for_ir = 1`
- Removed TPA push (`pushClaimFiles` not called)
- Success response simplified to `{status, code, message}`
### Files Changed
- `app/Controllers/Api/NonEbClaimApiController.php`
- `app/Config/Routes.php`
### API Docs
- `nonebapidocs.md`
- `dev_logs/non_eb_claim_api.md`

View File

@ -0,0 +1,298 @@
# Non-EB Claim API — Documentation
**Base URL:** `/employeeRest/api/v1/non-eb-claim`
**Auth:** Bearer JWT token via `Authorization` header (required on all endpoints)
**Filter:** `ratelimit`, `authJWT`
---
## 1. Create Claim
`POST /create`
**Content-Type:** `multipart/form-data`
### Request Fields
| Field | Type | Required | Notes |
|---|---|---|---|
| `client_policy_id` | int | Yes | Must be active Non-EB or Marine policy |
| `nature_of_loss` | string | Yes | Min 3 chars |
| `loss_location` | string | Yes | |
| `loss_date` | string | Yes | Format: DD-MM-YYYY |
| `loss_description` | string | No | Required if `asset_file` is uploaded |
| `loss_estimate` | numeric | No | |
| `claim_number` | string | No | Alphanumeric, `/`, `_`, `-` only |
| `asset_file` | file | No | Allowed: pdf, xls, xlsx, csv |
| `asset_id_code[]` | array | No | Asset details |
| `serial_no[]` | array | No | |
| `vehicle_no[]` | array | No | |
| `asset_description[]` | array | No | |
### Response — 200
```json
{
"status": true,
"code": 200,
"claim_id": 42,
"asset_file": "asset_20250315_abc123.pdf",
"message": "Non-EB Claim created successfully"
}
```
### Error Responses
| Code | Reason |
|---|---|
| 400 | Validation failed |
| 404 | Client policy not found |
| 409 | Duplicate claim (same client + loss_date + policy_no) |
| 415 | Unsupported asset file type |
| 422 | Policy type not Non-EB/Marine, or no claim status configured |
| 500 | DB insert or file upload failed |
---
## 2. List Claims
`POST /list`
**Content-Type:** `application/json`
### Request Body (all optional)
```json
{
"page": 1,
"per_page": 20,
"client_id": 101,
"insurer_id": 5,
"policy_type_id": 50,
"claim_status_id": 3,
"claim_number": "CLM/2025",
"nhance_claim_ref_no": "NHC/NEB",
"date_type": "created_date",
"start_date": "01-01-2025",
"end_date": "31-03-2025",
"show_closed": false
}
```
> `date_type`: `"created_date"` (default) or `"updated_date"`
> `show_closed`: if falsy, excludes Settled / Closed / Rejected / Withdrawn claims
### Response — 200
```json
{
"status": true,
"code": 200,
"total": 84,
"page": 1,
"per_page": 20,
"data": [
{
"id": 42,
"claim_number": "CLM/INS/2025/1234",
"nhance_claim_ref_no": "NHC/NEB/2025/00042",
"policy_no": "POL/MAR/2025/5678",
"policy_type_id": 50,
"claim_status_id": 3,
"status": "surveyor_assigned",
"status_display": "Surveyor Assigned",
"policy_type_name": "Marine Cargo",
"client_name": "Acme Logistics Pvt Ltd",
"insurer_name": "New India Assurance",
"loss_date": "2025-03-15",
"loss_location": "Chennai Port, Gate 3",
"nature_of_loss": "Fire and explosion damage to cargo",
"loss_estimate": "850000.00",
"insured_contact_name": "Ramesh Kumar",
"insured_contact_number": "9876543210",
"acm_name": "Priya Nair",
"created_date": "16-03-2025",
"updated_date": "20-03-2025"
}
]
}
```
### Error Responses
| Code | Reason |
|---|---|
| 400 | Invalid JSON body |
---
## 3. Claim Detail + History
`GET /history/{claim_id}`
### Response — 200
```json
{
"status": true,
"code": 200,
"claim_id": 42,
"claims_data": {
"id": 42,
"nhance_claim_ref_no": "NHC/NEB/2025/00042",
"claim_number": "CLM/INS/2025/1234",
"policy_no": "POL/MAR/2025/5678",
"policy_type_name": "Marine Cargo",
"client_name": "Acme Logistics Pvt Ltd",
"branch_name": "Chennai Branch",
"insurer_name": "New India Assurance",
"claim_status": "surveyor_assigned",
"status_display_name": "Surveyor Assigned",
"insured_contact_name": "Ramesh Kumar",
"insured_contact_number": "9876543210",
"insured_contact_email": "ramesh@acmelogistics.com",
"nature_of_loss": "Fire and explosion damage to cargo",
"loss_location": "Chennai Port, Gate 3",
"loss_date": "2025-03-15",
"loss_description": "Fire broke out in warehouse bay during unloading",
"loss_estimate": "850000.00",
"loss_assessed_value": "720000.00",
"settled_amount": null,
"settlement_utr": null,
"intimation_recd_date": "2025-03-16",
"intimated_to_insurer_date": "2025-03-17",
"surveyor_name": "Suresh Inspection Services",
"surveyor_contact_person": "Suresh Babu",
"surveyor_contact_number": "9444123456",
"acm": "Priya Nair",
"acm_mobile": "9500011122",
"asset_file": "asset_20250315_abc123.pdf",
"priority": 1,
"created_at": "2025-03-16 10:30:00",
"updated_at": "2025-03-20 14:45:00"
},
"required_docs": {
"is_action_freeze": false,
"docs": [
{ "document_name": "Claim Form", "document_received": true },
{ "document_name": "Survey Report", "document_received": false },
{ "document_name": "Police FIR Copy", "document_received": false }
]
},
"claims_files": [
{
"id": 101,
"doc_name": "Claim Form",
"file_name": "claim_form_42_20250316.pdf",
"mime_type": "application/pdf",
"file_type": 2,
"docs_for_ir": 1,
"created_at": "2025-03-16 11:00:00"
}
],
"ticket_data": [
{ "status": "Claim Registered", "changed_at": "16-03-2025 10:30 AM" },
{ "status": "Intimated to Insurer", "changed_at": "17-03-2025 09:00 AM" },
{ "status": "Surveyor Assigned", "changed_at": "20-03-2025 02:45 PM" }
]
}
```
### Error Responses
| Code | Reason |
|---|---|
| 404 | Claim not found or inactive |
---
## 4. Upload Required IR Documents
`POST /upload-required-doc`
**Content-Type:** `multipart/form-data`
### Request Fields
| Field | Type | Required | Notes |
|---|---|---|---|
| `ticket_id` | integer | Yes | The non-EB claim (ticket) ID |
| `claim_docs[]` | file(s) | Yes | One or more files to upload |
| `claim_doc_names[]` | string[] | Yes | Name for each file — index-matched to `claim_docs[]` |
| `required_docs` | string (JSON) | Yes | Full updated checklist JSON to store (e.g. `{"is_action_freeze":false,"docs":[...]}`) |
### Response — 200
```json
{
"status": true,
"code": 200,
"message": "Files uploaded successfully"
}
```
### Error Responses
| Code | Reason |
|---|---|
| 400 | No files uploaded or `ticket_id` missing |
| 401 | Missing / expired token |
| 500 | File upload or DB failed |
---
## 5. List Claim Statuses
`GET /statuses`
### Response — 200
```json
{
"status": true,
"code": 200,
"data": [
{ "id": 1, "claim_status": "claim_registered", "display_name": "Claim Registered" },
{ "id": 2, "claim_status": "intimated_to_insurer","display_name": "Intimated to Insurer" },
{ "id": 3, "claim_status": "surveyor_assigned", "display_name": "Surveyor Assigned" }
]
}
```
---
## 6. List Policies (Non-EB / Marine)
`POST /policies`
**Content-Type:** `application/json`
### Request Body
```json
{
"client_id": "5058f1af8388634f884bd49b13....",
"client_branch_id": 7
}
```
> `client_id` — MD5 hash of the client's numeric ID
### Response — 200
```json
{
"status": true,
"code": 200,
"total": 3,
"data": [
{
"id": 88,
"policy_no": "POL/MAR/2025/5678",
"policy_type_id": 50,
"policy_type_name": "Marine Cargo",
"insurer_id": 5,
"insurer_name": "New India Assurance",
"insurer_short_name": "NIA",
"policy_start_date": "01-04-2025",
"policy_end_date": "31-03-2026"
}
]
}
```
### Error Responses
| Code | Reason |
|---|---|
| 400 | Missing or invalid `client_id` / `client_branch_id` |
---
## Common Auth Error (all endpoints)
```json
{
"status": false,
"code": 401,
"message": "Unauthorized"
}
```

View File

@ -63,7 +63,7 @@ All responses follow this consistent shape:
| 1 | `POST` | `/api/v1/non-eb-claim/create` | Raise a new Non-EB claim |
| 2 | `POST` | `/api/v1/non-eb-claim/list` | List / search claims |
| 3 | `GET` | `/api/v1/non-eb-claim/history/{claim_id}` | Status timeline of a claim |
| 4 | `POST` | `/api/v1/non-eb-claim/{claim_id}/upload-required-doc` | Upload a required document |
| 4 | `POST` | `/api/v1/non-eb-claim/upload-required-doc` | Upload required IR documents for a claim |
| 5 | `GET` | `/api/v1/non-eb-claim/statuses` | List Non-EB claim statuses |
| 6 | `POST` | `/api/v1/non-eb-claim/policies` | List Non-EB policies by client (MD5) + branch |
@ -319,23 +319,13 @@ Authorization: Bearer <token>
---
## 4. Upload Required Document
## 4. Upload Required IR Documents
**POST** `/api/v1/non-eb-claim/{claim_id}/upload-required-doc`
**POST** `/api/v1/non-eb-claim/upload-required-doc`
### How it works
Each claim has a **required documents checklist** configured by the staff (e.g. "Invoice Copy", "Survey Report"). This endpoint lets the user upload a file against one of those checklist items.
When a document is uploaded successfully, its `document_received` flag in the checklist is set to `true` and the updated checklist is returned so you can refresh the UI.
The `document_name` you send must **exactly match** (case-sensitive) one of the `document_name` values in the checklist. Use the checklist data to drive your UI — display the exact names as options so the user cannot type the wrong value.
### URL Parameter
| Param | Type | Required | Description |
|---|---|---|---|
| `claim_id` | integer | Yes | The claim to upload against |
Uploads one or more IR documents for a claim. Each file is stored in `claim_files` with `docs_for_ir = 1`. The full updated `required_docs` JSON (checklist) sent by the client is written directly to `non_eb_ticket_master.required_docs` — the caller is responsible for reflecting any `document_received` flag changes in the JSON before sending.
### Request
@ -343,37 +333,24 @@ The `document_name` you send must **exactly match** (case-sensitive) one of the
| Field | Required | Type | Notes |
|---|---|---|---|
| `document_name` | Yes | string | Must exactly match a `document_name` in the claim's checklist |
| `file` | Yes | file | Allowed types: pdf, jpg, jpeg, png, doc, docx, xls, xlsx |
### How to get the checklist
The required documents list for a claim is returned when you fetch claim details (or can be shown after claim creation). The structure is:
```json
{
"is_action_freeze": false,
"docs": [
{ "document_name": "Invoice Copy", "document_received": false },
{ "document_name": "Survey Report", "document_received": true },
{ "document_name": "Police FIR Copy","document_received": false }
]
}
```
- `is_action_freeze: true` means the checklist is locked — the upload endpoint will reject new uploads with `423`.
- `document_received: true` means the staff has already received this document. You may still re-upload if needed (previous upload is replaced).
- Show only the document names as upload targets — do not allow freetext entry.
| `ticket_id` | Yes | integer | The non-EB claim (ticket) ID |
| `claim_docs[]` | Yes | file(s) | One or more files; use the same field name for multiple |
| `claim_doc_names[]` | Yes | string[] | Document name for each uploaded file — index-matched to `claim_docs[]` |
| `required_docs` | Yes | string (JSON) | Full checklist JSON to persist (e.g. `{"is_action_freeze":false,"docs":[...]}`) |
### Example Request
```
POST /api/v1/non-eb-claim/123/upload-required-doc
POST /api/v1/non-eb-claim/upload-required-doc
Content-Type: multipart/form-data
Authorization: Bearer <token>
document_name = Invoice Copy
file = <file>
ticket_id = 3
claim_docs[] = <file1>
claim_docs[] = <file2>
claim_doc_names[] = ecard
claim_doc_names[] = Sample Doc 1
required_docs = {"is_action_freeze":false,"docs":[{"document_name":"Sample Doc 1","document_received":true},{"document_name":"Sample Doc 2","document_received":false}]}
```
### Success Response `200`
@ -382,36 +359,16 @@ file = <file>
{
"status": true,
"code": 200,
"message": "Document uploaded successfully",
"claim_id": 123,
"document_name": "Invoice Copy",
"download_url": "https://yourdomain.com/downloadClaimFile/456",
"required_docs": {
"is_action_freeze": false,
"docs": [
{ "document_name": "Invoice Copy", "document_received": true },
{ "document_name": "Survey Report", "document_received": true },
{ "document_name": "Police FIR Copy", "document_received": false }
]
}
"message": "Files uploaded successfully"
}
```
> Use the returned `required_docs` to update the checklist UI immediately without a separate fetch.
### Error Responses
| Code | Scenario | Message |
|---|---|---|
| `400` | `document_name` not sent | `"document_name is required"` |
| `400` | No file sent or invalid file | `"A valid file is required"` |
| `400` | No valid files uploaded or `ticket_id` missing | `"Failed to upload the file"` |
| `401` | Missing / expired token | `"Unauthorized"` |
| `404` | Claim not found | `"Claim not found"` |
| `404` | `document_name` not in checklist | `"Document 'Invoice Copy' not found in required documents list"` |
| `415` | Unsupported file type | `"Unsupported file type: bmp"` |
| `422` | Claim has no checklist configured | `"No required documents checklist configured for this claim"` |
| `423` | Checklist is locked | `"Document checklist is locked for this claim"` |
| `500` | Upload or DB failure | `"File upload failed"` / `"Failed to save document. Please try again."` |
---