nhance/app/Controllers/NonEbClaimController.php
2026-03-31 12:04:35 +05:30

1195 lines
56 KiB
PHP

<?php
namespace App\Controllers;
use App\Controllers\BaseController;
use CodeIgniter\API\ResponseTrait;
use App\Models\NonEbTicketMasterModel;
use App\Models\NonEbClaimAssetModel;
use App\Models\ClaimFilesModel;
use App\Models\TicketClaimStatusModel;
use App\Models\ClientModel;
use App\Models\TicketHistoryModel;
use App\Models\TicketMailTemplateModel;
use App\Models\TicketMessageModel;
use App\Models\TicketNoteModel;
use App\Models\UserModel;
use App\Models\InsurerModel;
use App\Models\PolicyTypeModel;
use App\Models\ClientPolicyModel;
use DOMDocument;
use DOMXPath;
use App\Helpers\MailHelper;
class NonEbClaimController extends BaseController
{
use ResponseTrait;
protected $myLogger;
public $priorityType;
public $placeHolders;
public $triggerType;
protected $nonEbTicketModel;
protected $assetModel;
protected $claimFilesModel;
protected $claimStatus;
protected $clientModel;
protected $ticketHistoryModel;
protected $ticketMailTemplateModel;
protected $ticketMessageModel;
protected $ticketNoteModel;
protected $userModel;
protected $insurerModel;
protected $policyTypeModel;
protected $clientPolicyModel;
// Sections visible per claim_status value
protected $statusSectionVisibility;
public function __construct()
{
$this->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 = 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 = 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']);
}
$updated = $this->claimFilesModel->where('id', $id)->set(['is_active' => 0])->update();
if ($updated) {
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);
}
}