722 lines
28 KiB
PHP
722 lines
28 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers\Api;
|
|
|
|
use App\Controllers\BaseController;
|
|
use CodeIgniter\API\ResponseTrait;
|
|
|
|
use App\Models\NonEbTicketMasterModel;
|
|
use App\Models\NonEbClaimAssetModel;
|
|
use App\Models\TicketHistoryModel;
|
|
use App\Models\TicketClaimStatusModel;
|
|
use App\Models\ClaimFilesModel;
|
|
use App\Models\ClientPolicyModel;
|
|
use App\Models\PolicyTypeModel;
|
|
use App\Models\ClientRMModel;
|
|
use App\Models\EmployeeModel;
|
|
use App\Models\LevelContactModel;
|
|
use App\Helpers\JWTToken;
|
|
|
|
class NonEbClaimApiController extends BaseController
|
|
{
|
|
use ResponseTrait;
|
|
|
|
protected $nonEbTicketModel;
|
|
protected $assetModel;
|
|
protected $ticketHistoryModel;
|
|
protected $claimStatusModel;
|
|
protected $claimFilesModel;
|
|
protected $clientPolicyModel;
|
|
protected $policyTypeModel;
|
|
protected $clientRMModel;
|
|
protected $myLogger;
|
|
|
|
// API system user ID — used as created_by for all inserts (no session)
|
|
const API_SYSTEM_USER_ID = 0;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->nonEbTicketModel = new NonEbTicketMasterModel();
|
|
$this->assetModel = new NonEbClaimAssetModel();
|
|
$this->ticketHistoryModel = new TicketHistoryModel();
|
|
$this->claimStatusModel = new TicketClaimStatusModel();
|
|
$this->claimFilesModel = new ClaimFilesModel();
|
|
$this->clientPolicyModel = new ClientPolicyModel();
|
|
$this->policyTypeModel = new PolicyTypeModel();
|
|
$this->clientRMModel = new ClientRMModel();
|
|
$this->myLogger = \Config\Services::mylogger();
|
|
}
|
|
|
|
// ===================== AUTH HELPER =====================
|
|
|
|
/**
|
|
* Decode the JWT from Authorization header and return the resolved user row.
|
|
* Returns array with normalised keys: id, name, email, mobile.
|
|
* Returns null if token is missing or invalid.
|
|
*/
|
|
protected function getAuthUser(): ?array
|
|
{
|
|
$authHeader = $this->request->getHeaderLine('Authorization');
|
|
if (empty($authHeader)) {
|
|
return null;
|
|
}
|
|
|
|
$result = JWTToken::validateJWT($authHeader);
|
|
if ($result['status'] !== true) {
|
|
return null;
|
|
}
|
|
|
|
$decoded = $result['decoded'];
|
|
|
|
if (isset($decoded['emp_code'])) {
|
|
$model = new EmployeeModel();
|
|
$user = $model->find($decoded['id'] ?? null);
|
|
if (!$user) return null;
|
|
return [
|
|
'id' => $user['id'],
|
|
'name' => $user['name'] ?? '',
|
|
'email' => $user['email_corporate'] ?? $user['email'] ?? '',
|
|
'mobile' => $user['mobile'] ?? '',
|
|
];
|
|
}
|
|
|
|
$model = new LevelContactModel();
|
|
$user = $model->find($decoded['post_hr_id'] ?? null);
|
|
if (!$user) return null;
|
|
return [
|
|
'id' => $user['id'],
|
|
'name' => $user['name'] ?? '',
|
|
'email' => $user['email'] ?? '',
|
|
'mobile' => $user['mobile'] ?? '',
|
|
];
|
|
}
|
|
|
|
// ===================== SHARED HELPERS =====================
|
|
|
|
/**
|
|
* Convert DD-MM-YYYY date fields to Y-m-d for DB storage.
|
|
*/
|
|
protected function formatDatesForClaim(array $data): array
|
|
{
|
|
$dateFields = ['loss_date', 'intimation_recd_date', 'intimated_to_insurer_date', 'eta_for_documents'];
|
|
foreach ($dateFields as $field) {
|
|
if (empty($data[$field])) {
|
|
$data[$field] = null;
|
|
} else {
|
|
$data[$field] = change_date_format($data[$field], null, 'Y-m-d');
|
|
}
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Duplicate check: same client + loss_date + policy_no.
|
|
*/
|
|
protected function checkDuplicateNonEbClaim(array $data): bool
|
|
{
|
|
$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());
|
|
}
|
|
|
|
/**
|
|
* Upload the asset_file from the request. Returns filename or null.
|
|
*/
|
|
protected function handleAssetFileUpload(): ?string
|
|
{
|
|
$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;
|
|
}
|
|
|
|
/**
|
|
* Save asset rows (asset_id_code[], serial_no[], etc.) linked to a claim.
|
|
*/
|
|
protected function saveAssets(int $claim_id, array $post_data): void
|
|
{
|
|
$this->assetModel->where('non_eb_ticket_id', $claim_id)->set(['is_active' => 0])->update();
|
|
|
|
$codes = $post_data['asset_id_code'] ?? [];
|
|
$serials = $post_data['serial_no'] ?? [];
|
|
$vehicles = $post_data['vehicle_no'] ?? [];
|
|
$descriptions = $post_data['asset_description'] ?? [];
|
|
|
|
if (!is_array($codes)) return;
|
|
|
|
for ($i = 0; $i < count($codes); $i++) {
|
|
$code = trim($codes[$i] ?? '');
|
|
$serial = trim($serials[$i] ?? '');
|
|
$vehicle = trim($vehicles[$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,
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Insert the initial history row after a new claim is created.
|
|
*/
|
|
protected function putHistoryAfterInsert(array $ticket_data, int $ticket_id): void
|
|
{
|
|
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' => self::API_SYSTEM_USER_ID,
|
|
'is_active' => 1,
|
|
]);
|
|
}
|
|
}
|
|
|
|
// ===================== ENDPOINTS =====================
|
|
|
|
public function createClaim()
|
|
{
|
|
$authUser = $this->getAuthUser();
|
|
if (!$authUser) {
|
|
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
|
|
}
|
|
|
|
$rawBody = $this->request->getBody();
|
|
$isJson = str_contains($this->request->getHeaderLine('Content-Type'), 'application/json');
|
|
$body = ($isJson || ($rawBody !== '' && $rawBody !== null))
|
|
? (json_decode($rawBody, true) ?? $this->request->getPost())
|
|
: $this->request->getPost();
|
|
|
|
// Validate minimal user-facing fields only
|
|
$rules = [
|
|
'client_policy_id' => ['rules' => 'required|is_natural_no_zero', 'errors' => ['required' => 'Client Policy is required']],
|
|
'nature_of_loss' => ['rules' => 'required|min_length[3]', 'errors' => ['required' => 'Nature of Loss is required']],
|
|
'loss_location' => ['rules' => 'required', 'errors' => ['required' => 'Loss Location is required']],
|
|
'loss_date' => ['rules' => 'required', 'errors' => ['required' => 'Loss Date is required']],
|
|
'loss_description' => ['rules' => 'permit_empty'],
|
|
'loss_estimate' => ['rules' => 'permit_empty|numeric'],
|
|
'claim_number' => ['rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/_-]+$/]'],
|
|
];
|
|
|
|
if (!$this->validateData($body, $rules)) {
|
|
return $this->respond([
|
|
'status' => false, 'code' => 400,
|
|
'message' => 'Input validation failed',
|
|
'errors' => $this->validator->getErrors(),
|
|
], 400);
|
|
}
|
|
|
|
// Fetch client_policy — derive all FK fields from it
|
|
$cp = $this->clientPolicyModel
|
|
->where('id', (int)$body['client_policy_id'])
|
|
->where('is_active', 1)
|
|
->first();
|
|
|
|
if (!$cp) {
|
|
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Client policy not found or inactive'], 404);
|
|
}
|
|
|
|
// Validate policy type is Non-EB or Marine
|
|
$policyType = $this->policyTypeModel
|
|
->select('allocg')
|
|
->where('id', $cp['policy_type_id'])
|
|
->where('is_active', 1)
|
|
->first();
|
|
|
|
if (!$policyType || !in_array($policyType['allocg'], ['Non-EB', 'Marine'])) {
|
|
return $this->respond(['status' => false, 'code' => 422, 'message' => 'Only Non-EB or Marine policy types are allowed'], 422);
|
|
}
|
|
|
|
// Fetch ACM from client_rm (level 3)
|
|
$acm = $this->clientRMModel
|
|
->where('client_id', $cp['client_id'])
|
|
->where('level', 3)
|
|
->where('is_active', 1)
|
|
->first();
|
|
|
|
if (!$acm) {
|
|
$this->myLogger->logme('error', "[NON_EB_API] No ACM found for client_id: {$cp['client_id']}");
|
|
}
|
|
|
|
// Auto-set first claim_status_id for this policy type
|
|
$firstStatus = $this->claimStatusModel
|
|
->select('id')
|
|
->where('ticket_type', $cp['policy_type_id'])
|
|
->orderBy('id', 'ASC')
|
|
->first();
|
|
|
|
if (!$firstStatus) {
|
|
return $this->respond(['status' => false, 'code' => 422, 'message' => 'No claim status configured for this policy type'], 422);
|
|
}
|
|
|
|
// Build ticket data — merge user input with server-derived values
|
|
$ticket_data = [
|
|
'client_policy_id' => (int)$cp['id'],
|
|
'client_id' => (int)$cp['client_id'],
|
|
'branch_id' => (int)$cp['client_branch_id'],
|
|
'policy_type_id' => (int)$cp['policy_type_id'],
|
|
'insurer_id' => (int)$cp['insurer_id'],
|
|
'policy_no' => $cp['policy_no'] ?? null,
|
|
'acm_id' => $acm ? (int)$acm['user_id'] : null,
|
|
'claim_status_id' => (int)$firstStatus['id'],
|
|
'insured_contact_name' => $authUser['name'],
|
|
'insured_contact_number' => $authUser['mobile'],
|
|
'insured_contact_email' => $authUser['email'],
|
|
'nature_of_loss' => $body['nature_of_loss'],
|
|
'loss_location' => $body['loss_location'],
|
|
'loss_date' => $body['loss_date'],
|
|
'loss_description' => $body['loss_description'] ?? null,
|
|
'loss_estimate' => $body['loss_estimate'] ?? null,
|
|
'claim_number' => $body['claim_number'] ?? null,
|
|
'priority' => 1,
|
|
'created_by' => self::API_SYSTEM_USER_ID,
|
|
];
|
|
|
|
$ticket_data = $this->formatDatesForClaim($ticket_data);
|
|
|
|
// Handle optional asset file upload
|
|
$assetFileName = null;
|
|
$assetFile = $this->request->getFile('asset_file');
|
|
|
|
if ($assetFile !== null && $assetFile->isValid() && !$assetFile->hasMoved()) {
|
|
$ext = strtolower($assetFile->getClientExtension());
|
|
$allowed = UPLOAD_EXT_ASSET_FILES; // ['pdf', 'xls', 'xlsx', 'csv']
|
|
|
|
if (!in_array($ext, $allowed)) {
|
|
return $this->respond([
|
|
'status' => false, 'code' => 415,
|
|
'message' => 'Unsupported file type: ' . $ext . '. Allowed: ' . implode(', ', $allowed),
|
|
], 415);
|
|
}
|
|
|
|
if (empty(trim($ticket_data['loss_description'] ?? ''))) {
|
|
return $this->respond([
|
|
'status' => false, 'code' => 400,
|
|
'message' => 'Input validation failed',
|
|
'errors' => ['loss_description' => 'Loss Description is required when uploading an asset file.'],
|
|
], 400);
|
|
}
|
|
|
|
$uploadPath = WRITEPATH . 'uploads/non_eb_asset_files/';
|
|
$assetFileName = file_Upload($assetFile, $uploadPath, $allowed);
|
|
|
|
if (empty($assetFileName)) {
|
|
return $this->respond(['status' => false, 'code' => 500, 'message' => 'Asset file upload failed'], 500);
|
|
}
|
|
|
|
$ticket_data['asset_file'] = $assetFileName;
|
|
}
|
|
|
|
// Duplicate check (runs after derivation so client_id + policy_no are populated)
|
|
if ($this->checkDuplicateNonEbClaim($ticket_data)) {
|
|
return $this->respond([
|
|
'status' => false, 'code' => 409,
|
|
'message' => 'Duplicate claim found for Client + Loss Date + Policy No combination',
|
|
], 409);
|
|
}
|
|
|
|
$claim_id = $this->nonEbTicketModel->insert($ticket_data);
|
|
|
|
if (!$claim_id) {
|
|
return $this->respond(['status' => false, 'code' => 500, 'message' => 'Failed to create claim'], 500);
|
|
}
|
|
|
|
$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']}");
|
|
|
|
return $this->respond([
|
|
'status' => true,
|
|
'code' => 200,
|
|
'claim_id' => $claim_id,
|
|
'asset_file' => $assetFileName,
|
|
'message' => 'Non-EB Claim created successfully',
|
|
], 200);
|
|
}
|
|
|
|
public function listClaims()
|
|
{
|
|
$authUser = $this->getAuthUser();
|
|
if (!$authUser) {
|
|
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
|
|
}
|
|
|
|
try {
|
|
$body = $this->request->getJSON(true) ?? [];
|
|
} catch (\Throwable $e) {
|
|
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Invalid JSON body'], 400);
|
|
}
|
|
$page = max(1, (int)($body['page'] ?? 1));
|
|
$per_page = min(100, max(1, (int)($body['per_page'] ?? 20)));
|
|
$offset = ($page - 1) * $per_page;
|
|
|
|
$db = db_connect();
|
|
$builder = $db->table('non_eb_ticket_master tm');
|
|
|
|
$builder->select([
|
|
'tm.id',
|
|
'tm.claim_number',
|
|
'tm.nhance_claim_ref_no',
|
|
'tm.policy_no',
|
|
'tm.policy_type_id',
|
|
'tm.claim_status_id',
|
|
'tcs.claim_status AS status',
|
|
'tcs.display_name AS status_display',
|
|
'pt.policy_type AS policy_type_name',
|
|
'c.client_name',
|
|
'i.name AS insurer_name',
|
|
'tm.loss_date',
|
|
'tm.loss_location',
|
|
'tm.nature_of_loss',
|
|
'tm.loss_estimate',
|
|
'tm.insured_contact_name',
|
|
'tm.insured_contact_number',
|
|
'(SELECT first_name FROM user_profiles WHERE user_profiles.id = tm.acm_id) AS acm_name',
|
|
'DATE_FORMAT(tm.created_at, "%d-%m-%Y") AS created_date',
|
|
'DATE_FORMAT(tm.updated_at, "%d-%m-%Y") AS updated_date',
|
|
]);
|
|
|
|
$builder->join('clients c', 'c.id = tm.client_id AND c.is_active = 1', 'left');
|
|
$builder->join('insurers i', 'i.id = tm.insurer_id AND i.is_active = 1', 'left');
|
|
$builder->join('ticket_claim_status tcs','tcs.id = tm.claim_status_id AND tcs.is_active = 1', 'left');
|
|
$builder->join('policy_type pt', 'pt.id = tm.policy_type_id AND pt.is_active = 1', 'left');
|
|
|
|
$builder->where('tm.is_active', 1);
|
|
|
|
// Filters
|
|
if (!empty($body['client_id'])) $builder->where('md5(tm.client_id)', (int)$body['client_id']);
|
|
if (!empty($body['insurer_id'])) $builder->where('tm.insurer_id', (int)$body['insurer_id']);
|
|
if (!empty($body['policy_type_id'])) $builder->where('tm.policy_type_id', (int)$body['policy_type_id']);
|
|
if (!empty($body['claim_status_id'])) $builder->where('tm.claim_status_id', (int)$body['claim_status_id']);
|
|
if (!empty($body['claim_number'])) $builder->like('tm.claim_number', $body['claim_number']);
|
|
if (!empty($body['nhance_claim_ref_no'])) $builder->like('tm.nhance_claim_ref_no', $body['nhance_claim_ref_no']);
|
|
|
|
// Date range filter
|
|
if (!empty($body['date_type']) && !empty($body['start_date']) && !empty($body['end_date'])) {
|
|
$col = $body['date_type'] === 'updated_date' ? 'tm.updated_at' : 'tm.created_at';
|
|
$start = date('Y-m-d 00:00:00', strtotime(str_replace('-', '/', $body['start_date'])));
|
|
$end = date('Y-m-d 23:59:59', strtotime(str_replace('-', '/', $body['end_date'])));
|
|
$builder->where("$col BETWEEN '$start' AND '$end'");
|
|
}
|
|
|
|
// Exclude terminal statuses by default
|
|
if (empty($body['show_closed'])) {
|
|
$builder->whereNotIn('tcs.display_name', ['Claim Settled', 'Claim Closed', 'Claim Rejected', 'Claim Withdrawn']);
|
|
}
|
|
|
|
// COUNT for pagination (clone before limit)
|
|
$countBuilder = clone $builder;
|
|
$total = $countBuilder->countAllResults(false);
|
|
|
|
$builder->orderBy('tm.id', 'DESC');
|
|
$builder->limit($per_page, $offset);
|
|
|
|
$data = $builder->get()->getResultArray();
|
|
|
|
return $this->respond([
|
|
'status' => true,
|
|
'code' => 200,
|
|
'total' => (int)$total,
|
|
'page' => $page,
|
|
'per_page' => $per_page,
|
|
'data' => $data,
|
|
], 200);
|
|
}
|
|
|
|
public function claimHistory(int $claim_id)
|
|
{
|
|
$authUser = $this->getAuthUser();
|
|
if (!$authUser) {
|
|
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();
|
|
|
|
if (!$claim) {
|
|
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Claim not found'], 404);
|
|
}
|
|
|
|
// Fetch only claim_status_id history rows, oldest first
|
|
$history_rows = $this->ticketHistoryModel
|
|
->select('new_value, created_at')
|
|
->where('ticket_id', $claim_id)
|
|
->where('field_name', 'claim_status_id')
|
|
->where('is_active', 1)
|
|
->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('display_name IS NOT NULL')
|
|
->where("display_name != ''")
|
|
->where('is_active', 1)
|
|
->findAll();
|
|
|
|
$display_map = array_column($statuses, 'display_name', 'id');
|
|
|
|
// Filter and format — skip statuses with no display_name
|
|
$history = [];
|
|
foreach ($history_rows as $row) {
|
|
$status_id = (int)$row['new_value'];
|
|
$display_name = $display_map[$status_id] ?? null;
|
|
if (!$display_name) continue;
|
|
|
|
$history[] = [
|
|
'status' => $display_name,
|
|
'changed_at' => date('d-m-Y h:i A', strtotime($row['created_at'])),
|
|
];
|
|
}
|
|
|
|
return $this->respond([
|
|
'status' => true,
|
|
'code' => 200,
|
|
'claim_id' => $claim_id,
|
|
'history' => $history,
|
|
], 200);
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/non-eb-claim/statuses
|
|
* Returns Non-EB claim statuses (ticket_type = 50).
|
|
*/
|
|
public function listClaimStatuses()
|
|
{
|
|
$authUser = $this->getAuthUser();
|
|
if (!$authUser) {
|
|
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
|
|
}
|
|
|
|
$statuses = $this->claimStatusModel
|
|
->select('id, claim_status, display_name')
|
|
->where('ticket_type', 50)
|
|
->where('is_active', 1)
|
|
->orderBy('id', 'ASC')
|
|
->findAll();
|
|
|
|
return $this->respond([
|
|
'status' => true,
|
|
'code' => 200,
|
|
'data' => $statuses,
|
|
], 200);
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/non-eb-claim/policies
|
|
* Returns Non-EB / Marine policies for a given client (md5) + branch.
|
|
*
|
|
* Body:
|
|
* client_id string MD5 hash of the client's numeric id (required)
|
|
* client_branch_id int client branch id (required)
|
|
*/
|
|
public function listPolicies()
|
|
{
|
|
$authUser = $this->getAuthUser();
|
|
if (!$authUser) {
|
|
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
|
|
}
|
|
|
|
try {
|
|
$body = $this->request->getJSON(true) ?? $this->request->getPost();
|
|
} catch (\Throwable $e) {
|
|
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Invalid JSON body'], 400);
|
|
}
|
|
$client_id_md5 = trim($body['client_id'] ?? '');
|
|
$client_branch_id = (int)($body['client_branch_id'] ?? 0);
|
|
|
|
$errors = [];
|
|
if (empty($client_id_md5)) {
|
|
$errors['client_id'] = 'client_id is required';
|
|
} elseif (!preg_match('/^[a-f0-9]{32}$/i', $client_id_md5)) {
|
|
$errors['client_id'] = 'client_id must be a valid MD5 hash';
|
|
}
|
|
if ($client_branch_id <= 0) {
|
|
$errors['client_branch_id'] = 'client_branch_id is required';
|
|
}
|
|
if (!empty($errors)) {
|
|
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Input validation failed', 'errors' => $errors], 400);
|
|
}
|
|
|
|
$db = db_connect();
|
|
$builder = $db->table('client_policy cp');
|
|
|
|
$builder->select([
|
|
'cp.id',
|
|
'cp.policy_no',
|
|
'cp.policy_type_id',
|
|
'pt.policy_type AS policy_type_name',
|
|
'cp.insurer_id',
|
|
'i.name AS insurer_name',
|
|
'i.short_name AS insurer_short_name',
|
|
'DATE_FORMAT(cp.policy_start_date, "%d-%m-%Y") AS policy_start_date',
|
|
'DATE_FORMAT(cp.policy_end_date, "%d-%m-%Y") AS policy_end_date',
|
|
]);
|
|
$builder->join('policy_type pt', 'pt.id = cp.policy_type_id', 'left');
|
|
$builder->join('insurers i', 'i.id = cp.insurer_id AND i.is_active = 1', 'left');
|
|
$builder->where('MD5(cp.client_id)', $client_id_md5);
|
|
$builder->where('cp.client_branch_id', $client_branch_id);
|
|
$builder->where('cp.is_active', 1);
|
|
$builder->whereIn('pt.allocg', ['Non-EB', 'Marine']);
|
|
$builder->orderBy('cp.id', 'DESC');
|
|
|
|
$policies = $builder->get()->getResultArray();
|
|
|
|
return $this->respond([
|
|
'status' => true,
|
|
'code' => 200,
|
|
'total' => count($policies),
|
|
'data' => $policies,
|
|
], 200);
|
|
}
|
|
|
|
public function uploadRequiredDoc(int $claim_id)
|
|
{
|
|
$authUser = $this->getAuthUser();
|
|
if (!$authUser) {
|
|
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
|
|
}
|
|
|
|
// Verify claim exists
|
|
$claim = $this->nonEbTicketModel
|
|
->select('id, client_id, required_docs')
|
|
->where('id', $claim_id)
|
|
->where('is_active', 1)
|
|
->first();
|
|
|
|
if (!$claim) {
|
|
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Claim not found'], 404);
|
|
}
|
|
|
|
// Validate required_docs checklist is configured
|
|
$required_docs = json_decode($claim['required_docs'] ?? '{}', true);
|
|
if (empty($required_docs) || empty($required_docs['docs'])) {
|
|
return $this->respond(['status' => false, 'code' => 422, 'message' => 'No required documents checklist configured for this claim'], 422);
|
|
}
|
|
|
|
// Check if checklist is locked
|
|
if (!empty($required_docs['is_action_freeze'])) {
|
|
return $this->respond(['status' => false, 'code' => 423, 'message' => 'Document checklist is locked for this claim'], 423);
|
|
}
|
|
|
|
// Validate inputs
|
|
$document_name = trim($this->request->getPost('document_name') ?? '');
|
|
if (empty($document_name)) {
|
|
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Input validation failed', 'errors' => ['document_name' => 'document_name is required']], 400);
|
|
}
|
|
|
|
$file = $this->request->getFile('file');
|
|
if (!$file || !$file->isValid() || $file->hasMoved()) {
|
|
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Input validation failed', 'errors' => ['file' => 'A valid file is required']], 400);
|
|
}
|
|
|
|
// Validate file extension
|
|
$allowed = defined('UPLOAD_EXT_CLAIM_DOCS') ? UPLOAD_EXT_CLAIM_DOCS : ['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx', 'xls', 'xlsx'];
|
|
$ext = strtolower($file->getClientExtension());
|
|
if (is_array($allowed) && !in_array($ext, $allowed)) {
|
|
return $this->respond(['status' => false, 'code' => 415, 'message' => 'Unsupported file type: ' . $ext], 415);
|
|
}
|
|
|
|
// Find matching doc in checklist (exact case-sensitive match)
|
|
$matched_index = null;
|
|
foreach ($required_docs['docs'] as $i => $doc) {
|
|
if (($doc['document_name'] ?? '') === $document_name) {
|
|
$matched_index = $i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($matched_index === null) {
|
|
return $this->respond([
|
|
'status' => false, 'code' => 404,
|
|
'message' => "Document '{$document_name}' not found in required documents list",
|
|
], 404);
|
|
}
|
|
|
|
// Upload file and update required_docs atomically
|
|
$db = db_connect();
|
|
$db->transStart();
|
|
|
|
$upload_path = WRITEPATH . 'uploads/claim_files/';
|
|
$file_name = file_Upload($file, $upload_path, UPLOAD_EXT_CLAIM_DOCS);
|
|
|
|
if (empty($file_name)) {
|
|
$db->transRollback();
|
|
return $this->respond(['status' => false, 'code' => 500, 'message' => 'File upload failed'], 500);
|
|
}
|
|
|
|
// Insert into claim_files
|
|
$this->claimFilesModel->insert([
|
|
'ticket_id' => $claim_id,
|
|
'ticket_type' => 2,
|
|
'doc_name' => $document_name,
|
|
'file_name' => $file_name,
|
|
'url' => $upload_path . $file_name,
|
|
'file_type' => 2,
|
|
'mime_type' => getMimeTypeByFileName($file_name),
|
|
'is_active' => 1,
|
|
'created_by' => self::API_SYSTEM_USER_ID,
|
|
]);
|
|
|
|
// Mark document as received in required_docs JSON
|
|
$required_docs['docs'][$matched_index]['document_received'] = true;
|
|
$updated_json = json_encode($required_docs);
|
|
|
|
$db->query('UPDATE non_eb_ticket_master SET required_docs = ? WHERE id = ?', [$updated_json, $claim_id]);
|
|
|
|
$db->transComplete();
|
|
|
|
if (!$db->transStatus()) {
|
|
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;
|
|
|
|
return $this->respond([
|
|
'status' => true,
|
|
'code' => 200,
|
|
'message' => 'Document uploaded successfully',
|
|
'claim_id' => $claim_id,
|
|
'document_name' => $document_name,
|
|
'download_url' => $download_url,
|
|
'required_docs' => $required_docs,
|
|
], 200);
|
|
}
|
|
}
|