Job Queue for file read , commission calculation , BDS entry

This commit is contained in:
Gowtham M 2025-11-25 17:47:10 +05:30
parent 34fc582d64
commit 14ff8fe394
8 changed files with 547 additions and 20 deletions

View File

@ -138,7 +138,8 @@ $routes->group('api', ['filter' => 'appSignature'], function ($routes) {
$routes->get('checkPolicyDoc', 'PolicyController::checkPolicyDoc');
$routes->get('checkPolicyDoc', 'PolicyController::readFile');
$routes->get('calculateCommission', 'PolicyController::calculateCommission');
$routes->get('testPolicy', 'QuotationController::testPolicy');

235
app/Controllers/JobWorker.php Executable file
View File

@ -0,0 +1,235 @@
<?php
namespace App\Controllers;
use App\Models\JobModel;
use App\Models\FileModel;
class JobWorker extends BaseController
{
const STATUS_DONE = 'done';
const STATUS_QUEUED = 'queued';
const STATUS_RUNNING = 'running';
const STATUS_FAILED = 'failed';
/**
* Constructs the class
*/
private static $event_class_mapping =
[
'readFileAndCalculateCommission' => [
'type' => 'CC', // Handler Category (Possible values: HC, CC, HF)
'handler' => 'App\Controllers\PolicyController',
],
];
public function __construct()
{
}
public static function streamOutput($data)
{
ob_implicit_flush(true);
// try { ob_end_flush(); } catch(Exception $e) { echo $e->getMessage(); }
echo $data;
flush();
}
public static function processJobs(array $jobdata = [])
{
// echo 'listen';//die();
$query = "
SELECT id, name, payload, uuid
FROM partner_jobs
WHERE status=?
ORDER BY created_dt ASC";
$where_condition = [self::STATUS_QUEUED];
$db = \Config\Database::connect();
$jobs = $db->query($query, $where_condition)->getResult();
if(count($jobs))
{
// echo count($jobs);
// print_r($jobs);die;
foreach($jobs as $key => $job)
{
// echo $job->id.' - '.$job->name;
//sleep(1);
SELF::processjob(['id' => $job->id,'uuid' => $job->uuid]);
// usleep( 500000 );
}
}
}
/**
* process jobs
*/
public static function processJob(array $jobdata = [])
{
// print_r($jobdata);
// echo 'listen';
// die();
$query = "
SELECT id, name, payload, uuid
FROM partner_jobs
WHERE status=?
ORDER BY created_dt ASC
LIMIT 1 FOR UPDATE";
$where_condition = [self::STATUS_QUEUED];
if(isset($jobdata) && count($jobdata))
{
$query = "
SELECT id, name, payload, uuid
FROM partner_jobs
WHERE status=? AND id=? AND uuid=?
ORDER BY created_dt ASC
LIMIT 1 FOR UPDATE";
$where_condition = [self::STATUS_QUEUED,$jobdata['id'],$jobdata['uuid']];
}
$db = \Config\Database::connect();
$job = $db->query($query, $where_condition)->getResult();
// print_r($job);die();
if ($job !== [])
{
$job = $job[0];
// echo "\nProcessing job id - " . $job->id . "\n";
SELF::streamOutput("\nProcessing job id - " . $job->id . "\n");
// sleep(5);
// echo "Job name - " . $job->name . "\n";
SELF::streamOutput("Job name - " . $job->name . "\n");
// sleep(5);
// print_r(SELF::$event_class_mapping);
// echo array_key_exists($job->name,SELF::$event_class_mapping) ? 'mapped' : 'notmapped';
// die();
try
{
$start = microtime(true);
$runtime = null;
$job_status = self::STATUS_RUNNING;
$db->query("UPDATE jobs SET status=? WHERE id=? AND uuid=?", [$job_status, $job->id,$job->uuid]);
if (!array_key_exists($job->name,SELF::$event_class_mapping))
{
throw new \RuntimeException('Job ' . $job->name . ' handler not registered');
}
$handler = SELF::$event_class_mapping[$job->name];
$handleInstance = null;
if($handler['type'] == 'CC' || $handler['type'] == 'HC')
{
// echo 'CLASS - ' . $handler['type'].' - ' . $handler['handler'];
$handlerClass = $handler['handler'];
if(class_exists($handlerClass))
{
$handleInstance = new $handlerClass();
}
else
{
throw new \RuntimeException('Job ' . $job->name . ' or handler class not found');
// echo $e->getMessage();
}
if (method_exists($handleInstance, $job->name))
{
$jobHandler = [$handleInstance, $job->name];
//throw new \RuntimeException('Job ' . $job->name . ' not found');
}
else if(method_exists($handleInstance, 'handle'))
{
$jobHandler = [$handleInstance, 'handle'];
}
else
{
throw new \RuntimeException('Job ' . $job->name . ' or handler not found');
}
}
else if($handler['type'] == 'HF')
{
// echo 'HF - ' . $handler['type'];
$jobHandler = $handleInstance = $handler['handler'];
// echo $jobHandler;
}
else{
throw new \RuntimeException('Job ' . $job->name . ' Invalid job type');
}
$payload = json_decode($job->payload, true);
if (!is_array($payload))
{
throw new \InvalidArgumentException('Invalid payload format here');
}
$payload = is_array($payload) ? $payload : [];
try
{
$response = $jobHandler($payload,$job->id);
$job_status = self::STATUS_DONE;
}
catch(\Exception $e)
{
$job_status = self::STATUS_FAILED;
$runtime = $runtime === null ? microtime(true) - $start : $runtime;
$response = ['file_name' => $e->getFile(),'error' => $e->getMessage(),'line_no' => $e->getLine(),'info' => $e->getTraceAsString(),'scope' => 'task failed'];
}
$runtime = microtime(true) - $start;
}
catch (\Exception $e)
{
//die();
$job_status = self::STATUS_FAILED;
$runtime = $runtime === null ? microtime(true) - $start : $runtime;
$response = ['file_name' => $e->getFile(),'error' => $e->getMessage(),'line_no' => $e->getLine(),'info' => $e->getTraceAsString(),'scope' => 'worker failed'];
}
$db->query("UPDATE jobs SET status=?, run_time=?, response=? WHERE id=? AND uuid=?", [
$job_status,
$runtime,
json_encode($response),
$job->id,$job->uuid
]);
//update file status if this job is directly linked with a file id
if($job_status == self::STATUS_FAILED)
{
//get file from job payload
// if(array_key_exists('file_id', $payload) && $payload['file_id'] != NULL && $payload['file_id'] != "" && is_numeric($payload['file_id']) && count($payload) == 1)
// {
// $fileModel = new FileModel();
// $fileModel->where('id', $payload['file_id'])
// ->set(['status' => 'failed','reason' => json_encode(['error_type' => 0,'error_summary' => [0],'error_data' => 'System Error, please contact Admin/Support team']) ])
// ->update();
// }
}
// echo "Job $job->id $job_status. Response - $response \n";
// echo "Job $job->id $job_status \n";
SELF::streamOutput("Job $job->id $job_status \n");
return true;
}
else
{
echo 'no job found in queue';
}
}
}

65
app/Controllers/Jobs.php Executable file
View File

@ -0,0 +1,65 @@
<?php
namespace App\Controllers;
use App\Models\JobModel;
class Jobs extends BaseController
{
const STATUS_DONE = 'done';
const STATUS_QUEUED = 'queued';
const STATUS_RUNNING = 'running';
const STATUS_FAILED = 'failed';
protected $job_payload = [];
protected $myLogger;
/**
* Constructs the class
*/
// public function __construct(array $payload = ['job_name' => 'check','payload' => ['a' => 10]])
public function __construct()
{
// $this->job_payload = $payload;
// $this->myLogger = \Config\Services::mylogger();
}
/**
* add jobs to queue
*/
public static function addJob(array $payload)
{
$jobModel = new JobModel();
$myLogger = \Config\Services::mylogger();
try
{
if(!is_array($payload))
{
throw new \InvalidArgumentException('Invalid payload format while add job');
}
if(!isset($payload['job_name']))
{
throw new \InvalidArgumentException('Job name not found while add job');
}
if(!isset($payload['payload']))
{
throw new \InvalidArgumentException('Job payload not found while add job');
}
$uuid = generate_uuid();
$jobid = $jobModel->insert(['name' => $payload['job_name'],'uuid' => $uuid,'payload' => json_encode($payload['payload']), 'status' => isset($payload['status']) ? $payload['status'] : 'queued']);
// echo $jobid;
$myLogger->logme('error','new job {jobid} added to queue',['jobid' => $jobid]);
return ['id' => $jobid,'uuid' => $uuid,'job_name' => $payload['job_name']];
}
catch(\Exception $e)
{
$message = $e->getMessage();
return $message;
$myLogger->logme('error','{messgae}',['messgae' => $message]);
}
}
}

View File

@ -249,7 +249,6 @@ class PolicyController extends ResourceController
}
}
public function uploadPolicyFile()
{
try {
@ -333,8 +332,6 @@ class PolicyController extends ResourceController
}
}
// Download policy file
public function downloadPolicyFile()
{
@ -380,12 +377,93 @@ class PolicyController extends ResourceController
}
}
public function readFileAndCalculateCommission(array $data)
{
$policyId = $data['policy_id'];
public function checkPolicyDoc($policyId)
log_message('debug', 'checkPolicyDoc() started for policy_id: '.$policyId);
$readDoc = $this->checkPolicyDoc(["policy_id" => $policyId]);
if($readDoc['status'] == 'success')
{
$data = $readDoc['data'];
//update data in to policy table
$policyData['policy_number'] = $data['policy']['policy_number'] ?? null;
$policyData['issued_date'] = $data['policy']['issue_date'] ?? null;
$policyData['start_date'] = $data['policy']['period']['start'] ?? null;
$policyData['end_date'] = $data['policy']['period']['end'] ?? null;
$policyData['tp'] = $data['premium']['tp'] ?? null;
$policyData['od'] = $data['premium']['od'] ?? null;
$policyData['pa'] = $data['premium']['pa'] ?? null;
$policyData['cgst'] = $data['premium']['taxes']['cgst'] ?? 0;
$policyData['sgst'] = $data['premium']['taxes']['sgst'] ?? 0;
$policyData['igst'] = $data['premium']['taxes']['igst'] ?? 0;
$policyData['premium_amount'] = $data['premium']['total'] ?? null;
$policyData['rto_state_code'] = $data['vehicle']['rto_state_code'] ?? null;
$policyData['rto_city_code'] = $data['vehicle']['rto_city_code'] ?? null;
$policyData['weight'] = $data['vehicle']['weight'] ?? null;
$policyData['fuel_type'] = $data['vehicle']['fuel_type'] ?? null;
$policyData['date_of_registration'] = $data['vehicle']['date_of_registration'] ?? null;
$policyData['year_of_manufacture'] = $data['vehicle']['year_of_manufacture'] ?? null;
$policyData['engine_no'] = $data['vehicle']['engine_no'] ?? null;
$policyData['chassis_no'] = $data['vehicle']['chassis_no'] ?? null;
$policyData['make'] = $data['vehicle']['make'] ?? null;
$policyData['model'] = $data['vehicle']['model'] ?? null;
$policyData['cubic_capacity'] = $data['vehicle']['cubic_capacity'] ?? null;
$policyData['vehicle_type'] = $data['vehicle']['vehicle_type'] ?? null;
log_message('debug', 'Policy Update Payload: ' . json_encode($policyData));
$updateStatus = $this->PolicyModel->update($policyId,$policyData);
if ($updateStatus)
{
log_message('debug', 'Policy updated successfully for ID: '.$policyId);
$calculateCommission = $this->calculateCommission($policyId);
if($calculateCommission['status'] == 'success')
{
log_message('debug', 'BDS record creating started for ID: '.$policyId);
// Call helper to create BDS record after creating client,clientPolicy,vehicle records
$policyData = $this->PolicyModel->select('partner_policy.*,Q.insurer_id,Q.insurer_branch_id,E.name as client_name,E.mobile as client_mobile,E.email as client_email,E.reg_no,E.vehicle_type_id,A.id as agent_id,A.agent_code')
->join('partner_quotation Q', 'Q.id = partner_policy.quotation_id AND Q.status = "Accepted"', 'left')
->join('partner_enquiry E', 'E.id = partner_policy.enquiry_id', 'left')
->join('partner_agent A', 'A.id = partner_policy.agent_id', 'left')
->where('partner_policy.id',$policyId)
->first();
$bdsLogs = createBDS($policyData, $policyId);
if (!empty($bdsLogs)) {
foreach ($bdsLogs as $msg) {
log_message('info', '[BDS Entry] ' . $msg);
}
}
}
} else {
log_message('error', 'Failed to update policy for ID: '.$policyId);
}
} else {
log_message('error', 'checkPolicyDoc returned failure: ' . json_encode($readDoc));
}
echo '<pre>';
print_r($readDoc);
die;
$this->respond($data);
}
public function checkPolicyDoc($policyId = null)
{
try
{
// Replace with your actual Gemini API key
$apiKey = getenv('GEMINI_API_KEY');
@ -398,7 +476,7 @@ class PolicyController extends ResourceController
$record = $this->PolicyModel->find($policyId);
$uploadedPath = WRITEPATH . 'uploads/policy/policy_pdf/';
$filePath = $uploadedPath.$record['policy_pdf_file_name'];
// dd($filePath);
// Check if the file exists
if (!file_exists($filePath)) {
log_message('info',"Error: File not found at {$filePath}");
@ -546,7 +624,7 @@ class PolicyController extends ResourceController
log_message('info', "Extracted and decoded final JSON data successfully.");
return ['status'=>"sucess", 'message'=> "Doc read sucess" , 'data'=>$final_data];
return ['status'=>"success", 'message'=> "Doc read sucess" , 'data'=>$final_data];
} else {
log_message('info',"Response structure invalid or no generated text candidate found.");
@ -564,6 +642,110 @@ class PolicyController extends ResourceController
}
public function calculateCommission($policyId = null)
{
log_message('debug', 'calculateCommission() started with policyId: ' . $policyId);
$record = $this->PolicyModel->select('partner_policy.* , pe.insurer_id , VT.vehicle_type , ipti.insurance_plan_type,')
->join('partner_enquiry pe', 'pe.id = partner_policy.enquiry_id', 'left')
->join('vehicle_type VT', 'VT.id = pe.vehicle_type_id', 'left')
->join('partner_quotation pq', 'pq.id = partner_policy.quotation_id', 'left')
->join('partner_insurance_plan_type_master ipti', 'ipti.id = pq.insurance_plan_type_id', 'left')
->where('partner_policy.id', $policyId)
->first();
$apiUrl = "https://venbait.in/nhance/dev/getCommission";
$token = "A7fP3xQ9mD2vT6sR1bW8kJ4yC0gN5zH3uL9pE2rF7cV1tX6hB0qM4dG8nS5aU3jK7";
$manufactureYear = (int) $record['year_of_manufacture'];
$currentYear = (int) date("Y");
$vehicleAge = $currentYear - $manufactureYear;
$postData = [
"department" => "Motor",
"policy_business_type" => "retail",
"product" => "",
"renewal_type" => "fresh",
"renewal_sub_type" => "fresh",
"vehicle_type" => $record['vehicle_type'],
"policy_type" => $record['insurance_plan_type'],
"premium" => $record['premium_amount'],
"od_premium" => $record['od'],
"tp_premium" => $record['tp'],
"cubic_capcity" => $record['cubic_capacity'],
"weight" => $record['weight'],
"make" => $record['make'],
"model" => $record['model'],
"manufacture_year" => $record['year_of_manufacture'],
"vehicle_age" => $vehicleAge,
"date_of_registration" => $record['date_of_registration'],
"fuel_type" => $record['fuel_type'],
"geo_rto_state" => $record['rto_state_code'],
"geo_rto_city" => $record['rto_city_code'],
"policy_issue_date" => $record['issued_date'],
"insurer_id" => $record['insurer_id']
];
log_message('debug', 'API Request Payload: ' . json_encode($postData));
$ch = curl_init($apiUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Headers
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"Authorization: Bearer " . $token
]);
// POST Data
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postData));
$response = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($error)
{
log_message('error', 'cURL Error: ' . $error);
return ['status'=>"failed", 'message'=> 'cURL Error: ' . $error];
} else {
log_message('debug', 'API Response: ' . $response);
$result = json_decode($response, true);
// Optional: Debug API result
// print_r($result);
if (!empty($result) && isset($result['success']) && $result['success'] === true) {
// Safely extract values
$policyData = [
'commission_amount' => $result['data']['payout'] ?? null,
'commission_applied_rule' => $result['data']['rule']['id'] ?? null,
];
log_message('debug', 'Updating Policy (ID=75) With: ' . json_encode($policyData));
// Update record
$this->PolicyModel->update($policyId, $policyData);
log_message('debug', 'Policy update successful!');
return ['status'=>"success", 'message'=> "Commission Updated!"];
} else {
log_message('debug', 'calculateCommission() - Invalid API Response');
return ['status'=>"failed", 'message'=> "Invalid API Response!"];
}
}
}

