diff --git a/app/Config/Acl.php b/app/Config/Acl.php index 107c754d..b59905ff 100644 --- a/app/Config/Acl.php +++ b/app/Config/Acl.php @@ -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#' => [ diff --git a/app/Config/Routes.php b/app/Config/Routes.php index a21e6106..7ed357a6 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -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'); }); diff --git a/app/Controllers/Api/NonEbClaimApiController.php b/app/Controllers/Api/NonEbClaimApiController.php index 87347903..0e2355b5 100644 --- a/app/Controllers/Api/NonEbClaimApiController.php +++ b/app/Controllers/Api/NonEbClaimApiController.php @@ -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, diff --git a/app/Controllers/ClientController.php b/app/Controllers/ClientController.php index d1bc5d9b..cf931aa6 100755 --- a/app/Controllers/ClientController.php +++ b/app/Controllers/ClientController.php @@ -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(); diff --git a/app/Controllers/LeadsController.php b/app/Controllers/LeadsController.php index 9a8ec5f1..12f9ed38 100644 --- a/app/Controllers/LeadsController.php +++ b/app/Controllers/LeadsController.php @@ -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) diff --git a/app/Controllers/PolicyTransactionController.php b/app/Controllers/PolicyTransactionController.php index 50e1dd16..da5c0612 100644 --- a/app/Controllers/PolicyTransactionController.php +++ b/app/Controllers/PolicyTransactionController.php @@ -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); diff --git a/app/Controllers/TicketController.php b/app/Controllers/TicketController.php index 44d25abe..49d4e615 100644 --- a/app/Controllers/TicketController.php +++ b/app/Controllers/TicketController.php @@ -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.']], diff --git a/app/Models/LeadsModel.php b/app/Models/LeadsModel.php index 5ed42522..d194375a 100644 --- a/app/Models/LeadsModel.php +++ b/app/Models/LeadsModel.php @@ -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); diff --git a/app/Views/client_policy.php b/app/Views/client_policy.php index e8720c97..b45d5d11 100755 --- a/app/Views/client_policy.php +++ b/app/Views/client_policy.php @@ -357,7 +357,7 @@ input:checked + .slider_blue::before {
- +
diff --git a/app/Views/layout/header.php b/app/Views/layout/header.php index 42012b60..97eef3be 100755 --- a/app/Views/layout/header.php +++ b/app/Views/layout/header.php @@ -2084,16 +2084,13 @@ body[data-sidebar-size="condensed"] .footer {
- - - + +
  • @@ -2119,10 +2122,19 @@ body[data-sidebar-size="condensed"] .footer {
  • - + +
  • @@ -2245,8 +2258,7 @@ body[data-sidebar-size="condensed"] .footer {
  • - - +
  • diff --git a/app/Views/leads_list.php b/app/Views/leads_list.php index 2485c68e..07390580 100644 --- a/app/Views/leads_list.php +++ b/app/Views/leads_list.php @@ -236,7 +236,7 @@ table.dataTable tbody td { RFQ - + QCR @@ -262,7 +262,7 @@ table.dataTable tbody td { RFQ - + QCR diff --git a/dev_logs/2026-04-01.md b/dev_logs/2026-04-01.md new file mode 100644 index 00000000..1d7e53a6 --- /dev/null +++ b/dev_logs/2026-04-01.md @@ -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` diff --git a/dev_logs/non_eb_claim_api.md b/dev_logs/non_eb_claim_api.md new file mode 100644 index 00000000..3f844885 --- /dev/null +++ b/dev_logs/non_eb_claim_api.md @@ -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" +} +``` diff --git a/nonebapidocs.md b/nonebapidocs.md index c161de0b..1c7e99bc 100644 --- a/nonebapidocs.md +++ b/nonebapidocs.md @@ -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 --- -## 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 -document_name = Invoice Copy -file = +ticket_id = 3 +claim_docs[] = +claim_docs[] = +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 = { "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."` | ---