nhance_partner_be/app/Controllers/PolicyController.php

2294 lines
104 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Controllers;
use CodeIgniter\RESTful\ResourceController;
use App\Models\PolicyModel;
use App\Models\EnquiryModel;
use App\Models\QuotationModel;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\IOFactory;
class PolicyController extends ResourceController
{
protected $PolicyModel;
protected $QuotationModel;
protected $EnquiryModel;
protected $tier1VehicleTypeMap = [
/* ---------------- Two Wheeler ---------------- */
'two wheeler' => 'Two Wheeler',
'2 wheeler' => 'Two Wheeler',
'2w' => 'Two Wheeler',
'motorcycle' => 'Two Wheeler',
'bike' => 'Two Wheeler',
'scooter' => 'Two Wheeler',
'moped' => 'Two Wheeler',
/* ---------------- Four Wheeler ---------------- */
'four wheeler' => 'Four Wheeler',
'4 wheeler' => 'Four Wheeler',
'4w' => 'Four Wheeler',
'private car' => 'Four Wheeler',
'car' => 'Four Wheeler',
'jeep' => 'Four Wheeler',
/* ---------------- Goods Vehicle ---------------- */
'goods vehicle' => 'Goods Vehicle',
'good vehicle' => 'Goods Vehicle',
'goods carrier' => 'Goods Vehicle',
'goods carrying vehicle' => 'Goods Vehicle',
'cargo vehicle' => 'Goods Vehicle',
'lorry' => 'Goods Vehicle',
'truck' => 'Goods Vehicle',
'transport vehicle goods'=> 'Goods Vehicle',
/* ---------------- Commercial Vehicle ---------------- */
'commercial vehicle' => 'Commercial Vehicle',
'commercial car' => 'Commercial Vehicle',
'taxi' => 'Commercial Vehicle',
'cab' => 'Commercial Vehicle',
'hire vehicle' => 'Commercial Vehicle',
'passenger commercial' => 'Commercial Vehicle',
/* ---------------- Bus ---------------- */
'bus' => 'Bus',
'passenger bus' => 'Bus',
'school bus' => 'Bus',
'private bus' => 'Bus',
'stage carriage' => 'Bus',
/* ---------------- Tractor ---------------- */
'tractor' => 'Tractor',
'agricultural tractor' => 'Tractor',
'farm tractor' => 'Tractor',
];
protected $fuelType = [
'Petrol' => 'Petrol','Diesel' => 'Diesel','CNG' => 'CNG','LPG' => 'LPG',
];
public function __construct()
{
$this->PolicyModel = new PolicyModel();
$this->QuotationModel = new QuotationModel();
$this->EnquiryModel = new EnquiryModel();
}
// List policies
public function policyList()
{
try {
$manager_id = $this->request->getGet('manager_id');
$data = $this->PolicyModel ->where('manager_id',$manager_id)->findAll();
return $this->respond(['status' => 'success', 'code' => 200, 'data' => $data], 200);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
}
}
// Find single policy
public function findPolicy()
{
try {
$id = $this->request->getGet('id');
$record = $this->PolicyModel->select('partner_policy.* , ipti.insurance_plan_type,pq.insurance_plan_type_id,pq.insurer_id, i.name as insurer_name,i.short_name as insurer_short_name,Q.payment_mode_id,A.retention_rate as agent_retention_rate,S.retention_rate as manager_retention_rate, pe.vehicle_type_id as enquiry_vehicle_type_id_for_commission,pe.broker_id as enquiry_broker_id_for_commission, pe.reg_no as enquiry_reg_no_for_commission,pe.reg_no as enquiry_reg_no_for_commission')
->select('a.agent_code,a.name as agent_name')
->join('partner_quotation pq', 'pq.id = partner_policy.quotation_id', 'left')
->join('partner_enquiry pe', 'pe.id = partner_policy.enquiry_id', 'left')
->join('partner_quotation Q', 'Q.enquiry_id = pe.id AND Q.status = "Accepted" ', 'left')
->join('partner_insurance_plan_type_master ipti', 'ipti.id = pq.insurance_plan_type_id', 'left')
->join('partner_agent A', 'A.id = partner_policy.agent_id', 'left')
->join('partner_staff S', 'S.id = partner_policy.manager_id', 'left')
->join('insurers i', 'i.id = pq.insurer_id', 'left')
->join('partner_agent a', 'a.id = pe.agent_id', 'left')
->find((int)$id);
$record['issued_date'] = format_date_for_client($record['issued_date']);
$record['start_date'] = format_date_for_client($record['start_date']);
$record['end_date'] = format_date_for_client($record['end_date']);
if (!$record) {
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => []], 200);
}
return $this->respond(['status' => 'success', 'code' => 200, 'data' => $record], 200);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
}
}
// Create policy
public function createPolicy()
{
try {
$data = $this->request->getPost();
$pdfFileName = policy_upload_pdf($this->request->getFile('policy_pdf_file_name'));
$receiptFileName = policy_upload_receipt($this->request->getFile('policy_payment_receipt_file_name'));
//fetch enquiry_id & agent_id
$quotData = $this->QuotationModel->select('partner_quotation.*,E.agent_id')
->join('partner_enquiry E', 'E.id = partner_quotation.enquiry_id', 'left')
->where('partner_quotation.id',$data['quotation_id'])
->first();
$insertData = [
'enquiry_id' => $quotData['enquiry_id'],
'quotation_id' => $data['quotation_id'],
'insured_name' => $data['insured_name'],
'issued_date' => format_date_for_database($data['issued_date']),
'start_date' => format_date_for_database($data['start_date']),
'end_date' => format_date_for_database($data['end_date']),
'premium_amount' => $data['premium_amount'],
'policy_number' => $data['policy_number'],
'payment_mode' => $data['payment_mode'],
'manager_id' => $data['manager_id'],
'agent_id' => $quotData['agent_id'],
'policy_pdf_file_name' => $pdfFileName,
'policy_payment_receipt_file_name' => $receiptFileName,
// newly added fields
'tp' => $data['tp'] ?? null,
'od' => $data['od'] ?? null,
'pa' => $data['pa'] ?? null,
'cgst' => $data['cgst'] ?? null,
'sgst' => $data['sgst'] ?? null,
'igst' => $data['igst'] ?? null,
'rto_state_code' => $data['rto_state_code'] ?? null,
'rto_city_code' => $data['rto_city_code'] ?? null,
'weight' => $data['weight'] ?? null,
'fuel_type' => $data['fuel_type'] ?? null,
'date_of_registration' => !empty($data['date_of_registration']) ? format_date_for_database($data['date_of_registration']) : null,
'year_of_manufacture' => $data['year_of_manufacture'] ?? null,
'engine_no' => $data['engine_no'] ?? null,
'chassis_no' => $data['chassis_no'] ?? null,
'make' => $data['make'] ?? null,
'model' => $data['model'] ?? null,
'cubic_capacity' => $data['cubic_capacity'] ?? null,
'vehicle_type' => $data['vehicle_type'] ?? null,
'created_by' => $data['created_by']
];
$this->PolicyModel->insert($insertData);
$policyId = $this->PolicyModel->getInsertID();
//update enquiry status
$quotationData = $this->QuotationModel->where('id',$data['quotation_id'])->first();
if (!empty($quotationData)){
$enquiryArray = ['vehicle_type_id' => $data['vehicle_type_id'] ?? 0 , 'broker_id' => $data['broker_id'] ?? 0 , 'status'=> 'Policy Created' , 'enquiry_status' => 'Completed' ];
$this->EnquiryModel->update($quotationData['enquiry_id'], $enquiryArray );
}
// 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,partner_policy.insured_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);
// }
// }
// trigger_email('policy_created', ['policy_id' => $policyId]);
return $this->respond(['status' => 'success', 'code' => 200, 'data' => []], 200);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
}
}
// Update policy
public function updatePolicy()
{
try {
$data = $this->request->getJSON(true);
if (!isset($data['id'])) {
return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'ID Required'], 200);
}
$id = $data['id'];
$policy = $this->PolicyModel->find((int)$id);
if (!$policy) {
return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'Data Not Found'], 200);
}
// Check rc_no first, then fallback to enquiry_reg_no_for_commission
// Priority 1: New Form Input ($data)
// Priority 2: Existing Policy Record ($policy)
// Priority 3: Original Enquiry Record ($enquiry_reg_no)
$final_reg_no = !empty($data['rc_no'])
? $data['rc_no']
: (!empty($policy['rc_no'])
? $policy['rc_no']
: ($data['enquiry_reg_no_for_commission'] ?? null));
$final_payment_mode = !empty($data['payment_mode'])
? $data['payment_mode']
: (!empty($policy['payment_mode'])
? $policy['payment_mode']
: null);
$updateData = [
'rc_no' => $final_reg_no,
'insured_name' => $data['insured_name'] ?? $policy['insured_name'],
'issued_date' => format_date_for_database($data['issued_date']),
'start_date' => format_date_for_database($data['start_date']),
'end_date' => format_date_for_database($data['end_date']),
'premium_amount' => $data['premium_amount'] ?? $policy['premium_amount'],
'policy_number' => $data['policy_number'] ?? $policy['policy_number'],
'payment_mode' => $final_payment_mode,
'tp' => $data['tp'] ?? null,
'od' => $data['od'] ?? null,
'pa' => $data['pa'] ?? null,
'cgst' => $data['cgst'] ?? null,
'sgst' => $data['sgst'] ?? null,
'igst' => $data['igst'] ?? null,
'rto_state_code' => $data['rto_state_code'] ?? null,
'rto_city_code' => $data['rto_city_code'] ?? null,
'weight' => $data['weight'] ?? null,
'fuel_type' => $data['fuel_type'] ?? null,
'date_of_registration' => !empty($data['date_of_registration']) ? format_date_for_database($data['date_of_registration']) : null,
'year_of_manufacture' => $data['year_of_manufacture'] ?? null,
'engine_no' => $data['engine_no'] ?? null,
'chassis_no' => $data['chassis_no'] ?? null,
'make' => $data['make'] ?? null,
'model' => $data['model'] ?? null,
'cubic_capacity' => $data['cubic_capacity'] ?? null,
'vehicle_type' => $data['vehicle_type'] ?? null,
'broker_name' => $data['broker_name'] ?? null,
'commission_amount' => $data['commission_amount'] ?? null,
'updated_by' => $data['updated_by'] ?? null,
'is_data_accuracy_checked'=> 1
];
$EnquiryupdateData = [];
if (!empty($data['enquiry_broker_id_for_commission'])) {
$EnquiryupdateData['broker_id'] = $data['enquiry_broker_id_for_commission'];
}
if (!empty($data['enquiry_vehicle_type_id_for_commission'])) {
$EnquiryupdateData['vehicle_type_id'] = $data['enquiry_vehicle_type_id_for_commission'];
}
if ($final_reg_no) {
$EnquiryupdateData['reg_no'] = $final_reg_no;
}
if (!empty($data['insured_name'])) {
$EnquiryupdateData['name'] = $data['insured_name'];
}
if (!empty($EnquiryupdateData)) {
$policy = $this->PolicyModel
->select('enquiry_id')
->where('id', $id)
->first();
if (!empty($policy['enquiry_id'])) {
$EnquiryupdateData['updated_by'] = $data['updated_by'];
$db = \Config\Database::connect();
$db->table('partner_enquiry')
->where('id', $policy['enquiry_id'])
->update($EnquiryupdateData);
}
}
$partnerQuotationUpdateData = [];
if (!empty($data['insurance_plan_type_id'])) {
$partnerQuotation = $this->PolicyModel
->select('quotation_id')
->where('id', $id)
->first();
$partnerQuotationUpdateData['insurance_plan_type_id'] = $data['insurance_plan_type_id'];
if ($final_payment_mode) { $partnerQuotationUpdateData['payment_mode_id'] = $final_payment_mode; }
if (!empty($partnerQuotationUpdateData)) {
$partnerQuotationUpdateData['updated_by'] = $data['updated_by'];
$db = \Config\Database::connect();
$db->table('partner_quotation')
->where('id', $partnerQuotation['quotation_id'])
->update($partnerQuotationUpdateData);
}
// $updatedQuotation = $db->table('partner_quotation')
// ->where('id', $partnerQuotation['quotation_id'])
// ->get()
// ->getRowArray();
}
$policyPdf = policy_upload_pdf($this->request->getFile('policy_pdf_file_name'));
if ($policyPdf !== null) {
$updateData['policy_pdf_file_name'] = $policyPdf;
}
$policyReceipt = policy_upload_receipt($this->request->getFile('policy_payment_receipt_file_name'));
if ($policyReceipt !== null) {
$updateData['policy_payment_receipt_file_name'] = $policyReceipt;
}
$this->PolicyModel->update($id, $updateData);
// 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,partner_policy.insured_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,E.broker_id')
->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',$id)
->first();
if($policyData['broker_id'] == 1) //only for nhance
{
// $bdsLogs = createBDS($policyData, $id);
// if (!empty($bdsLogs)) {
// foreach ($bdsLogs as $msg) {
// log_message('info', '[BDS Entry] ' . $msg);
// }
// }
}
return $this->respond(['status' => 'success', 'code' => 200, 'data' => []], 200);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
}
}
public function updatePolicyCommission()
{
try {
$data = $this->request->getJSON(true);
if (empty($data['id'])) {
return $this->respond([
'status' => 'failed',
'code' => 422,
'data' => 'Policy id is required',
], 422);
}
if (!isset($data['commission_amount']) || $data['commission_amount'] === '') {
return $this->respond([
'status' => 'failed',
'code' => 422,
'data' => 'commission_amount is required',
], 422);
}
$policyId = (int) $data['id'];
$policy = $this->PolicyModel->find($policyId);
if (!$policy) {
return $this->respond([
'status' => 'failed',
'code' => 404,
'data' => 'Policy not found',
], 404);
}
$commission = (float) $data['commission_amount'];
if ($commission < 0) {
return $this->respond([
'status' => 'failed',
'code' => 422,
'data' => 'commission_amount must be positive',
], 422);
}
/*
* Commission-only update endpoint.
* Keeps the existing policy workflow unchanged and updates only required fields.
*/
$updateData = [
'commission_amount' => number_format($commission, 2, '.', ''),
'updated_by' => $data['updated_by'] ?? null,
'updated_on' => date('Y-m-d H:i:s'),
];
if (!$this->PolicyModel->update($policyId, $updateData)) {
return $this->respond([
'status' => 'failed',
'code' => 422,
'data' => $this->PolicyModel->errors(),
], 422);
}
return $this->respond([
'status' => 'success',
'code' => 200,
'data' => [
'policy_id' => (string) $policyId,
'commission_amount' => $updateData['commission_amount'],
'message' => 'Commission updated successfully',
],
], 200);
} catch (\Exception $e) {
return $this->respond([
'status' => 'failed',
'code' => 500,
'data' => $e->getMessage(),
], 500);
}
}
public function uploadPolicyFile()
{
try {
$data = $this->request->getPost();
$pdfFileName = policy_upload_pdf($this->request->getFile('policy_pdf_file_name'));
//fetch enquiry_id & agent_id
$quotData = $this->QuotationModel->select('partner_quotation.*,E.agent_id,E.name , VT.vehicle_type')
->join('partner_enquiry E', 'E.id = partner_quotation.enquiry_id', 'left')
->join('partner_agent A', 'A.id = E.agent_id', 'left')
->join('vehicle_type VT', 'VT.id = E.vehicle_type_id', 'left')
->where('partner_quotation.id',$data['quotation_id'])
->first();
if (!$quotData) {
return $this->respond(['status' => 'failed', 'code' => 404, 'message' => 'Quotation not found'], 404);
}
// ===== CHECK EXISTING POLICY =====
$policy = $this->PolicyModel
->where('enquiry_id', $quotData['enquiry_id'])
->first();
if ($policy)
{
$updateData = [
'policy_pdf_file_name' => $pdfFileName,
'updated_by' => $data['updated_by']
];
$this->PolicyModel->update($data['id'], $updateData);
$policyId = $data['id'];
Jobs::addJob(['job_name' => 'readFileAndCalculateCommission','payload' => ['policy_id' => $policyId]]);
log_message("info",'readFileAndCalculateCommission job pushed');
return $this->respond(['status' => 'success', 'code' => 200, 'data' => $policyId ], 200);
}
if (!isset($data['id'])) {
$insertData = [
'enquiry_id' => $quotData['enquiry_id'],
'quotation_id' => $data['quotation_id'],
'insured_name' => $quotData['name'],
'manager_id' => $data['manager_id'],
'agent_id' => $quotData['agent_id'],
'vehicle_type' => $quotData['vehicle_type'],
'policy_pdf_file_name' => $pdfFileName,
'created_by' => $data['created_by']
];
$policyId = $this->PolicyModel->insert($insertData);
//update enquiry status
$quotationData = $this->QuotationModel->where('id',$data['quotation_id'])->first();
if (!empty($quotationData)){
$enquiryArray = ['status'=> 'Policy Created' , 'enquiry_status' => 'Completed' ];
$this->EnquiryModel->update($quotationData['enquiry_id'], $enquiryArray );
}
Jobs::addJob(['job_name' => 'readFileAndCalculateCommission','payload' => ['policy_id' => $policyId]]);
log_message("info",'readFileAndCalculateCommission job pushed');
}else{
$updateData = [
'policy_pdf_file_name' => $pdfFileName,
'updated_by' => $data['updated_by']
];
$this->PolicyModel->update($data['id'], $updateData);
$policyId = $data['id'];
Jobs::addJob(['job_name' => 'readFileAndCalculateCommission','payload' => ['policy_id' => $policyId]]);
log_message("info",'readFileAndCalculateCommission job pushed');
}
return $this->respond(['status' => 'success', 'code' => 200, 'data' => $policyId ], 200);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
}
}
// Download policy file
public function downloadPolicyFile()
{
try {
$policyId = $this->request->getGet('policy_id');
$fileType = $this->request->getGet('file_type'); // policy_pdf , policy_payment_receipt
if (!$policyId || !$fileType) {
return $this->respond(['status' => 'failed','code' => 400,'data' => 'policy_id and file_type are required'], 200);
}
// Map file_type to DB column and folder
$fileMap = policy_file_type_map();
if (!array_key_exists($fileType, $fileMap)) {
return $this->respond(['status' => 'failed','code' => 400,'data' => 'Invalid file_type'], 200);
}
$fileColumn = $fileMap[$fileType]['column'];
// Fetch record from DB
$fileRecord = $this->PolicyModel->where('is_active', 1)->find((int)$policyId);
if (!$fileRecord || empty($fileRecord[$fileColumn])) {
return $this->respond([ 'status' => 'failed','code' => 404,'data' => 'File not found in database'], 200);
}
if (!policy_exists_by_type($fileRecord[$fileColumn], $fileType)) {
return $this->respond(['status' => 'failed','code' => 404,'data' => 'File missing on server'], 200);
}
return policy_download_by_type($fileRecord[$fileColumn], $fileType);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed','code' => 500,'message' => $e->getMessage() ], 500);
}
}
public function PolicyFilePath()
{
try {
$policyId = $this->request->getGet('policy_id');
$fileType = $this->request->getGet('file_type'); // policy_pdf , policy_payment_receipt
if (!$policyId || !$fileType) {
return $this->respond(['status' => 'failed','code' => 400,'data' => 'policy_id and file_type are required'], 200);
}
// Map file_type to DB column and folder
$fileMap = policy_file_type_map();
if (!array_key_exists($fileType, $fileMap)) {
return $this->respond(['status' => 'failed','code' => 400,'data' => 'Invalid file_type'], 200);
}
$fileColumn = $fileMap[$fileType]['column'];
// Fetch record from DB
$fileRecord = $this->PolicyModel->where('is_active', 1)->find((int)$policyId);
if (!$fileRecord || empty($fileRecord[$fileColumn])) {
return $this->respond([ 'status' => 'failed','code' => 404,'data' => 'File not found in database'], 200);
}
if (!policy_exists_by_type($fileRecord[$fileColumn], $fileType)) {
return $this->respond(['status' => 'failed','code' => 404,'data' => 'File missing on server'], 200);
}
return policy_download_by_type($fileRecord[$fileColumn], $fileType, true);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed','code' => 500,'message' => $e->getMessage() ], 500);
}
}
public function readFile()
{
$readDoc = $this->checkPolicyDoc(15862);
dd($readDoc);
}
public function readFileAndCalculateCommission(array $data)
{
$policyId = $data['policy_id'];
log_message('debug', 'checkPolicyDoc() started for policy_id: '.$policyId);
$readDoc = $this->checkPolicyDoc($policyId);
if($readDoc['status'] == 'success')
{
$data = $readDoc['data'];
$vehicleType = $data['vehicle']['vehicle_type'] ?? null;
$fuelType = $data['vehicle']['fuel_type'] ?? null;
if($vehicleType !== null)
{
$vehicleType = $this->postProcessGeminiData($vehicleType,$this->tier1VehicleTypeMap);
}
else
{
$vehicleType = 'UN_IDEN_DOC';
}
if($fuelType !== null)
{
$fuelType = $this->postProcessGeminiData($fuelType,$this->fuelType);
}
else
{
$fuelType = 'UN_IDEN_DOC';
}
//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['broker_name'] = $data['policy']['intermediary_name'] ?? 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'] = $fuelType;
$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['product'] = $vehicleType;
$policyData['rc_no'] = $data['vehicle']['reg_no'] ?? null;
$policyData['insured_name'] = $data['insured']['name'] ?? null;
$policyData['enquiry_vehicle_type_id_for_commission'] = $data['insured']['name'] ?? null;
$policyData['enquiry_broker_id_for_commission'] = $data['insured']['name'] ?? null;
// ->join('partner_enquiry pe', 'pe.id = partner_policy.enquiry_id', 'left')
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));
}
return ['checkPolicyDoc' => $readDoc['status'] , 'calculateCommission' => $calculateCommission['status'] ?? 'failed' ];
}
public function checkPolicyDoc($policyId = null , $return = null)
{
try
{
helper('policy_pdf_helper');
// Replace with your actual Gemini API key
$apiKey = getenv('GEMINI_API_KEY');
// The model to use and the API endpoint
$model = "gemini-pro";
$model = getenv('GEMINI_MODEL');
// $model = "gemini-1.5-pro";
$url = "https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key={$apiKey}";
$record = $this->PolicyModel->find((int)$policyId);
$pdfFileName = $record['policy_pdf_file_name'] ?? '';
$mdFileName = policy_md_file_name($pdfFileName);
if ($pdfFileName === '' || !policy_exists_by_type($pdfFileName, 'policy_pdf')) {
$missingPath = policy_local_pdf_path($pdfFileName);
log_message('info',"Error: File not found at {$missingPath}");
if($return == true)
{
return $this->respond(['status'=>"failed", 'message'=> "File not found at {$missingPath}"], 200);
}else{
return ['status'=>"failed", 'message'=> "File not found at {$missingPath}"];
}
}
$pdfFilePath = policy_local_pdf_path($pdfFileName);
$markdownResult = convert_policy_pdf_to_markdown($pdfFilePath);
if ($markdownResult['status'] !== 'success') {
log_message('info', 'Policy PDF to markdown conversion failed: ' . $markdownResult['message']);
if ($return == true) {
return $this->respond(['status' => 'failed', 'message' => $markdownResult['message']], 200);
}
return ['status' => 'failed', 'message' => $markdownResult['message']];
}
if (!policy_md_exists($mdFileName)) {
log_message('info',"Error: Markdown file not found for {$mdFileName}");
if($return == true)
{
return $this->respond(['status'=>"failed", 'message'=> "Markdown file not found for {$mdFileName}"], 200);
}else{
return ['status'=>"failed", 'message'=> "Markdown file not found for {$mdFileName}"];
}
}
$fileContent = $markdownResult['content'] ?? policy_read_md($mdFileName);
$mimeType = 'text/markdown';
// Define supported inline MIME types
$supportedInlineMimeTypes = ['application/pdf', 'text/csv', 'text/plain', 'text/markdown', 'text/x-markdown'];
$prompt = "Read the following motor policy document and convert into JSON format as sample specified. Give me only JSON,not any explanations.while pick up premium break up give own damage, third party ,personal accident and taxes as mentioned in JSON format.Notes:vehicle_type should be like Two Wheeler,Four Feeler,Good Vehicle,Bus, Tractor,Commercial Vehicle.rto_state_code is first two char of reg_no and rto_city_code 3rd & 4th char of reg_no and it should be two digit numeric code. Any date should be in mysql date format.(note : while fetching TP premium do not add PA AMOUNT)";
$prompt .= '"{\"policy\":{\"intermediary_name\":\"\",\"policy_number\":\"\",\"issue_date\":\"\",\"period\":{\"start\":\"\",\"end\":\"\"},\"insurer\":\"\",\"previous_policy_number\":\"\"},\"insured\":{\"name\":\"\",\"father_name\":\"\",\"address\":[\"\"],\"mobile\":\"\",\"id_proofs\":{\"aadhaar\":\"\",\"pan\":\"\"}},\"vehicle\":{\"reg_no\":\"\",\"rto_state_code\":\"\",\"rto_city_code\":\"\",\"weight\":\"\",\"fuel_type\":\"\",\"date_of_registration\":\"\",\"year_of_manufacture\":\"\",\"engine_no\":\"\",\"chassis_no\":\"\",\"make\":\"\",\"model\":\"\",\"year\":\"\",\"cubic_capacity\":\"\",\"vehicle_type\":\"\"},\"rto\":\"\",\"premium\":{\"tp\":0,\"od\":0,\"pa\":0,\"taxes\":{\"cgst\":\"\",\"sgst\":\"\",\"igst\":\"\"},\"total\":0,\"in_words\":\"\"},\"endorsements\":[{\"code\":\"\",\"desc\":\"\"}]}"';
$payloadPart = null;
// The text part of the prompt
$text_part = [
"text" => $prompt
];
// Conditionally handle the file upload based on MIME type
if (in_array($mimeType, $supportedInlineMimeTypes)) {
if ($fileContent === '' || trim($fileContent) === '') {
log_message('info', "Markdown file is empty for {$mdFileName}");
if ($return == true) {
return $this->respond(['status' => 'failed', 'message' => "Markdown file is empty for {$mdFileName}"], 200);
}
return ['status' => 'failed', 'message' => "Markdown file is empty for {$mdFileName}"];
}
$base64Content = base64_encode($fileContent);
$payloadPart = [
"inlineData" => [
"mimeType" => $mimeType,
"data" => $base64Content
]
];
$content_parts = [
"parts" => [
$text_part,
$payloadPart
]
];
} else {
log_message('info', "Unsupported inline MIME type ({$mimeType}) for markdown file.");
if($return == true)
{
return $this->respond(['status'=>"failed", 'message'=> "Unsupported inline MIME type ({$mimeType})"], 200);
}else{
return ['status'=>"failed", 'message'=> "Unsupported inline MIME type ({$mimeType})"];
}
}
// The final JSON payload
$data = [
"contents" => [
$content_parts
]
];
// Encode the data to a JSON string
$json_data = json_encode($data);
// Initialize cURL
$ch = curl_init($url);
// Set cURL options
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); // Set the Content-Type header
curl_setopt($ch, CURLOPT_POSTFIELDS, $json_data); // Set the JSON payload
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Return the response as a string
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Optional: to bypass SSL verification if needed (not recommended for production)
// Execute the cURL request and get the response
$response = curl_exec($ch);
// Check for cURL errors
if (curl_errno($ch)) {
$curl_error = curl_error($ch);
log_message('info',"cURL Error: " . $curl_error);
if($return == true)
{
return $this->respond(['status'=>"failed", 'message'=> "cURL Error: " . $curl_error], 200);
}else{
return ['status'=>"failed", 'message'=> "cURL Error: " . $curl_error];
}
}
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// Close the cURL handle
curl_close($ch);
$ch = null; // Mark handle as closed
// Check for non-successful HTTP status codes
if ($http_code < 200 || $http_code >= 300) {
log_message('info',"API returned non-successful HTTP status code: $http_code. Response: " . substr($response, 0, 200));
if($return == true)
{
return $this->respond(['status'=>"failed", 'message'=> "API returned non-successful HTTP status code: $http_code. Response: " . substr($response, 0, 200)], 200);
}else{
return ['status'=>"failed", 'message'=> "API returned non-successful HTTP status code: $http_code. Response: " . substr($response, 0, 200)];
}
}
// Decode the JSON response
$responseData = json_decode($response, true, 512, JSON_THROW_ON_ERROR);
$text_path = $responseData['candidates'][0]['content']['parts'][0]['text'] ?? null;
if ($text_path) {
$generatedText = $text_path;
log_message('info', "Successfully received generated text.");
// 9. Attempt to extract and decode the exact JSON from the generated text
$json_string = '';
// Preferred: Regex to find content inside a JSON markdown block: ```json\n(.*?)\n```
$extracted_json_match = [];
if (preg_match('/```json\s*(.*?)\s*```/s', $generatedText, $extracted_json_match)) {
$json_string = trim($extracted_json_match[1]);
} else {
// Fallback: Find the first '{' and the last '}'
$start = strpos($generatedText, '{');
$end = strrpos($generatedText, '}');
if ($start !== false && $end !== false && $end > $start) {
$json_string = substr($generatedText, $start, $end - $start + 1);
}
}
if (empty($json_string)) {
log_message('info',"Could not extract a valid JSON string from the generated text.");
if($return == true)
{
return $this->respond(['status'=>"failed", 'message'=> "Could not extract a valid JSON string from the generated text."], 200);
}else{
return ['status'=>"failed", 'message'=> "Could not extract a valid JSON string from the generated text."];
}
}
// Decode the extracted JSON string
$final_data = json_decode($json_string, true, 512, JSON_THROW_ON_ERROR);
log_message('info', "Extracted and decoded final JSON data successfully.");
if($return == true)
{
return $this->respond(['status'=>"success", 'message'=> "Doc read sucess" , 'data'=>$final_data], 200);
}else{
return ['status'=>"success", 'message'=> "Doc read sucess" , 'data'=>$final_data];
}
} else {
log_message('info',"Response structure invalid or no generated text candidate found.");
if($return == true)
{
return $this->respond(['status'=>"failed", 'message'=> "Response structure invalid or no generated text candidate found"], 200);
}else{
return ['status'=>"failed", 'message'=> "Response structure invalid or no generated text candidate found"];
}
}
} catch (\Exception $e) {
// if ($ch !== null) {
// curl_close($ch);
// }
log_message('error', "Fatal Error: " . $e->getMessage());
if($return == true)
{
return $this->respond(['status'=>"failed", 'message'=> "Error: " . $e->getMessage()], 200);
}else{
return ['status'=>"failed", 'message'=> "Error: " . $e->getMessage()];
}
}
}
public function calculateCommission($policyId = null , $return = null)
{
log_message('debug', 'calculateCommission() started with policyId: ' . $policyId);
$record = $this->PolicyModel->select('partner_policy.* , rto_state.rto_state_name as rto_state_code ,pe.insurer_id , VT.vehicle_type as vehicle_type_master , ipti.insurance_plan_type,A.retention_rate as agent_retention_rate,S.retention_rate as manager_retention_rate')
->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')
->join('rto_master rto_state', 'rto_state.rto_state = partner_policy.rto_state_code', 'left')
// ->join('rto_master rto_city', 'rto_city.rto_code = partner_policy.rto_city_code', 'left')
->join('partner_agent A', 'A.id = partner_policy.agent_id', 'left')
->join('partner_staff S', 'S.id = partner_policy.manager_id', 'left')
->where('partner_policy.id', $policyId)
->first();
$apiUrl = getenv('COMMISSION_CALCULATION_URL');
$token = getenv('COMMISSION_CALCULATION_TOKEN');
$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_capacity" => $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);
if($return == true)
{
return $this->respond( ['status'=>"failed", 'message'=> 'cURL Error: ' . $error], 200);
}else{
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($return == true)
{
return $this->respond(['status' => 'success', 'code' => 200, 'data' => $result , 'payload' => $postData ], 200);
}
if (!empty($result) && isset($result['success']) && $result['success'] === true) {
$actualCommission = $result['data']['payout'];
$agentRetention = $record['agent_retention_rate'];
$managerRetention = $record['manager_retention_rate'];
$commissionDeduction = $actualCommission * ($agentRetention+$managerRetention) / 100;
$commission = $actualCommission - $commissionDeduction;
// Safely extract values
$policyData = [
'commission_from_insurer' => $result['data']['payout'] ?? null,
'agent_retention_rate' => $agentRetention,
'manager_retention_rate' => $managerRetention,
'commission_amount' => $commission,
'commission_applied_rule' => $result['data']['rule']['id'] ?? null,
];
log_message('debug', 'Updating Policy (ID=75) With: ' . json_encode($policyData));
if($return == true)
{
return $this->respond(['status' => 'success', 'code' => 200, 'data' => $policyData ], 200);
}else{
// 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');
if($return == true)
{
return $this->respond( ['status'=>"failed", 'message'=> "Invalid API Response!"], 200);
}else{
return ['status'=>"failed", 'message'=> "Invalid API Response!"];
}
}
}
}
public function policyCommissionCalculationAndGeminiPolicyRead()
{
$type = $this->request->getGet('type');
$policyId = $this->request->getGet('policy_id');
if($type == 'read')
{
$readDoc = $this->checkPolicyDoc($policyId , false);
if($readDoc['status'] == 'success')
{
$data = $readDoc['data'];
$vehicleType = $data['vehicle']['vehicle_type'] ?? null;
$fuelType = $data['vehicle']['fuel_type'] ?? null;
if($vehicleType !== null)
{
$vehicleType = $this->postProcessGeminiData($vehicleType,$this->tier1VehicleTypeMap);
}
else
{
$vehicleType = 'UN_IDEN_DOC';
}
if($fuelType !== null)
{
$fuelType = $this->postProcessGeminiData($fuelType,$this->fuelType);
}
else
{
$fuelType = 'UN_IDEN_DOC';
}
//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['broker_name'] = $data['policy']['intermediary_name'] ?? 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'] = $fuelType;
$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['product'] = $vehicleType;
$policyData['rc_no'] = $data['vehicle']['reg_no'] ?? null;
$policyData['insured_name'] = $data['insured']['name'] ?? null;
log_message('debug', 'Policy Update Payload: ' . json_encode($policyData));
$updateStatus = $this->PolicyModel->update($policyId,$policyData);
} else {
log_message('error', 'checkPolicyDoc returned failure: ' . json_encode($readDoc));
}
return $this->respond(['status'=>$readDoc['status'], 'data'=> $readDoc ], 200);
}else if($type == 'commission')
{
return $this->calculateCommission($policyId , true);
}
}
public function searchThePolicies()
{
try{
$policy_number = $this->request->getGet('policy_number');
$vehicle_no = $this->request->getGet('vehicle_number');
if ($policy_number == '' && $vehicle_no == '') {
return $this->respond([
'status' => 'failed',
'code' => 200,
'data' => 'Value Required'
], 200);
}
$builder = $this->PolicyModel->select('partner_policy.*,
partner_policy.agent_id as agent_id,
pa.name as agent_name,
pe.broker_id as broker_id,
pb.name as broker_name,
pq.insurer_id as insurer_id,
i.name as insurer_name,
i.short_name as insurer_short_name');
$builder->join('partner_enquiry pe', 'partner_policy.enquiry_id = pe.id', 'left');
$builder->join('partner_agent pa', 'pa.id = partner_policy.agent_id', 'left');
$builder->join('partner_brokers pb', 'pb.id = pe.broker_id', 'left');
$builder->join('partner_quotation pq', 'pq.id = partner_policy.quotation_id', 'left');
$builder->join('insurers i', 'i.id = pq.insurer_id', 'left');
if ($policy_number) {
$builder->like('partner_policy.policy_number', $policy_number);
} elseif ($vehicle_no) {
$builder->like('pe.reg_no', $vehicle_no);
} else {
$builder->groupStart()
->like('partner_policy.policy_number', $policy_number)
->orLike('pe.reg_no', $vehicle_no)
->groupEnd();
}
$result = $builder->get()->getResultArray();
if (!$result) {
return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'No Data Found'], 200);
}
return $this->respond(['status' => 'success', 'code' => 200, 'data' => $result], 200);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
}
}
public function postProcessGeminiData(string $incoming, array $masterArray): string
{
$value = strtolower(trim($incoming));
$value = preg_replace('/\s+/', ' ', $value); // normalize spaces
return $masterArray[$value] ?? 'UN_IDEN_TYPE';
}
public function calculateCommissionRequest()
{
$postRequest = $this->request->getJSON(true);
$apiUrl = getenv('COMMISSION_CALCULATION_URL');
$token = getenv('COMMISSION_CALCULATION_TOKEN');
$manufactureYear = (int) $postRequest['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" => $postRequest['vehicle_type'],
"policy_type" => $postRequest['insurance_plan_type'],
"premium" => $postRequest['premium_amount'],
"od_premium" => $postRequest['od'],
"tp_premium" => $postRequest['tp'],
"cubic_capacity" => $postRequest['cubic_capacity'],
"weight" => $postRequest['weight'],
"make" => $postRequest['make'],
"model" => $postRequest['model'],
"manufacture_year" => $postRequest['year_of_manufacture'],
"vehicle_age" => $vehicleAge,
"date_of_registration" => $postRequest['date_of_registration'],
"fuel_type" => $postRequest['fuel_type'],
"geo_rto_state" => $postRequest['rto_state_code'],
"geo_rto_city" => $postRequest['rto_city_code'],
"policy_issue_date" => $postRequest['issued_date'],
"insurer_id" => $postRequest['insurer_id'],
"payment_mode" => $postRequest['payment_mode']
];
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 $this->respond( ['status'=>"failed", 'message'=> 'cURL Error: ' . $error], 200);
} else {
log_message('debug', 'API Response: ' . $response);
$result = json_decode($response, true);
if (!empty($result) && isset($result['success']) && $result['success'] === true) {
$actualCommission = $result['data']['payout'];
$agentRetention = $postRequest['agent_retention_rate'];
$managerRetention = $postRequest['manager_retention_rate'];
$commissionDeduction = $actualCommission * ($agentRetention+$managerRetention) / 100;
$commission = $actualCommission - $commissionDeduction;
// Safely extract values
$policyData = [
'commission_from_insurer' => $result['data']['payout'] ?? null,
'agent_retention_rate' => $agentRetention,
'manager_retention_rate' => $managerRetention,
'commission_amount' => $commission,
'commission_applied_rule' => $result['data']['rule']['id'] ?? null,
];
log_message('debug', 'Updating Policy (ID=75) With: ' . json_encode($policyData));
return $this->respond(['status' => 'success', 'code' => 200, 'data' => $policyData ], 200);
} else {
log_message('debug', 'calculateCommission() - Invalid API Response');
return $this->respond( ['status'=>"failed", 'message'=> "Invalid API Response!"], 200);
}
}
}
public function vehicleTypeMaster()
{
// Get only values & make unique
$vehicleTypes = array_values(array_unique($this->tier1VehicleTypeMap));
return $this->respond([
'status' => 'success',
'data' => $vehicleTypes
], 200);
}
public function fuelTypeMaster()
{
$fuelTypes = array_values(array_unique($this->fuelType));
return $this->respond([
'status' => 'success',
'data' => $fuelTypes
], 200);
}
public function softdelete()
{
try {
$policyId = (int) $this->request->getGet('policy_id');
if (!$policyId) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'policy_id is required'
], 400);
}
$policy = $this->PolicyModel->find($policyId);
if (!$policy) {
return $this->respond([
'status' => 'failed',
'code' => 404,
'data' => 'Data Not Found'
], 404);
}
// Start DB transaction
$db = \Config\Database::connect();
$db->transStart();
// Soft delete policy
$this->PolicyModel->update($policyId, ['is_active' => 0]);
// Soft delete quotation
if (!empty($policy['quotation_id'])) {
$this->QuotationModel->update(
(int) $policy['quotation_id'],
['is_active' => 0]
);
}
// Soft delete enquiry
if (!empty($policy['enquiry_id'])) {
$this->EnquiryModel->update(
(int) $policy['enquiry_id'],
['is_active' => 0]
);
}
$db->transComplete();
if ($db->transStatus() === false) {
return $this->respond([
'status' => 'failed',
'code' => 500,
'data' => 'Failed to delete data'
], 500);
}
return $this->respond([
'status' => 'success',
'code' => 200,
'data' => 'Deleted Successfully!'
], 200);
} catch (\Throwable $e) {
return $this->respond([
'status' => 'failed',
'code' => 500,
'message' => $e->getMessage()
], 500);
}
}
/**
* Stream a simple XLSX (title row, header row, data rows) without totals row.
*/
private function streamPendingCommissionExcel(array $header, array $data, string $title, string $fileName): void
{
while (ob_get_level() > 0) {
ob_end_clean();
}
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$columnCount = count($header);
$lastCol = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($columnCount);
$sheet->mergeCells("A1:{$lastCol}1");
$sheet->setCellValue('A1', $title);
$sheet->getStyle('A1')->getFont()->setBold(true)->setSize(14);
$sheet->getStyle('A1')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
$sheet->fromArray($header, null, 'A2');
$sheet->getStyle("A2:{$lastCol}2")->getFont()->setBold(true);
$sheet->getStyle("A2:{$lastCol}2")->getFill()
->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)
->getStartColor()->setARGB('F2F2F2');
if ($data !== []) {
$sheet->fromArray($data, null, 'A3');
}
for ($i = 1; $i <= $columnCount; $i++) {
$col = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($i);
$sheet->getColumnDimension($col)->setAutoSize(true);
}
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header("Content-Disposition: attachment; filename=\"{$fileName}\"");
header('Cache-Control: max-age=0');
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->save('php://output');
exit();
}
/**
* GET: from_date, to_date (optional), agent_code (optional) — at least one filter required.
* Exports policies where commission_amount is NULL or 0 (active policies only).
*/
public function downloadPendingCommissionExcel()
{
try {
$fromDate = $this->request->getGet('from_date');
$toDate = $this->request->getGet('to_date');
$agentCode = $this->request->getGet('agent_code');
$agentCode = $agentCode !== null && $agentCode !== '' ? trim((string) $agentCode) : null;
if ($fromDate === null || $fromDate === '') {
$fromDate = null;
}
if ($toDate === null || $toDate === '') {
$toDate = null;
}
if ($fromDate === null && $toDate === null && $agentCode === null) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'Provide at least one of: from_date, to_date, or agent_code',
], 400);
}
$db = \Config\Database::connect();
$builder = $db->table('partner_policy pp');
$builder->select('pa.agent_code, pp.policy_number');
$builder->join('partner_agent pa', 'pa.id = pp.agent_id', 'left');
$builder->where('pp.is_active', 1);
$builder->groupStart()
->where('pp.commission_amount', null)
->orWhere('pp.commission_amount', 0)
->groupEnd();
if ($fromDate !== null) {
$builder->where('pp.issued_date >=', date('Y-m-d', strtotime((string) $fromDate)));
}
if ($toDate !== null) {
$builder->where('pp.issued_date <=', date('Y-m-d', strtotime((string) $toDate)));
}
if ($agentCode !== null) {
$builder->where('pa.agent_code', $agentCode);
}
$builder->orderBy('pa.agent_code', 'ASC');
$builder->orderBy('pp.policy_number', 'ASC');
$rows = $builder->get()->getResultArray();
$dataRows = [];
foreach ($rows as $row) {
$dataRows[] = [
$row['agent_code'] ?? '',
"`" . ($row['policy_number'] ?? ''), // 👈 force text
'',
];
}
$header = ['Agent Code', 'Policy Number', 'Payout Amount'];
$fromPart = $fromDate !== null ? date('Y-m-d', strtotime((string) $fromDate)) : 'na';
$toPart = $toDate !== null ? date('Y-m-d', strtotime((string) $toDate)) : 'na';
$agentPart = $agentCode !== null
? preg_replace('/[^A-Za-z0-9_-]+/', '_', $agentCode)
: 'all_agents';
$fileName = "commission_pending_{$fromPart}_{$toPart}_{$agentPart}.xlsx";
$title = 'Pending commission payout (fill Payout Amount and re-upload)';
$this->streamPendingCommissionExcel($header, $dataRows, $title, $fileName);
} catch (\Throwable $e) {
return $this->respond([
'status' => 'failed',
'code' => 500,
'message' => $e->getMessage(),
], 500);
}
}
/**
* GET: static sample .xlsx for clients (same columns as uploadCommissionExcel).
* No filters; use downloadPendingCommissionExcel for real pending rows from the database.
*/
public function downloadCommissionUploadSampleExcel()
{
try {
$header = ['Agent Code', 'Policy Number', 'Payout Amount'];
// Example rows — clients replace with real data or delete before upload.
$dataRows = [
["AG001", "`YOUR-POLICY-NUMBER-1", "1500.00"],
["AG002", "`YOUR-POLICY-NUMBER-2", ""],
];
$title = 'Sample: Fill Payout Amount, keep Agent Code matching the policy, upload via policy/uploadCommissionExcel';
$fileName = 'commission_payout_upload_sample.xlsx';
$this->streamPendingCommissionExcel($header, $dataRows, $title, $fileName);
} catch (\Throwable $e) {
return $this->respond([
'status' => 'failed',
'code' => 500,
'message' => $e->getMessage(),
], 500);
}
}
/**
* GET: commission / payout rows with filters. Invoice "raised" = active row in partner_invoice_items for policy_id.
*
* Query: from_date, to_date (issued_date), agent_id (partner_policy.agent_id), payout_raised=yes|no|all
* payout_raised=all: include both invoice-raised and not-raised policies (no EXISTS filter).
* At least one filter required among: from_date, to_date, agent_id, payout_raised.
*
* Response includes summary: total / raised / pending commission sums (commission_amount).
*
* List columns also include: reg_no, weight, fuel_type, vehicle_type, utr_no (from partner_invoice_utr via invoice).
*/
public function commissionPayoutReport()
{
try {
$fromDate = $this->request->getGet('from_date');
$toDate = $this->request->getGet('to_date');
$agentIdRaw = $this->request->getGet('agent_id');
$payoutRaised = $this->request->getGet('payout_raised');
$fromDate = ($fromDate !== null && $fromDate !== '') ? trim((string) $fromDate) : null;
$toDate = ($toDate !== null && $toDate !== '') ? trim((string) $toDate) : null;
$agentId = null;
if ($agentIdRaw !== null && $agentIdRaw !== '') {
$agentId = (int) $agentIdRaw;
}
if ($payoutRaised !== null && $payoutRaised !== '') {
$payoutRaised = strtolower(trim((string) $payoutRaised));
if (! in_array($payoutRaised, ['yes', 'no', 'all'], true)) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'payout_raised must be yes, no, or all',
], 400);
}
} else {
$payoutRaised = null;
}
$hasDateFilter = $fromDate !== null || $toDate !== null;
$hasPayoutFilter = $payoutRaised !== null;
if (! $hasDateFilter && $agentId === null && ! $hasPayoutFilter) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'Provide at least one filter: from_date, to_date, agent_id, or payout_raised',
], 400);
}
$db = \Config\Database::connect();
$builder = $db->table('partner_policy pp');
$builder->select('pa.agent_code, pp.policy_number AS policy_no, pp.premium_amount AS premium, pp.commission_amount, pe.reg_no, pp.weight, pp.fuel_type, pp.vehicle_type');
$builder->select('COALESCE((SELECT GROUP_CONCAT(piu.utr_no ORDER BY piu.id SEPARATOR ", ") FROM partner_invoice_items pii_utr INNER JOIN partner_invoice pi_u ON pi_u.id = pii_utr.invoice_id AND pi_u.is_active = 1 INNER JOIN partner_invoice_utr piu ON piu.invoice_id = pii_utr.invoice_id AND piu.is_active = 1 WHERE pii_utr.policy_id = pp.id AND pii_utr.is_active = 1), "") AS utr_no', false);
$builder->select("(CASE WHEN EXISTS (SELECT 1 FROM partner_invoice_items pii INNER JOIN partner_invoice pi ON pi.id = pii.invoice_id AND pi.is_active = 1 WHERE pii.policy_id = pp.id AND pii.is_active = 1) THEN 'yes' ELSE 'no' END) AS received_or_not", false);
$builder->join('partner_agent pa', 'pa.id = pp.agent_id', 'left');
$builder->join('partner_enquiry pe', 'pe.id = pp.enquiry_id', 'left');
$builder->where('pp.is_active', 1);
if ($fromDate !== null) {
$builder->where('pp.issued_date >=', date('Y-m-d', strtotime($fromDate)));
}
if ($toDate !== null) {
$builder->where('pp.issued_date <=', date('Y-m-d', strtotime($toDate)));
}
if ($agentId !== null) {
$builder->where('pp.agent_id', $agentId);
}
if ($payoutRaised === 'yes') {
$builder->where('EXISTS (SELECT 1 FROM partner_invoice_items pii2 INNER JOIN partner_invoice pi2 ON pi2.id = pii2.invoice_id AND pi2.is_active = 1 WHERE pii2.policy_id = pp.id AND pii2.is_active = 1)', null, false);
} elseif ($payoutRaised === 'no') {
$builder->where('NOT EXISTS (SELECT 1 FROM partner_invoice_items pii2 INNER JOIN partner_invoice pi2 ON pi2.id = pii2.invoice_id AND pi2.is_active = 1 WHERE pii2.policy_id = pp.id AND pii2.is_active = 1)', null, false);
}
// payout_raised=all: no EXISTS / NOT EXISTS filter (both cases).
$builder->orderBy('pa.agent_code', 'ASC');
$builder->orderBy('pp.policy_number', 'ASC');
$rows = $builder->get()->getResultArray();
$sumTotal = 0.0;
$sumRaised = 0.0;
$sumPending = 0.0;
foreach ($rows as $row) {
$amt = (float) ($row['commission_amount'] ?? 0);
$sumTotal += $amt;
if (($row['received_or_not'] ?? '') === 'yes') {
$sumRaised += $amt;
} else {
$sumPending += $amt;
}
}
return $this->respond([
'status' => 'success',
'code' => 200,
'data' => $rows,
'count' => count($rows),
'summary' => [
'total' => round($sumTotal, 2),
'raised' => round($sumRaised, 2),
'pending' => round($sumPending, 2),
],
], 200);
} catch (\Throwable $e) {
return $this->respond([
'status' => 'failed',
'code' => 500,
'message' => $e->getMessage(),
], 500);
}
}
public function commissionPayoutReportExport()
{
try {
// ── 1. Collect & validate filters (same logic as commissionPayoutReport) ──
$fromDate = $this->request->getGet('from_date');
$toDate = $this->request->getGet('to_date');
$agentIdRaw = $this->request->getGet('agent_id');
$payoutRaised = $this->request->getGet('payout_raised');
$fromDate = ($fromDate !== null && $fromDate !== '') ? trim((string) $fromDate) : null;
$toDate = ($toDate !== null && $toDate !== '') ? trim((string) $toDate) : null;
$agentId = null;
if ($agentIdRaw !== null && $agentIdRaw !== '') {
$agentId = (int) $agentIdRaw;
}
if ($payoutRaised !== null && $payoutRaised !== '') {
$payoutRaised = strtolower(trim((string) $payoutRaised));
if (! in_array($payoutRaised, ['yes', 'no', 'all'], true)) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'payout_raised must be yes, no, or all',
], 400);
}
} else {
$payoutRaised = null;
}
$hasDateFilter = $fromDate !== null || $toDate !== null;
$hasPayoutFilter = $payoutRaised !== null;
if (! $hasDateFilter && $agentId === null && ! $hasPayoutFilter) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'Provide at least one filter: from_date, to_date, agent_id, or payout_raised',
], 400);
}
// ── 2. Build query (identical to the report API) ──────────────────────
$db = \Config\Database::connect();
$builder = $db->table('partner_policy pp');
$builder->select(
'pa.agent_code, pa.name AS agent_name, '
. 'ins.short_name AS insurer_short_name, ipti.insurance_plan_type AS insurance_plan_type, '
. 'pp.policy_number AS policy_no, pp.premium_amount AS premium, '
. 'pp.commission_amount, pp.insured_name, pe.reg_no, pp.weight, pp.fuel_type, '
. 'pp.vehicle_type, pp.issued_date, pp.od, pp.tp, pp.pa, pp.cgst, pp.sgst, pp.igst'
);
$builder->select(
'COALESCE((SELECT GROUP_CONCAT(piu.utr_no ORDER BY piu.id SEPARATOR ", ") '
. 'FROM partner_invoice_items pii_utr '
. 'INNER JOIN partner_invoice pi_u ON pi_u.id = pii_utr.invoice_id AND pi_u.is_active = 1 '
. 'INNER JOIN partner_invoice_utr piu ON piu.invoice_id = pii_utr.invoice_id AND piu.is_active = 1 '
. 'WHERE pii_utr.policy_id = pp.id AND pii_utr.is_active = 1), "") AS utr_no',
false
);
$builder->select(
'COALESCE((SELECT GROUP_CONCAT(DATE_FORMAT(piu.utr_date, \'%d-%m-%Y\') ORDER BY piu.id SEPARATOR ", ") '
. 'FROM partner_invoice_items pii_utr2 '
. 'INNER JOIN partner_invoice pi_u2 ON pi_u2.id = pii_utr2.invoice_id AND pi_u2.is_active = 1 '
. 'INNER JOIN partner_invoice_utr piu ON piu.invoice_id = pii_utr2.invoice_id AND piu.is_active = 1 '
. 'WHERE pii_utr2.policy_id = pp.id AND pii_utr2.is_active = 1), "") AS utr_date',
false
);
$builder->select(
"(CASE WHEN EXISTS (SELECT 1 FROM partner_invoice_items pii "
. "INNER JOIN partner_invoice pi ON pi.id = pii.invoice_id AND pi.is_active = 1 "
. "WHERE pii.policy_id = pp.id AND pii.is_active = 1) THEN 'yes' ELSE 'no' END) AS received_or_not",
false
);
$builder->join('partner_agent pa', 'pa.id = pp.agent_id', 'left');
$builder->join('partner_enquiry pe', 'pe.id = pp.enquiry_id', 'left');
$builder->join('partner_quotation pq', 'pq.id = pp.quotation_id', 'left');
$builder->join('insurers ins', 'ins.id = pq.insurer_id', 'left');
$builder->join('partner_insurance_plan_type_master ipti', 'ipti.id = pq.insurance_plan_type_id', 'left');
$builder->where('pp.is_active', 1);
if ($fromDate !== null) {
$builder->where('pp.issued_date >=', date('Y-m-d', strtotime($fromDate)));
}
if ($toDate !== null) {
$builder->where('pp.issued_date <=', date('Y-m-d', strtotime($toDate)));
}
if ($agentId !== null) {
$builder->where('pp.agent_id', $agentId);
}
if ($payoutRaised === 'yes') {
$builder->where(
'EXISTS (SELECT 1 FROM partner_invoice_items pii2 '
. 'INNER JOIN partner_invoice pi2 ON pi2.id = pii2.invoice_id AND pi2.is_active = 1 '
. 'WHERE pii2.policy_id = pp.id AND pii2.is_active = 1)',
null, false
);
} elseif ($payoutRaised === 'no') {
$builder->where(
'NOT EXISTS (SELECT 1 FROM partner_invoice_items pii2 '
. 'INNER JOIN partner_invoice pi2 ON pi2.id = pii2.invoice_id AND pi2.is_active = 1 '
. 'WHERE pii2.policy_id = pp.id AND pii2.is_active = 1)',
null, false
);
}
$builder->orderBy('pa.agent_code', 'ASC');
$builder->orderBy('pp.policy_number', 'ASC');
$rows = $builder->get()->getResultArray();
// ── 3. Build spreadsheet ──────────────────────────────────────────────
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle('Payout Report');
// ── Shared style helpers ──────────────────────────────────────────────
$headerFill = [
'fill' => [
'fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID,
'startColor' => ['argb' => 'FFB8CCE4'], // light-blue like the screenshot
],
];
$redBoldCenter = [
'font' => ['bold' => true, 'color' => ['argb' => 'FFFF0000'], 'name' => 'Arial', 'size' => 11],
'alignment' => ['horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER],
];
$labelBold = [
'font' => ['bold' => true, 'name' => 'Arial', 'size' => 10],
'alignment' => ['horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT],
];
$dateHighlight = [
'fill' => ['fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, 'startColor' => ['argb' => 'FFFFFF00']],
'font' => ['bold' => true, 'name' => 'Arial', 'size' => 10],
];
$colHeaderStyleText = [
'font' => ['bold' => true, 'name' => 'Arial', 'size' => 10],
'fill' => ['fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, 'startColor' => ['argb' => 'FFB8CCE4']],
'alignment' => ['horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT, 'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER, 'wrapText' => true],
'borders' => ['allBorders' => ['borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN]],
];
$colHeaderStyleNumber = [
'font' => ['bold' => true, 'name' => 'Arial', 'size' => 10],
'fill' => ['fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID, 'startColor' => ['argb' => 'FFB8CCE4']],
'alignment' => ['horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT, 'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER, 'wrapText' => true],
'borders' => ['allBorders' => ['borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN]],
];
// Data rows: text left-aligned (LTR), numbers right-aligned
$cellBorder = ['allBorders' => ['borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN]];
$cellStyleText = [
'font' => ['name' => 'Arial', 'size' => 9],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
'borders' => $cellBorder,
];
$cellStyleNumber = [
'font' => ['name' => 'Arial', 'size' => 9],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_RIGHT,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
],
'borders' => $cellBorder,
];
// Total columns used: AV (UTR NO + UTR DATE after PAYMENT PO MODE)
$lastCol = 'V';
$lastColNo = 22;
// ── Row 1: Title ──────────────────────────────────────────────────────
$sheet->mergeCells("A1:{$lastCol}1");
$sheet->setCellValue('A1', 'Payout Report');
$sheet->getStyle('A1')->applyFromArray($redBoldCenter);
$sheet->getRowDimension(1)->setRowHeight(20);
// ── Row 2: Partner name + Date range ─────────────────────────────────
$sheet->setCellValue('A2', 'PARTNER NAME');
$sheet->getStyle('A2')->applyFromArray($labelBold);
// Agent name: use first row's agent_name or fall back to id
$partnerLabel = ! empty($rows) ? ($rows[0]['agent_name'] ?? ('Agent #' . $agentId)) : 'ALL AGENTS';
$sheet->mergeCells('B2:D2');
$sheet->setCellValue('B2', strtoupper($partnerLabel));
$sheet->getStyle('B2')->applyFromArray($labelBold);
$sheet->setCellValue('F2', 'DATE RANG');
$sheet->getStyle('F2')->applyFromArray($dateHighlight);
$dateRangeFrom = $fromDate ? date('d-m-Y', strtotime($fromDate)) : 'N/A';
$dateRangeTo = $toDate ? date('d-m-Y', strtotime($toDate)) : 'N/A';
$sheet->setCellValue('G2', $dateRangeFrom);
$sheet->getStyle('G2')->applyFromArray($dateHighlight);
$sheet->setCellValue('H2', 'TO');
$sheet->setCellValue('I2', $dateRangeTo);
$sheet->getStyle('I2')->applyFromArray($dateHighlight);
$sheet->getRowDimension(2)->setRowHeight(18);
// ── Row 3: Column headers ─────────────────────────────────────────────
$headers = [
'A3' => 'REF NO',
'B3' => 'INSURER',
'C3' => 'PLAN TYPE',
'D3' => 'POLICY NO',
'E3' => "INSURED\nNAME",
'F3' => 'REG NO',
'G3' => 'PRODUCT',
'H3' => 'FUEL TYPE',
'I3' => 'GVW',
'J3' => 'MFG',
'K3' => 'RTO LOC',
'L3' => "DATE OF\nREG.",
'M3' => 'COMPANY',
'N3' => "MAKE &\nVARIANT",
'O3' => 'A (OD)',
'P3' => 'B (TP)',
'Q3' => 'A+B (NET)',
'R3' => 'S.TAX',
'S3' => 'TOTAL',
'T3' => 'PAYOUT',
'U3' => "PAYMENT MODE",
'V3' => 'UTR NO',
'W3' => 'UTR DATE',
];
$headerNumberCols = ['A', 'O', 'P', 'Q', 'R', 'S', 'I'];
foreach ($headers as $cell => $label) {
$sheet->setCellValue($cell, $label);
$col = preg_replace('/\d+/', '', $cell);
$sheet->getStyle($cell)->applyFromArray(
in_array($col, $headerNumberCols, true) ? $colHeaderStyleNumber : $colHeaderStyleText
);
}
$sheet->getRowDimension(3)->setRowHeight(30);
// ── Rows 4+: Data ─────────────────────────────────────────────────────
$dataStartRow = 4;
$rowNum = $dataStartRow;
foreach ($rows as $index => $row) {
$refNo = $index + 1;
$policyNo = $row['policy_no'] ?? '';
$regNo = $row['reg_no'] ?? '';
$fuelType = $row['fuel_type'] ?? '';
$vehicleType = $row['vehicle_type'] ?? '';
$weight = $row['weight'] ?? '';
$commissionAmt = (float) ($row['commission_amount'] ?? 0); // payout amount
$premium = (float) ($row['premium'] ?? 0);
$od = (float) ($row['od'] ?? 0);
$tp = (float) ($row['tp'] ?? 0);
$pa = (float) ($row['pa'] ?? 0);
$cgst = (float) ($row['cgst'] ?? 0);
$sgst = (float) ($row['sgst'] ?? 0);
$igst = (float) ($row['igst'] ?? 0);
$tpWithPa = $tp + $pa;
$netAmount = $od + $tp + $pa;
$serviceTax = $cgst + $sgst + $igst;
$utrNo = $row['utr_no'] ?? '';
$utrDate = $row['utr_date'] ?? '';
$payoutStatus = ($row['received_or_not'] ?? '') === 'yes' ? 'LINK' : 'PENDING';
$rowData = [
'A' => $refNo,
'B' => $row['insurer_short_name'] ?? '',
'C' => $row['insurance_plan_type'] ?? '',
'D' => $policyNo,
'E' => $row['insured_name'] ?? '',
'F' => $regNo,
'G' => $vehicleType, // Product (two-wheeler, etc.)
'H' => $fuelType,
'I' => $weight, // GVW / weight
'J' => '', // MFG add to query if available
'K' => '', // RTO LOC add to query if available
'L' => isset($row['issued_date']) ? date('d-m-Y', strtotime($row['issued_date'])) : '',
'M' => '', // Company / insurer add to query if available
'N' => '', // Make & Variant add if available
'O' => $od, // A (OD) => od
'P' => $tpWithPa, // B (TP) => tp+pa
'Q' => $netAmount, // A+B Net => od+tp+pa
'R' => $serviceTax, // S.TAX => cgst+igst+sgst
'S' => $premium, // TOTAL => premium amount
'T' => $commissionAmt, // PAYOUT => payout amount
'U' => $payoutStatus,
'V' => $utrNo,
'W' => $utrDate,
];
$dataNumberCols = ['A', 'I', 'O', 'P', 'Q', 'R', 'S'];
foreach ($rowData as $col => $value) {
$sheet->setCellValue("{$col}{$rowNum}", $value);
$sheet->getStyle("{$col}{$rowNum}")->applyFromArray(
in_array($col, $dataNumberCols, true) ? $cellStyleNumber : $cellStyleText
);
}
$sheet->getRowDimension($rowNum)->setRowHeight(18);
$rowNum++;
}
// ── Column widths ─────────────────────────────────────────────────────
$colWidths = [
'A' => 8, 'B' => 16, 'C' => 13, 'D' => 14, 'E' => 16,
'F' => 13, 'G' => 13, 'H' => 10, 'I' => 8, 'J' => 8,
'K' => 10, 'L' => 13, 'M' => 16, 'N' => 16, 'O' => 8,
'P' => 8, 'Q' => 10, 'R' => 8, 'S' => 10, 'T' => 12,
'U' => 18, 'V' => 14,
];
foreach ($colWidths as $col => $width) {
$sheet->getColumnDimension($col)->setWidth($width);
}
// ── Freeze panes below header ─────────────────────────────────────────
$sheet->freezePane('A4');
// ── 4. Stream the file to the browser ─────────────────────────────────
$filename = 'commission_payout_report_' . date('Ymd_His') . '.xlsx';
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
// Prevent any prior output from corrupting the binary stream
if (ob_get_length()) {
ob_end_clean();
}
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Cache-Control: max-age=0');
$writer->save('php://output');
exit;
} catch (\Throwable $e) {
return $this->respond([
'status' => 'failed',
'code' => 500,
'message' => $e->getMessage(),
], 500);
}
}
/**
* POST multipart field: commission_excel (xlsx). Updates partner_policy.commission_amount by policy number.
*/
public function uploadCommissionExcel()
{
try {
$file = $this->request->getFile('commission_excel');
if ($file === null || ! $file->isValid()) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'commission_excel file is required (xlsx)',
], 400);
}
$ext = strtolower((string) $file->getClientExtension());
if ($ext !== 'xlsx') {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'Only .xlsx files are supported',
], 400);
}
$spreadsheet = IOFactory::load($file->getTempName());
$sheet = $spreadsheet->getActiveSheet();
$highestRow = (int) $sheet->getHighestDataRow();
$headerRowNum = null;
for ($r = 1; $r <= min(5, $highestRow); $r++) {
$a = strtolower(trim((string) $sheet->getCell(Coordinate::stringFromColumnIndex(1) . $r)->getValue()));
$b = strtolower(trim((string) $sheet->getCell(Coordinate::stringFromColumnIndex(2) . $r)->getValue()));
if ($a === 'agent code' && str_contains($b, 'policy')) {
$headerRowNum = $r;
break;
}
}
if ($headerRowNum === null) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'Could not find header row (Agent Code, Policy Number)',
], 400);
}
$db = \Config\Database::connect();
$db->transStart();
$updated = 0;
$skipped = 0;
$errors = [];
for ($r = $headerRowNum + 1; $r <= $highestRow; $r++) {
$agentCodeExcel = trim((string) $sheet->getCell('A' . $r)->getFormattedValue());
$policyNumber = trim((string) $sheet->getCell('B' . $r)->getFormattedValue());
$payoutRaw = $sheet->getCell('C' . $r)->getCalculatedValue();
// 🔥 Best Practice (Final Version)
// safe removal: single quote from the beginning of the policy number
if (isset($policyNumber[0]) && $policyNumber[0] === "`") {
$policyNumber = substr($policyNumber, 1);
}
if ($policyNumber === '') {
continue;
}
if ($agentCodeExcel === '') {
$errors[] = [
'row' => $r,
'policy_number' => $policyNumber,
'agent_code' => '',
'message' => 'Agent code is empty for this policy',
];
continue;
}
if ($payoutRaw === null || $payoutRaw === '') {
$skipped++;
continue;
}
if (is_numeric($payoutRaw)) {
$amount = (float) $payoutRaw;
} else {
$amount = (float) str_replace([',', ' '], '', (string) $payoutRaw);
}
if ($amount < 0 || is_nan($amount)) {
$errors[] = [
'row' => $r,
'policy_number' => $policyNumber,
'agent_code' => $agentCodeExcel,
'message' => 'Invalid payout amount',
];
continue;
}
$policy = $db->table('partner_policy pp')
->select('pp.id, pa.agent_code as db_agent_code')
->join('partner_agent pa', 'pa.id = pp.agent_id', 'left')
->where('pp.policy_number', $policyNumber)
->where('pp.is_active', 1)
->get()
->getRowArray();
if (! $policy) {
$errors[] = [
'row' => $r,
'policy_number' => $policyNumber,
'agent_code' => $agentCodeExcel,
'message' => 'Policy not found or inactive',
];
continue;
}
$dbAgentCode = trim((string) ($policy['db_agent_code'] ?? ''));
if (strcasecmp($dbAgentCode, $agentCodeExcel) !== 0) {
$errors[] = [
'row' => $r,
'policy_number' => $policyNumber,
'agent_code' => $agentCodeExcel,
'expected_agent_code' => $dbAgentCode,
'message' => 'Agent code does not match this policy number',
];
continue;
}
$this->PolicyModel->update((int) $policy['id'], ['commission_amount' => $amount]);
$updated++;
}
$db->transComplete();
if ($db->transStatus() === false) {
return $this->respond([
'status' => 'failed',
'code' => 500,
'data' => 'Transaction failed',
], 500);
}
return $this->respond([
'status' => 'success',
'code' => 200,
'data' => [
'updated_rows' => $updated,
'skipped_empty_payout' => $skipped,
'error_count' => count($errors),
'errors' => $errors,
],
], 200);
} catch (\Throwable $e) {
return $this->respond([
'status' => 'failed',
'code' => 500,
'message' => $e->getMessage(),
], 500);
}
}
/**
* POST JSON: id (partner_policy.id), commission_amount (number or null).
*/
public function payoutInilneEditUpdate()
{
try {
$data = $this->request->getJSON(true);
if (! is_array($data)) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'Invalid JSON body',
], 400);
}
if (! isset($data['id']) || $data['id'] === '' || $data['id'] === null) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'id is required',
], 400);
}
if (! array_key_exists('commission_amount', $data)) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'commission_amount is required',
], 400);
}
$id = (int) $data['id'];
$policy = $this->PolicyModel->find($id);
if (! $policy) {
return $this->respond([
'status' => 'failed',
'code' => 404,
'data' => 'Data Not Found',
], 404);
}
$raw = $data['commission_amount'];
if ($raw === null || $raw === '') {
$commission = null;
} elseif (is_numeric($raw)) {
$commission = (float) $raw;
} else {
$clean = str_replace([',', ' '], '', (string) $raw);
if ($clean === '' || ! is_numeric($clean)) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'commission_amount must be numeric or null',
], 400);
}
$commission = (float) $clean;
}
if ($commission !== null && ($commission < 0 || is_nan($commission))) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'data' => 'commission_amount cannot be negative',
], 400);
}
$this->PolicyModel->update($id, ['commission_amount' => $commission]);
return $this->respond([
'status' => 'success',
'code' => 200,
'data' => [
'id' => $id,
'commission_amount' => $commission,
],
], 200);
} catch (\Throwable $e) {
return $this->respond([
'status' => 'failed',
'code' => 500,
'message' => $e->getMessage(),
], 500);
}
}
}