View File

@ -6,6 +6,9 @@ use App\Models\QuotationModel;
use App\Models\EnquiryModel;
use App\Models\PolicyModel;
use App\Controllers\Jobs ;
use App\Controllers\JobWorker ;
class QuotationController extends ResourceController
{
protected $QuotationModel;
@ -471,19 +474,22 @@ class QuotationController extends ResourceController
// Update enquiry status → Policy Created
$this->EnquiryModel->update($quot['enquiry_id'], ['status' => 'Policy Created']);
Jobs::addJob(['job_name' => 'readFileAndCalculateCommission','payload' => ['policy_id' => $newPolicyId]]);
log_message("info",'readFileAndCalculateCommission job pushed');
// Call helper to create BDS record after creating client,clientPolicy,vehicle records
$policyData = $this->PolicyModel->select('partner_policy.*,Q.insurer_id,Q.insurer_branch_id,E.name as client_name,E.mobile as client_mobile,E.email as client_email,E.reg_no,E.vehicle_type_id,A.id as agent_id,A.agent_code')
->join('partner_quotation Q', 'Q.id = partner_policy.quotation_id AND Q.status = "Accepted"', 'left')
->join('partner_enquiry E', 'E.id = partner_policy.enquiry_id', 'left')
->join('partner_agent A', 'A.id = partner_policy.agent_id', 'left')
->where('partner_policy.id',$newPolicyId)
->first();
$bdsLogs = createBDS($policyData, $newPolicyId);
if (!empty($bdsLogs)) {
foreach ($bdsLogs as $msg) {
log_message('info', '[BDS Entry] ' . $msg);
}
}
// $policyData = $this->PolicyModel->select('partner_policy.*,Q.insurer_id,Q.insurer_branch_id,E.name as client_name,E.mobile as client_mobile,E.email as client_email,E.reg_no,E.vehicle_type_id,A.id as agent_id,A.agent_code')
// ->join('partner_quotation Q', 'Q.id = partner_policy.quotation_id AND Q.status = "Accepted"', 'left')
// ->join('partner_enquiry E', 'E.id = partner_policy.enquiry_id', 'left')
// ->join('partner_agent A', 'A.id = partner_policy.agent_id', 'left')
// ->where('partner_policy.id',$newPolicyId)
// ->first();
// $bdsLogs = createBDS($policyData, $newPolicyId);
// if (!empty($bdsLogs)) {
// foreach ($bdsLogs as $msg) {
// log_message('info', '[BDS Entry] ' . $msg);
// }
// }
}

