myLogger = \Config\Services::mylogger(); set_session_context('Non EB Claim Controller'); $this->nonEbTicketModel = new NonEbTicketMasterModel(); $this->assetModel = new NonEbClaimAssetModel(); $this->claimFilesModel = new ClaimFilesModel(); $this->claimStatus = new TicketClaimStatusModel(); $this->clientModel = new ClientModel(); $this->ticketHistoryModel = new TicketHistoryModel(); $this->ticketMailTemplateModel = new TicketMailTemplateModel(); $this->ticketMessageModel = new TicketMessageModel(); $this->ticketNoteModel = new TicketNoteModel(); $this->userModel = new UserModel(); $this->insurerModel = new InsurerModel(); $this->policyTypeModel = new PolicyTypeModel(); $this->clientPolicyModel = new ClientPolicyModel(); $this->priorityType = [ 1 => 'Low', 2 => 'Medium', 3 => 'High', 4 => 'Urgent', 5 => 'Emergency', 6 => 'Critical' ]; $this->triggerType = [ 1 => 'Trigger 1', 2 => 'Trigger 2', 3 => 'Trigger 3', 4 => 'Trigger 4', 5 => 'Trigger 5', 6 => 'Trigger 6', 7 => 'Trigger 7', 8 => 'Trigger 8', 9 => 'Trigger 9', ]; $this->placeHolders = [ '((ACM))' => 'acm', '((ACM_CONTACT))' => 'acm_mobile', '((INSURED_NAME))' => 'insured_contact_name', '((CORPORATE_NAME))' => 'client_name', '((CLAIM_NO))' => 'claim_number', '((POLICY_TYPE))' => 'policy_type_name', '((NHANCE_REF_NO))' => 'nhance_claim_ref_no', '((LOSS_DATE))' => 'loss_date', '((LOSS_LOCATION))' => 'loss_location', '((NATURE_OF_LOSS))' => 'nature_of_loss', ]; $this->statusSectionVisibility = [ 'Claim Intimation - Insured' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents'], 'Claim Intimation - Insurer' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents','surveyor'], 'Survey Appointment - Awaited' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents','surveyor'], 'Surveyor Appointed' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor'], 'LOR Awaited - Surveyor' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor'], 'Documents Awaited - Insured' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor'], 'Partial Documents Awaited - Insured' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor'], 'Documents Submitted - Awaiting Loss Assessment' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor'], 'Loss Assessment - Discrepancy' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor'], 'Loss Assessment - Consent Awaited - Insured' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor'], 'Consent Agreed' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor', 'settlement'], 'Claim Approved' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor', 'settlement'], 'Claim Settled' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor', 'settlement'], 'Claim Closed' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor', 'settlement'], 'Claim Rejected' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor'], 'Claim Withdrawn' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor'], 'Workshop Pending' => ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents', 'surveyor'], ]; } // ===================== LIST ===================== public function claimList() { $data['policy_types'] = $this->policyTypeModel->whereIn('allocg', ['Non-EB', 'Marine'])->where('is_active', 1)->findAll(); $data['priorityType'] = $this->priorityType; $data['tab_name'] = 'Non-EB Claims'; $data['claim_status'] = $this->claimStatus->select('id,ticket_type,claim_status,display_name')->where('is_active', 1)->findAll(); $data['client_list'] = $this->clientModel->select('id,client_name')->where('is_active', 1)->findAll(); $data['insurer_list'] = $this->insurerModel->where('is_active', 1)->findAll(); $data['date_type'] = ['created_date' => 'Created Date', 'updated_date' => 'Updated Date']; if ($this->request->is('get')) { $data['page_name'] = 'Non-EB Claims'; $data['ticket_data'] = $this->claimSearch(1); return $this->loadLayout('non_eb_claim_search', $data); } else { $data['ticket_data'] = $this->claimSearch(); $html = view('non_eb_claim_list', $data); return $this->respond(['status' => true, 'html' => $html], 200); } } public function claimSearch($action = null) { $db = db_connect(); $rawWhere = []; $query = $db->table('non_eb_ticket_master tm') ->select([ 'tm.id', 'tm.policy_type_id', 'tm.claim_status_id', 'tcs.claim_status AS status', 'tcs.display_name AS status_display', 'tm.claim_number', 'tm.nhance_claim_ref_no', 'c.client_name', 'c.short_name', 'i.name AS insurer_name', 'i.short_name AS insurer_short_name', 'pt.policy_type AS policy_type_name', 'tm.loss_date', 'tm.loss_location', 'tm.nature_of_loss', 'tm.loss_estimate', 'tm.insured_contact_name', 'tm.insured_contact_number', 'tm.insured_contact_email', 'tm.policy_no', 'tm.intimation_recd_date', 'tm.intimated_to_insurer_date', 'tm.surveyor_name', 'tm.loss_assessed_value', 'tm.settled_amount', 'tm.settlement_utr', 'tm.priority', 'tm.acm_id', 'tm.insurer_id', 'tm.client_policy_id', 'DATE_FORMAT(tm.created_at, "%d-%m-%Y") AS ticket_created_date', 'DATE_FORMAT(tm.updated_at, "%d-%m-%Y") AS ticket_updated_date', '(SELECT first_name FROM user_profiles WHERE user_profiles.id = tm.acm_id) AS acm_name', ]) ->join('clients c', 'c.id = tm.client_id AND c.is_active = 1', 'left') ->join('insurers i', 'i.id = tm.insurer_id AND i.is_active = 1', 'left') ->join('ticket_claim_status tcs', 'tcs.id = tm.claim_status_id AND tcs.is_active = 1', 'left') ->join('policy_type pt', 'pt.id = tm.policy_type_id AND pt.is_active = 1', 'left') ->where('tm.is_active', 1) ->orderBy('tm.id', 'DESC'); if ($action == 1) { // Default: open claims (exclude terminal statuses by display_name) $query->whereNotIn('tcs.display_name', ['Claim Settled', 'Claim Closed', 'Claim Rejected', 'Claim Withdrawn']); return $query->get()->getResultArray(); } // Filter mode $search_data = $this->request->getPost(); $where = []; foreach ($search_data as $key => $val) { if ($val != null && $val != '' && $val != 0) { if ($key == 'date_type' && $val === 'created_date') { $startDate = date('Y-m-d 00:00:00', strtotime($search_data['start_date'])); $endDate = date('Y-m-d 23:59:59', strtotime($search_data['end_date'])); $rawWhere[] = "tm.created_at BETWEEN '$startDate' AND '$endDate'"; } elseif ($key == 'date_type' && $val === 'updated_date') { $startDate = date('Y-m-d 00:00:00', strtotime($search_data['start_date'])); $endDate = date('Y-m-d 23:59:59', strtotime($search_data['end_date'])); $rawWhere[] = "tm.updated_at BETWEEN '$startDate' AND '$endDate'"; } elseif (!in_array($key, ['date_type', 'start_date', 'end_date'])) { if ($key == 'policy_type_id') { $where['tm.policy_type_id'] = $val; } elseif ($key == 'claim_status_id') { $where['tm.claim_status_id'] = $val; } elseif ($key == 'client_id') { $where['tm.client_id'] = $val; } elseif ($key == 'insurer_id') { $where['tm.insurer_id'] = $val; } elseif ($key == 'claim_number') { $query->like('tm.claim_number', $val); } elseif ($key == 'nhance_claim_ref_no') { $query->like('tm.nhance_claim_ref_no', $val); } } } } if (!empty($where)) { $query->where($where); } foreach ($rawWhere as $condition) { $query->where($condition, null, false); } return $query->get()->getResultArray(); } // ===================== FORM (NEW) ===================== public function claimForm($policy_type_id) { $data = $this->getFormData($policy_type_id); $data['tab_name'] = 'Non-EB Claims'; $data['page_name'] = 'Non-EB Claims'; return $this->loadLayout('non_eb_claim_form', $data); } protected function getFormData($policy_type_id, $claim_status_id = null) { $data = [ 'policy_type_id' => $policy_type_id, 'priorityType' => $this->priorityType, ]; // Policy type info $data['policy_type_info'] = $this->policyTypeModel->where('id', $policy_type_id)->where('is_active', 1)->first(); $data['policy_type_name'] = $data['policy_type_info']['policy_type'] ?? ''; // ACMs $db = db_connect(); $data['acms'] = $db->table('user_profiles')->select('id, first_name')->where(['is_active' => 1, 'role' => 3])->get()->getResultArray(); // Clients $data['clients'] = $this->clientModel->select('id, client_name')->where('is_active', 1)->findAll(); // Insurers $data['insurer_list'] = $this->insurerModel->where('is_active', 1)->findAll(); // Claim status $data['claim_status'] = $this->getClaimStatusForPolicyType($policy_type_id, $claim_status_id); // Visible sections $current_status = !empty($data['claim_status']) ? $data['claim_status'][0] : null; $data['visible_sections'] = $this->getVisibleSections($current_status, $data['claim_status']); return $data; } public function getClaimStatusForPolicyType($policy_type_id, $claim_status_id = null) { if (empty($claim_status_id)) { // Get the first status for this policy type $firstStatus = $this->claimStatus ->where(['is_active' => 1, 'ticket_type' => $policy_type_id]) ->orderBy('id', 'ASC') ->first(); $claim_status_id = $firstStatus['id'] ?? null; } if (empty($claim_status_id)) return []; $claimStatusData = $this->claimStatus ->where(['is_active' => 1, 'ticket_type' => $policy_type_id, 'id' => $claim_status_id]) ->first(); $filtered = []; if (!empty($claimStatusData)) { $filtered[] = $claimStatusData; $allowedIds = json_decode($claimStatusData['allowed_status'] ?? '[]', true); if (!empty($allowedIds) && is_array($allowedIds)) { $additional = $this->claimStatus ->where(['is_active' => 1, 'ticket_type' => $policy_type_id]) ->whereIn('id', $allowedIds) ->findAll(); $filtered = array_merge($filtered, $additional); } } return $filtered; } public function getVisibleSections($currentStatus, $allStatuses) { $sections = ['policy_account', 'loss_incident', 'intimation', 'insured_contact', 'asset', 'documents']; if (!empty($currentStatus)) { $claimStatus = $currentStatus['claim_status'] ?? ''; if (isset($this->statusSectionVisibility[$claimStatus])) { $sections = $this->statusSectionVisibility[$claimStatus]; } } return array_values($sections); } public function getVisibleSectionsAjax() { $claim_status_id = $this->request->getPost('claim_status_id'); $policy_type_id = $this->request->getPost('policy_type_id'); $statuses = $this->getClaimStatusForPolicyType($policy_type_id, $claim_status_id); // print_rr($statuses);die; $currentStatus = !empty($statuses) ? $statuses[0] : null; $sections = $this->getVisibleSections($currentStatus, $statuses); return $this->respond(['status' => true, 'sections' => $sections], 200); } // ===================== VIEW / EDIT ===================== public function view_claim($claim_id) { $ticket_data = $this->nonEbTicketModel->getTicketDataByTicketID($claim_id); if (empty($ticket_data)) { return redirect()->to(base_url('non-eb-claim/list'))->with('error', 'Claim not found'); } $ticket_data = $this->formatDatesForClaim($ticket_data, 'd/m/Y'); $template_data = $this->nonEbTicketModel->getTemplateDataByTicketID($claim_id); if (!empty($template_data)) { $template_data['mail_content'] = $this->replacePlaceholders($template_data['mail_content'] ?? '', $ticket_data); $template_data['subject'] = $this->replacePlaceholders($template_data['subject'] ?? '', $ticket_data); } $data = $this->getFormData($ticket_data['policy_type_id'], $ticket_data['claim_status_id']); $data['reply_data'] = $template_data; $data['placeHolders'] = $this->placeHolders; $data['message_data'] = $this->getTicketMessage($claim_id); $data['view_ticket_page'] = true; $data['ticket_data'] = $ticket_data; $data['ticket_history'] = $this->claimHistory($claim_id); $data['assets_data'] = $this->getAssets($claim_id); $data['tab_name'] = 'Non-EB Claims'; $data['page_name'] = 'Non-EB Claims'; // Visible sections for current + allowed next statuses $data['visible_sections'] = $this->getVisibleSections( !empty($data['claim_status']) ? $data['claim_status'][0] : null, $data['claim_status'] ); return $this->loadLayout('non_eb_claim_edit', $data); } // ===================== CREATE ===================== public function createClaim() { $rules = $this->getValidationRules(); if (!$this->validate($rules)) { return $this->response->setStatusCode(400)->setJSON([ 'status' => false, 'message' => 'Input validation failed', 'code' => 400, 'errors' => $this->validator->getErrors() ]); } $request_data = $this->request->getPost(); $sanitized_data = sanitizeInputArrayAdvanced($request_data); $ticket_data = $this->formatDatesForClaim($sanitized_data); // Remove fields not in DB unset($ticket_data['remark_mode']); // Remove asset arrays from ticket data $assetFields = ['asset_id_code', 'serial_no', 'vehicle_no', 'asset_description']; foreach ($assetFields as $f) { unset($ticket_data[$f]); } // Handle asset file upload $assetFileName = $this->handleAssetFileUpload(); if ($assetFileName) { $ticket_data['asset_file'] = $assetFileName; // Loss description required when asset file uploaded if (empty(trim($ticket_data['loss_description'] ?? ''))) { return $this->response->setStatusCode(400)->setJSON([ 'status' => false, 'message' => 'Input validation failed', 'code' => 400, 'errors' => ['loss_description' => 'Loss Description is required when uploading an asset file.'] ]); } } // Duplicate check if ($this->checkDuplicateNonEbClaim($ticket_data)) { return $this->respond([ 'status' => false, 'code' => 409, 'message' => 'Duplicate claim found for Client + Loss Date + Policy No combination' ], 200); } $return_value = $this->nonEbTicketModel->insert($ticket_data); if ($return_value) { // Save assets $this->saveAssets($return_value, $request_data); // History $this->putHistoryAfterInsert($ticket_data, $return_value); // Auto mail $mail_responce = $this->sendAutoMailTrigger($return_value); $this->autoMessageInsertBasedOnMailResponse($mail_responce, $return_value); return $this->respond([ 'status' => true, 'ticket_id' => $return_value, 'code' => 200, 'message' => 'Non-EB Claim created successfully', 'mail_responce' => $mail_responce ], 200); } return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to create claim'], 200); } // ===================== UPDATE ===================== public function updateClaim() { $rules = $this->getValidationRules(); if (!$this->validate($rules)) { return $this->response->setStatusCode(400)->setJSON([ 'status' => false, 'message' => 'Input validation failed', 'code' => 400, 'errors' => $this->validator->getErrors() ]); } $request_data = $this->request->getPost(); $sanitized_data = sanitizeInputArrayAdvanced($request_data); $ticket_data = $this->formatDatesForClaim($sanitized_data); $ticket_id = $this->request->getPost('ticket_master_id'); // Handle closure remark append mode $remark_mode = $request_data['remark_mode'] ?? 'overwrite'; unset($ticket_data['remark_mode']); if ($remark_mode === 'append' && !empty($ticket_data['closure_remark'])) { $existing = $this->nonEbTicketModel->select('closure_remark')->where('id', $ticket_id)->first(); $existingRemark = $existing['closure_remark'] ?? ''; if (!empty($existingRemark)) { $ticket_data['closure_remark'] = $existingRemark . "\n---\n" . $ticket_data['closure_remark']; } } // Remove asset arrays foreach (['asset_id_code', 'serial_no', 'vehicle_no', 'asset_description'] as $f) { unset($ticket_data[$f]); } // Handle asset file upload $assetFileName = $this->handleAssetFileUpload(); if ($assetFileName) { $ticket_data['asset_file'] = $assetFileName; // Loss description required when asset file uploaded if (empty(trim($ticket_data['loss_description'] ?? ''))) { return $this->response->setStatusCode(400)->setJSON([ 'status' => false, 'message' => 'Input validation failed', 'code' => 400, 'errors' => ['loss_description' => 'Loss Description is required when uploading an asset file.'] ]); } } $old_ticket_data = $this->nonEbTicketModel->where('id', $ticket_id)->where('is_active', 1)->first(); $ticket_data['last_updated_by'] = 'USER'; $this->myLogger->logme('error', "[UPDATE_NON_EB_CLAIM] Ticket Master ID: {data}", ['data' => $ticket_id]); $return_value = $this->nonEbTicketModel->where('id', $ticket_id)->set($ticket_data)->update(); if ($return_value) { // Save assets $this->saveAssets($ticket_id, $request_data); // Mail on status change $mail_responce = null; if ($old_ticket_data['claim_status_id'] != $ticket_data['claim_status_id']) { $mail_responce = $this->sendAutoMailTrigger($ticket_id); $this->autoMessageInsertBasedOnMailResponse($mail_responce, $ticket_id); } return $this->respond([ 'status' => true, 'code' => 200, 'message' => 'Non-EB Claim updated successfully', 'mail_responce' => $mail_responce ], 200); } return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to update claim'], 200); } // ===================== REMOVE ===================== public function removeClaim() { $ticket_id = $this->request->getGet('ticket_id'); if (!empty($ticket_id)) { $this->nonEbTicketModel->where('id', $ticket_id)->set(['is_active' => 0])->update(); return $this->respond(['status' => true, 'code' => 200, 'message' => 'Claim removed successfully'], 200); } return $this->respond(['status' => false, 'code' => 404, 'message' => 'Failed to remove Claim'], 200); } // ===================== ASSETS ===================== public function saveAssets($claim_id, $post_data) { // Soft delete existing assets $this->assetModel->where('non_eb_ticket_id', $claim_id)->set(['is_active' => 0])->update(); $assetCodes = $post_data['asset_id_code'] ?? []; $serialNos = $post_data['serial_no'] ?? []; $vehicleNos = $post_data['vehicle_no'] ?? []; $descriptions = $post_data['asset_description'] ?? []; if (!is_array($assetCodes)) return; for ($i = 0; $i < count($assetCodes); $i++) { $code = trim($assetCodes[$i] ?? ''); $serial = trim($serialNos[$i] ?? ''); $vehicle = trim($vehicleNos[$i] ?? ''); $desc = trim($descriptions[$i] ?? ''); if (empty($code) && empty($serial) && empty($vehicle) && empty($desc)) continue; $this->assetModel->insert([ 'non_eb_ticket_id' => $claim_id, 'asset_id_code' => $code, 'serial_no' => $serial, 'vehicle_no' => $vehicle, 'asset_description' => $desc, 'is_active' => 1, ]); } } public function getAssets($claim_id) { return $this->assetModel->where('non_eb_ticket_id', $claim_id)->where('is_active', 1)->findAll(); } // ===================== NOTES ===================== public function crudNote($action) { if ($action == 1) { $ticket_master_id = $this->request->getPost('id'); $is_auto_query = $this->request->getPost('is_auto_query') ?? 0; if (!empty($ticket_master_id)) { $data = $this->ticketNoteModel->where('is_active', 1)->where('ticket_id', $ticket_master_id)->where('is_auto_query', $is_auto_query)->first(); return $this->respond(['status' => !empty($data), 'data' => $data ?? []], 200); } } elseif ($action == 2) { $request_data = $this->request->getPost(); $rules = [ 'note' => ['label' => 'Add Notes', 'rules' => 'required|min_length[3]|max_length[1000]', 'errors' => ['required' => 'Note field cannot be empty.', 'min_length' => 'Note too short.', 'max_length' => 'Note cannot exceed 1000 characters.'] ], ]; if (!$this->validate($rules)) { return $this->response->setStatusCode(400)->setJSON(['status' => false, 'message' => 'Validation failed', 'code' => 400, 'errors' => $this->validator->getErrors()]); } $data = sanitizeInputArrayAdvanced($request_data); $status = $this->ticketNoteModel->save($data); $id = isset($data['id']) ? $data['id'] : $this->ticketNoteModel->insertID(); return $this->respond(['status' => (bool)$status, 'id' => $id], 200); } } // ===================== REPLY / MESSAGES ===================== public function saveReply() { $received_data = $this->request->getPost(); $rules = [ 'emp_mail' => ['label' => 'To', 'rules' => 'required|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]', 'errors' => ['required' => 'Email is required.', 'regex_match' => 'Invalid email format.'] ], 'mail_subject' => ['label' => 'Subject', 'rules' => 'required|min_length[5]', 'errors' => ['required' => 'Subject is required.', 'min_length' => 'Subject should be at least 5 characters.'] ], ]; if (!$this->validate($rules)) { return $this->response->setStatusCode(400)->setJSON(['status' => false, 'message' => 'Validation failed', 'code' => 400, 'errors' => $this->validator->getErrors()]); } $received_data['sender'] = 'staff'; $status = $this->ticketMessageModel->insert($received_data); if ($status) { $return = $this->sendReplyMessage($received_data); return $this->respond(['status' => true, 'code' => 200, 'return' => $return], 200); } return $this->respond(['status' => false, 'code' => 404], 404); } public function getTicketMessage($ticket_master_id) { $dataToSend = []; $dataToSend['messages'] = $this->ticketMessageModel ->select('ticket_messages.*, up.first_name as user_name') ->join('user_profiles up', 'up.id = ticket_messages.created_by', 'left') ->where('ticket_messages.is_active', 1) ->where('ticket_messages.ticket_id', $ticket_master_id) ->orderBy('ticket_messages.created_at', 'DESC') ->findAll(); foreach ($dataToSend['messages'] as &$message) { $message['mail_content'] = $this->convertHtmlToText($message['mail_content']); } unset($message); $claim = $this->nonEbTicketModel->select('insured_contact_name')->where('id', $ticket_master_id)->where('is_active', 1)->first(); $dataToSend['insured_name'] = $claim['insured_contact_name'] ?? ''; return $dataToSend; } // ===================== MAIL TEMPLATE ===================== public function mailTemplate() { $data = []; $data['trigger_type'] = $this->triggerType; $data['policy_types'] = $this->policyTypeModel->whereIn('allocg', ['Non-EB', 'Marine'])->where('is_active', 1)->findAll(); $data['placeHolders'] = $this->placeHolders; $data['ticket_data'] = $this->ticketMailTemplateModel->where('is_active', 1)->findAll(); foreach ($data['ticket_data'] as $key => $td) { $tooltip = $this->claimStatus->select('claim_status') ->where('ticket_type', $td['ticket_type']) ->where('trigger_type', $td['trigger_type']) ->where('is_active', 1)->first(); $data['ticket_data'][$key]['tool_tip'] = $tooltip['claim_status'] ?? $td['template_name']; } $data['tab_name'] = 'Non-EB Claims'; $data['page_name'] = 'Non-EB Claims'; return $this->loadLayout('non_eb_claim_mail_template', $data); } public function crudTemplate($action) { if ($action == 1) { $rules = [ 'template_name' => ['rules' => 'required', 'errors' => ['required' => 'Template Name is required']], 'ticket_type' => ['rules' => 'required', 'errors' => ['required' => 'Policy Type is required']], 'trigger_type' => ['rules' => 'required', 'errors' => ['required' => 'Trigger Type is required']], 'subject' => ['rules' => 'required', 'errors' => ['required' => 'Subject is required']], 'mail_content' => ['rules' => 'required', 'errors' => ['required' => 'Mail Content is required']], ]; if (!$this->validate($rules)) { return $this->response->setStatusCode(400)->setJSON(['status' => false, 'message' => 'Validation failed', 'code' => 400, 'errors' => $this->validator->getErrors()]); } $data = sanitizeInputArrayAdvanced($this->request->getPost()); $status = $this->ticketMailTemplateModel->save($data); return $this->respond(['status' => (bool)$status], 200); } elseif ($action == 2) { $table_id = (int)$this->request->getPost('id'); $data = $this->ticketMailTemplateModel->where('id', $table_id)->where('is_active', 1)->first(); if ($data) { $tooltip = $this->claimStatus->select('claim_status') ->where('ticket_type', $data['ticket_type'])->where('trigger_type', $data['trigger_type'])->where('is_active', 1)->first(); $data['tool_tip'] = $tooltip['claim_status'] ?? $data['template_name']; return $this->respond(['status' => true, 'data' => $data], 200); } return $this->respond(['status' => false], 404); } elseif ($action == 3) { $table_id = (int)$this->request->getPost('id'); $status = $this->ticketMailTemplateModel->query("UPDATE ticket_mail_template SET is_active = 0 WHERE id = :table_id:", ['table_id' => $table_id]); return $this->respond(['status' => (bool)$status], 200); } } // ===================== HISTORY ===================== public function claimHistory($ticket_id) { $sql = "SELECT th.id, th.field_name, th.display_name, th.old_value, th.new_value, th.created_at, th.updated_by, CONCAT_WS(' ', creator.first_name, creator.last_name) AS modified_by, old_status.claim_status AS old_status_value, new_status.claim_status AS new_status_value, CONCAT(old_acm.first_name, ' ', old_acm.last_name) AS old_account_manager, CONCAT(new_acm.first_name, ' ', new_acm.last_name) AS new_account_manager FROM ticket_history th LEFT JOIN user_profiles creator ON th.created_by = creator.id LEFT JOIN ticket_claim_status old_status ON th.field_name = 'claim_status_id' AND th.old_value = old_status.id AND old_status.is_active = 1 LEFT JOIN ticket_claim_status new_status ON th.field_name = 'claim_status_id' AND th.new_value = new_status.id LEFT JOIN user_profiles old_acm ON th.field_name = 'acm_id' AND th.old_value = old_acm.id LEFT JOIN user_profiles new_acm ON th.field_name = 'acm_id' AND th.new_value = new_acm.id WHERE th.ticket_id = :ticket_id: AND th.is_active = 1 ORDER BY th.created_at DESC"; $data = $this->ticketHistoryModel->query($sql, ['ticket_id' => (int)$ticket_id])->getResultArray(); $priorityType = $this->priorityType; foreach ($data as &$row) { if ($row['field_name'] == 'claim_status_id') { $row['old_value'] = $row['old_status_value'] ?? '-'; $row['new_value'] = $row['new_status_value'] ?? '-'; } elseif ($row['field_name'] == 'acm_id') { $row['old_value'] = $row['old_account_manager'] ?? '-'; $row['new_value'] = $row['new_account_manager'] ?? '-'; } elseif ($row['field_name'] == 'priority') { $row['old_value'] = $priorityType[$row['old_value']] ?? '-'; $row['new_value'] = $priorityType[$row['new_value']] ?? '-'; } } unset($row); return $data; } public function putHistoryAfterInsert($ticket_data, $ticket_id) { $this->myLogger->logme('error', "Put History After Insert (Non-EB) : $ticket_id"); if (!empty($ticket_data)) { $this->ticketHistoryModel->insert([ 'ticket_id' => $ticket_id, 'field_name' => 'claim_status_id', 'display_name' => 'Claim Created', 'old_value' => null, 'new_value' => $ticket_data['claim_status_id'], 'created_by' => get_session_userid(), 'is_active' => 1 ]); } } // ===================== EMAIL ===================== public function constructMailContent($ticket_id, $status_id = null) { $this->myLogger->logme('error', "Constructing mail content for Non-EB Ticket ID: $ticket_id"); try { $ticket_data = $this->nonEbTicketModel->getTicketDataByTicketID($ticket_id); if (!$ticket_data) return null; $template_data = $this->nonEbTicketModel->getTemplateDataByTicketID($ticket_id, $status_id); if (!$template_data) return null; if ($status_id && isset($template_data['claim_status'])) { $ticket_data['claim_status'] = $template_data['claim_status']; } $subject = $this->replacePlaceholders($template_data['subject'] ?? '', $ticket_data); $message = $this->replacePlaceholders($template_data['mail_content'] ?? '', $ticket_data); if (empty($ticket_data['insured_contact_email']) || empty($subject) || empty($message)) return null; return [ 'mail' => [$ticket_data['insured_contact_email']], 'subject' => $subject, 'message' => $message, 'cc' => $ticket_data['common_mails'] ?? '', 'attachments' => [] ]; } catch (\Exception $e) { $this->myLogger->logme('error', "Exception in constructMailContent (Non-EB): " . $e->getMessage()); return null; } } public function replacePlaceholders($content, $ticket_data) { if (!empty($ticket_data) && !empty($content)) { foreach ($this->placeHolders as $key => $value) { $replaceData = $ticket_data[$value] ?? ''; $content = str_replace($key, $replaceData, $content); } } return $content; } public function sendTrigger($mail_content) { if (!empty($mail_content)) { $mail_content['from_mail'] = 'claims@nhanceindia.in'; return MailHelper::send_email($mail_content); } return ['status' => false, 'code' => 404, 'message' => 'Mail not sent, data empty']; } public function sendReplyMessage($mail_data) { $ticket_data = $this->nonEbTicketModel->getTicketDataByTicketID($mail_data['ticket_id']); if (!$ticket_data) return null; if (!empty($mail_data)) { $mail_data['mail_content'] = $this->replacePlaceholders($mail_data['mail_content'] ?? '', $ticket_data); $mail_data['mail_subject'] = $this->replacePlaceholders($mail_data['mail_subject'] ?? '', $ticket_data); } $emailData = [ 'mail' => [$mail_data['emp_mail']], 'subject' => $mail_data['mail_subject'], 'message' => $mail_data['mail_content'], 'common' => [], 'cc' => $ticket_data['common_mails'] ?? '', 'attachments' => [] ]; return $this->sendTrigger($emailData); } public function testAutoMailTrigger($ticket_id) { $status_id = $this->request->getGet('status_id') ? (int)$this->request->getGet('status_id') : null; $preview = (bool)$this->request->getGet('preview'); if ($preview) { $ticket_data = $this->nonEbTicketModel->getTicketDataByTicketID($ticket_id); $template_data = $this->nonEbTicketModel->getTemplateDataByTicketID($ticket_id, $status_id); if ($status_id && !empty($template_data['claim_status'])) { $ticket_data['claim_status'] = $template_data['claim_status']; } $replaced_subject = $this->replacePlaceholders($template_data['subject'] ?? '', $ticket_data ?? []); $replaced_message = $this->replacePlaceholders($template_data['mail_content'] ?? '', $ticket_data ?? []); return $this->respond([ 'status' => true, 'code' => 200, 'ticket_id' => $ticket_id, 'status_id' => $status_id, 'ticket_data' => $ticket_data, 'template_data' => $template_data, 'replaced_subject' => $replaced_subject, 'replaced_message' => $replaced_message, ]); } $mail_content = $this->constructMailContent($ticket_id, $status_id); $mail_responce = $this->sendTrigger($mail_content); return $this->respond([ 'status' => true, 'code' => 200, 'ticket_id' => $ticket_id, 'status_id' => $status_id, 'mail_responce' => $mail_responce, 'message' => 'testAutoMailTrigger executed', ]); } public function sendAutoMailTrigger($ticket_id) { $auto_mail_enable = $this->nonEbTicketModel->getTemplateDataByTicketID($ticket_id); $mail_responce = []; if (!empty($auto_mail_enable) && ($auto_mail_enable['is_auto_mail'] ?? 0) == 1) { $mail_content = $this->constructMailContent($ticket_id); $mail_responce = $this->sendTrigger($mail_content); $this->myLogger->logme('error', 'Non-EB Auto Mail Sent Successfully'); } return $mail_responce; } public function autoMessageInsertBasedOnMailResponse($mail_sent_status, $ticket_id) { if (gettype($mail_sent_status) == 'array') { // already array } else { $mail_sent_status = json_decode($mail_sent_status); } if (!empty($mail_sent_status) && isset($mail_sent_status->status) && $mail_sent_status->status == 'success') { $sent_message_data = [ 'ticket_id' => $ticket_id, 'sender' => 'staff', 'emp_mail' => isset($mail_sent_status->data->params->mail[0]) ? $mail_sent_status->data->params->mail[0] : ($mail_sent_status->data->params->mail ?? null), 'mail_subject' => $mail_sent_status->data->params->subject, 'mail_content' => $mail_sent_status->data->params->message, ]; $this->ticketMessageModel->insert($sent_message_data); } } // ===================== REPORTS ===================== public function claimReports() { if ($this->request->is('get')) { $default_start = new \DateTime(); $data['default_end'] = $default_start->format('d-m-Y'); $data['default_start'] = $default_start->modify('-60 days')->format('d-m-Y'); $data['policy_types'] = $this->policyTypeModel->whereIn('allocg', ['Non-EB', 'Marine'])->where('is_active', 1)->findAll(); $data['account_manager'] = $this->userModel->select('first_name')->where('is_active', 1)->where('role', 3)->findAll(); $data['tab_name'] = 'Non-EB Claim Reports'; $data['page_name'] = 'Non-EB Claims'; $this->loadLayout('non_eb_claim_reports', $data); } } // ===================== AJAX HELPERS ===================== public function getBranchAndPolicyByClientID() { $client_id = $this->request->getPost('client_id'); if (empty($client_id)) { return $this->respond(['status' => false, 'message' => 'Client ID required'], 200); } $db = db_connect(); // Fetch branches for this client $branch_list = $db->table('client_branch') ->select('id, branch_name') ->where('client_id', $client_id) ->where('is_active', 1) ->get()->getResultArray(); // Fetch Non-EB/Marine policies grouped by branch_id $policy_list = []; foreach ($branch_list as $branch) { $policies = $db->table('client_policy cp') ->select("cp.id, cp.policy_no, DATE(cp.policy_start_date) as policy_start_date, DATE(cp.policy_end_date) as policy_end_date, cp.insurer_id, cp.insurer_branch_id, cp.client_branch_id, pt.policy_type, pt.id as policy_type_id, i.name as insurer_name, i.short_name as insurer_short_name, ib.branch_code as insurer_branch_code", false) ->join('policy_type pt', 'pt.id = cp.policy_type_id AND pt.is_active = 1') ->join('insurers i', 'i.id = cp.insurer_id AND i.is_active = 1', 'left') ->join('insurer_branch ib', 'ib.id = cp.insurer_branch_id AND ib.is_active = 1', 'left') ->where('cp.client_id', $client_id) ->where('cp.client_branch_id', $branch['id']) ->where('cp.is_active', 1) ->whereIn('pt.allocg', ['Non-EB', 'Marine']) ->get()->getResultArray(); $policy_list[$branch['id']] = $policies; } // Fetch client contacts grouped by branch_id $contact_list = []; foreach ($branch_list as $branch) { $contacts = $db->table('level_contacts') ->select('name, email, mobile') ->where('ref_id', $branch['id']) ->where('contact_type', 'client') ->where('is_active', 1) ->get()->getResultArray(); if (!empty($contacts)) { $contact_list[$branch['id']] = $contacts[0]; } } return $this->respond([ 'status' => true, 'branch_data' => $branch_list, 'policy_data' => $policy_list, 'contact_data' => $contact_list ], 200); } public function getMoreInfo($requestFrom = null, $ticket_id = null) { if ($this->request->is('post')) { $ticket_id = $this->request->getPost('ticket_id'); } if (empty($ticket_id)) { return $this->respond(['status' => false, 'message' => 'Ticket ID required'], 200); } $data = $this->nonEbTicketModel->getTicketDataByTicketID($ticket_id); if ($data) { return $this->respond(['status' => true, 'data' => $data], 200); } return $this->respond(['status' => false, 'message' => 'Not found'], 200); } // ===================== UTILITIES ===================== protected function getValidationRules() { return [ 'client_id' => ['rules' => 'required', 'errors' => ['required' => 'Client is required']], 'branch_id' => ['rules' => 'required', 'errors' => ['required' => 'Branch is required']], 'policy_type_id' => ['rules' => 'required', 'errors' => ['required' => 'Policy Type is required']], 'claim_status_id' => ['rules' => 'required', 'errors' => ['required' => 'Status is required']], 'acm_id' => ['rules' => 'required', 'errors' => ['required' => 'Account Manager is required']], 'nature_of_loss' => ['label' => 'Nature of Loss', 'rules' => 'required|min_length[3]', 'errors' => ['required' => 'Nature of Loss is required']], 'loss_date' => ['label' => 'Loss Date', 'rules' => 'required', 'errors' => ['required' => 'Loss Date is required']], 'loss_location' => ['label' => 'Loss Location', 'rules' => 'required', 'errors' => ['required' => 'Loss Location is required']], 'insured_contact_name' => ['label' => 'Contact Name', 'rules' => 'required|min_length[3]|regex_match[/^[a-zA-Z0-9\s_-]+$/]', 'errors' => ['required' => 'Insured Contact Name is required', 'regex_match' => 'Only letters, numbers, spaces, hyphens, underscores allowed.']], 'insured_contact_number' => ['label' => 'Contact Number', 'rules' => 'required|numeric|min_length[10]|max_length[15]', 'errors' => ['required' => 'Contact number is required', 'numeric' => 'Must be numeric']], 'insured_contact_email' => ['label' => 'Contact Email', 'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]', 'errors' => ['regex_match' => 'Invalid email format']], 'policy_no' => ['label' => 'Policy Number', 'rules' => 'permit_empty|max_length[100]'], 'loss_description' => ['label' => 'Loss Description', 'rules' => 'permit_empty'], 'loss_estimate' => ['label' => 'Loss Estimate', 'rules' => 'permit_empty|numeric', 'errors' => ['numeric' => 'Must be numeric']], 'nhance_claim_ref_no' => ['label' => 'Nhance Ref No', 'rules' => 'permit_empty|max_length[100]'], 'claim_number' => ['label' => 'Claim Number', 'rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/_-]+$/]'], 'surveyor_name' => ['rules' => 'permit_empty|max_length[255]'], 'surveyor_contact_person' => ['rules' => 'permit_empty|max_length[255]'], 'surveyor_contact_number' => ['rules' => 'permit_empty|numeric|min_length[10]|max_length[15]'], 'surveyor_email' => ['rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/]'], 'loss_assessed_value' => ['rules' => 'permit_empty|numeric'], 'settled_amount' => ['rules' => 'permit_empty|numeric'], 'settlement_utr' => ['rules' => 'permit_empty|max_length[100]'], ]; } public function formatDatesForClaim($ticket_data, $out_put_format = 'Y-m-d') { $dateFields = ['loss_date', 'intimation_recd_date', 'intimated_to_insurer_date', 'eta_for_documents']; foreach ($dateFields as $field) { if (empty($ticket_data[$field])) { $ticket_data[$field] = null; } else { $ticket_data[$field] = change_date_format($ticket_data[$field], null, $out_put_format); } } return $ticket_data; } protected function checkDuplicateNonEbClaim($data) { $client_id = $data['client_id'] ?? null; $loss_date = $data['loss_date'] ?? null; $policy_no = $data['policy_no'] ?? null; if (empty($client_id) || empty($loss_date)) return false; $query = $this->nonEbTicketModel ->where('client_id', $client_id) ->where('loss_date', $loss_date) ->where('is_active', 1); if (!empty($policy_no)) { $query->where('policy_no', $policy_no); } return !empty($query->first()); } protected function handleAssetFileUpload() { $file = $this->request->getFile('asset_file'); if ($file === null || !$file->isValid() || $file->hasMoved()) { return null; } $uploadPath = WRITEPATH . 'uploads/non_eb_asset_files/'; $fileName = storage_file_Upload($file, $uploadPath, UPLOAD_EXT_ASSET_FILES); return !empty($fileName) ? $fileName : null; } public function convertHtmlToText($html) { if (empty($html)) return ''; $html = preg_replace('/[\x00-\x1F\x80-\xFF]/', ' ', $html); $dom = new DOMDocument(); libxml_use_internal_errors(true); $dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8')); $xpath = new DOMXPath($dom); foreach ($xpath->query('//style|//script') as $node) { $node->parentNode->removeChild($node); } $text = $dom->textContent; $text = preg_replace('/\s+/', ' ', $text); return trim($text); } // ===================== CLAIM FILES ===================== public function uploadFile() { $request_data = $this->request->getPost(); $data = sanitizeInputArrayAdvanced($request_data); $rules = [ 'ticket_id_url' => ['rules' => 'required|numeric', 'errors' => ['required' => 'System could not identify the Ticket.', 'numeric' => 'Invalid format.']], 'docs_name.*' => ['rules' => 'required|regex_match[/^[a-zA-Z0-9_\- ]+$/]', 'errors' => ['required' => 'Document Name is required', 'regex_match' => 'Only letters, numbers, hyphens, underscores allowed']], ]; if (!$this->validate($rules)) { return $this->response->setStatusCode(400)->setJSON(['status' => false, 'message' => 'Input validation failed', 'code' => 400, 'errors' => $this->validator->getErrors()]); } $ticket_id = $data['ticket_id_url']; $get_file_data = $this->request->getFiles('file_upload') ?? []; if (empty($get_file_data)) { $insertArr = []; foreach ($data['docs_name'] as $key => $docName) { if (!empty($docName)) { $insertArr[] = [ 'doc_name' => $docName, 'url' => convertGoogleDriveToDownloadLink($data['url'][$key] ?? ''), 'ticket_id' => $ticket_id, 'ticket_type' => 2, 'file_type' => 1, 'is_active' => 1, 'created_by' => get_session_userid(), ]; } } if (!empty($insertArr)) { $insert = $this->claimFilesModel->insertBatch($insertArr); } if (isset($insert) && !empty($insert)) { return $this->respond(['status' => true, 'message' => 'File uploaded successfully']); } return $this->respond(['status' => false, 'message' => 'Failed to upload file']); } else { $file_path = WRITEPATH . 'uploads/claim_files/'; $file_data = storage_multi_file_Upload($get_file_data, $file_path, $data['docs_name'], UPLOAD_EXT_CLAIM_DOCS); if (!empty($file_data)) { $insertArr = []; foreach ($file_data as $f) { $insertArr[] = [ 'ticket_id' => $ticket_id, 'ticket_type' => 2, 'doc_name' => $f['doc_name'], 'file_name' => $f['file_name'], 'url' => $f['file_path'], 'file_type' => 2, 'mime_type' => getMimeTypeByFileName($f['file_name']), 'is_active' => 1, 'created_by' => get_session_userid(), ]; } $insert = $this->claimFilesModel->insertBatch($insertArr); if ($insert) { return $this->respond(['status' => true, 'message' => 'File uploaded successfully']); } } return $this->respond(['status' => false, 'message' => 'Failed to upload file']); } } public function getClaimFiles() { $ticket_id = $this->request->getPost('ticket_id'); if (!$ticket_id) { return $this->response->setJSON(['status' => false, 'message' => 'Ticket ID is required', 'data' => []]); } $files = $this->claimFilesModel ->select("*, CASE WHEN file_type = 2 AND url IS NOT NULL AND url != '' THEN CONCAT('" . base_url('downloadClaimFile/') . "', id) ELSE url END AS url") ->where('ticket_id', $ticket_id) ->where('ticket_type', 2) ->where('is_active', 1) ->findAll(); return $this->response->setJSON(['status' => true, 'data' => $files]); } public function removeFile() { $id = $this->request->getGet('id'); if (empty(trim($id ?? ''))) { return $this->response->setJSON(['status' => false, 'message' => 'Invalid ID']); } if (storage_soft_delete_claim_file((int) $id)) { return $this->response->setJSON(['status' => true, 'message' => 'File removed successfully.']); } return $this->response->setJSON(['status' => false, 'message' => 'Failed to remove file.']); } public function saveIRDocs() { $request_data = $this->request->getPost(); $data = sanitizeInputArrayAdvanced($request_data); $ticket_id = $data['ticket_id'] ?? ''; $required_docs = $data['required_docs'] ?? ''; $rules = [ 'ticket_id' => ['rules' => 'required|numeric', 'errors' => ['required' => 'Ticket ID required.', 'numeric' => 'Invalid format.']], 'required_docs' => ['rules' => 'required', 'errors' => ['required' => 'Document data is required']], ]; if (!$this->validate($rules)) { return $this->response->setStatusCode(400)->setJSON(['status' => false, 'message' => 'Input validation failed', 'code' => 400, 'errors' => $this->validator->getErrors()]); } $docsArray = json_decode($required_docs, true); if (json_last_error() !== JSON_ERROR_NONE || !isset($docsArray['docs']) || !is_array($docsArray['docs'])) { return $this->response->setStatusCode(400)->setJSON(['status' => false, 'message' => 'Invalid document data', 'code' => 400, 'errors' => ['required_docs' => 'Invalid JSON structure']]); } foreach ($docsArray['docs'] as $index => $doc) { if (!isset($doc['document_name']) || !preg_match('/^[a-zA-Z0-9_\- ]+$/', $doc['document_name'])) { return $this->response->setStatusCode(400)->setJSON(['status' => false, 'message' => 'Invalid document name at row ' . ($index + 1), 'code' => 400, 'errors' => ['required_docs' => 'Invalid document name at row ' . ($index + 1)]]); } } db_connect()->query("UPDATE non_eb_ticket_master SET required_docs = ? WHERE id = ?", [$required_docs, $ticket_id]); $saved = $this->nonEbTicketModel->select('required_docs')->where('id', $ticket_id)->first(); $savedDocs = json_decode($saved['required_docs'] ?? '{}', true) ?? []; return $this->respond(['status' => true, 'code' => 200, 'message' => 'IR docs saved successfully', 'data' => $savedDocs], 200); } }