View File

@ -185,3 +185,18 @@ if (!function_exists('format_date_for_client')) {
return null;
}
}
if (!function_exists('generate_uuid')) {
function generate_uuid($version = 4, $format = 'hex')
{
$data = random_bytes(16);
// Set version to 0100
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
// Set bits 6-7 to 10
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
// Output the 36 character UUID.
$uuid = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
return $uuid;
}
}

View File

@ -51,7 +51,9 @@ class EnquiryModel extends Model
P.policy_number,
P.id as policy_id,
P.policy_pdf_file_name,
PB.name as broker_name',
PB.name as broker_name,
pm.id as payment_mode_id ,
pm.value as payment_mode_value'
)
->join('vehicle_type VT', 'VT.id = partner_enquiry.vehicle_type_id', 'left')
->join('partner_agent A', 'A.id = partner_enquiry.agent_id', 'left')
@ -60,6 +62,7 @@ class EnquiryModel extends Model
->join('insurers I', 'I.id = partner_enquiry.insurer_id', 'left')
->join('partner_policy P', 'P.quotation_id = Q.id', 'left')
->join('partner_brokers PB', 'PB.id = partner_enquiry.broker_id', 'left')
->join('partner_payment_mode_master pm', 'pm.id = Q.payment_mode_id', 'left')
->where('partner_enquiry.is_active', 1)
->orderBy('partner_enquiry.created_on', 'ASC');

20
app/Models/JobModel.php Executable file
View File

@ -0,0 +1,20 @@
<?php
namespace App\Models;
use CodeIgniter\Model;
class JobModel extends Model
{
protected $table = 'partner_jobs';
protected $primaryKey = 'id';
protected $allowedFields = [
"id",
"name",
"payload",
"response",
"status",
"run_time",
"uuid"
];
}