Merge branch 'main' of bitbucket.org:jubilian/nhance_partner_be
This commit is contained in:
commit
aaf0c81d11
@ -28,7 +28,7 @@ $routes->group('api', ['filter' => 'appSignature'], function ($routes) {
|
||||
|
||||
|
||||
//api's with token
|
||||
$routes->group('api', ['filter' => ["jwtAuth:1,2,3,4,agent","appSignature"] ], function ($routes) {
|
||||
$routes->group('api', ['filter' => ["jwtAuth:1,2,3,4,agent","appSignature"] ], function ($routes) {
|
||||
|
||||
//logout
|
||||
$routes->get('auth/logout', 'StaffAuthController::logout');
|
||||
@ -74,6 +74,9 @@ $routes->group('api', ['filter' => 'appSignature'], function ($routes) {
|
||||
$routes->get('agent/deleteAgentIncentiveFile', 'AgentController::deleteAgentIncentiveFile');
|
||||
$routes->get('agent/downloadAgentIncentiveFile', 'AgentController::downloadAgentIncentiveFile');
|
||||
|
||||
$routes->get('grid', 'AgentIncentiveController::getGridData');
|
||||
$routes->post('grid/upload', 'AgentIncentiveController::uploadGridFile');
|
||||
$routes->get('grid/fileList', 'AgentIncentiveController::gridFileList');
|
||||
|
||||
//Staff
|
||||
$routes->get('staff/staffList', 'StaffController::staffList');
|
||||
@ -123,6 +126,7 @@ $routes->group('api', ['filter' => 'appSignature'], function ($routes) {
|
||||
$routes->get('policy/findPolicy', 'PolicyController::findPolicy');
|
||||
$routes->post('policy/createPolicy', 'PolicyController::createPolicy');
|
||||
$routes->post('policy/updatePolicy', 'PolicyController::updatePolicy');
|
||||
$routes->post('policy/updatePolicyCommission', 'PolicyController::updatePolicyCommission');
|
||||
$routes->get('policy/downloadPolicyFile', 'PolicyController::downloadPolicyFile');
|
||||
$routes->post('policy/uploadPolicyFile', 'PolicyController::uploadPolicyFile');
|
||||
$routes->get("policy/searchThePolicies", "PolicyController::searchThePolicies");
|
||||
@ -143,7 +147,9 @@ $routes->group('api', ['filter' => 'appSignature'], function ($routes) {
|
||||
$routes->post('endorsement/createEndorsement', 'EndorsementController::createEndorsement');
|
||||
$routes->post('endorsement/updateEndorsement', 'EndorsementController::updateEndorsement');
|
||||
$routes->get('endorsement/downloadEndorsementCompletionFile', 'EndorsementController::downloadEndorsementCompletionFile');
|
||||
|
||||
$routes->post('endorsement/uploadEndorsementFile', 'EndorsementController::uploadEndorsementFile');
|
||||
$routes->get('endorsement/deleteEndorsement', 'EndorsementController::deleteEndorsement');
|
||||
|
||||
//dashboard
|
||||
$routes->get('dashboard/managerDashboard', 'DashboardController::managerDashboard');
|
||||
$routes->get('dashboard/handlerDashboard', 'DashboardController::handlerDashboard');
|
||||
@ -178,15 +184,43 @@ $routes->group('api', ['filter' => 'appSignature'], function ($routes) {
|
||||
|
||||
$routes->get('reports/endorsement-excel', 'ExcelExportController::downloadExcelEndorsement');
|
||||
|
||||
|
||||
// DASHBOARD Season 6 — Partner Portal
|
||||
|
||||
// GET /partner/{id}/details
|
||||
$routes->get('partner/(:num)/details', 'DashboardController::partnerDetails/$1');
|
||||
|
||||
// GET /partner/{id}/policies
|
||||
$routes->get('partner/(:num)/policies', 'DashboardController::partnerPolicies/$1');
|
||||
|
||||
// GET /partner/{id}/renewals?days=N
|
||||
$routes->get('partner/(:num)/renewals', 'DashboardController::partnerRenewals/$1');
|
||||
|
||||
// GET /partner/{id}/earnings
|
||||
$routes->get('partner/(:num)/earnings', 'DashboardController::partnerEarnings/$1');
|
||||
|
||||
// DASHBOARD Season 7 — Partner Portal
|
||||
$routes->get('grid/download', 'ExcelExportController::downloadExcelGrid');
|
||||
|
||||
|
||||
//invoice
|
||||
$routes->get('invoice/list', 'InvoiceController::invoiceList');
|
||||
$routes->get('invoice/details', 'InvoiceController::findInvoiceWithItems');
|
||||
$routes->post('invoice/create-or-update', 'InvoiceController::createOrUpdateInvoice');
|
||||
$routes->get('invoice/utrDetails', 'InvoiceController::utrDetails');
|
||||
$routes->post('invoice/add-payment', 'InvoiceController::addInvoicePayment'); // tempo
|
||||
$routes->post('invoice/updateUtrDetails', 'InvoiceController::updateUtrDetails');
|
||||
$routes->get('invoice/add-payment-history', 'InvoiceController::addPaymentHistory'); // tempo
|
||||
$routes->post('invoice/add-payment-history', 'InvoiceController::addPaymentHistory'); // tempo
|
||||
$routes->get('invoice/listUtrDetails', 'InvoiceController::listUtrDetails');
|
||||
$routes->post('invoice/listUtrDetails', 'InvoiceController::listUtrDetails');
|
||||
$routes->get('invoice/delete', 'InvoiceController::deleteInvoice');
|
||||
$routes->post('invoice/commission-rate-list', 'InvoiceController::getCommissionRateList');
|
||||
$routes->get('invoice/getAgentUnusedCommissionList', 'InvoiceController::getAgentUnusedCommissionList');
|
||||
$routes->post('invoice/bulk-upload-commission', 'InvoiceController::bulkUploadCommission');
|
||||
$routes->post('invoice/bulk-upload-commission/proceed', 'InvoiceController::bulkUploadCommissionProceed');
|
||||
$routes->post('invoice/bulk-upload-commission', 'InvoiceController::bulkUploadCommission');
|
||||
$routes->post('invoice/bulk-upload-commission/proceed', 'InvoiceController::bulkUploadCommissionProceed');
|
||||
|
||||
|
||||
// SALES EXECUTIVE
|
||||
@ -203,7 +237,7 @@ $routes->group('api', ['filter' => 'appSignature'], function ($routes) {
|
||||
$routes->get('audit/history', 'MasterController::getHistory');
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// common
|
||||
@ -688,15 +722,3 @@ $routes->get("processjob", "JobWorker::processJob");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -269,8 +269,10 @@ class AgentController extends ResourceController
|
||||
try{
|
||||
|
||||
$agent_id = $this->request->getGet('agent_id');
|
||||
$type = $this->request->getGet('type');
|
||||
|
||||
$data = $this->AgentIncentiveFileModel->where('agent_id',$agent_id)->where('is_active',1)->findAll();
|
||||
$fileType = (!empty($type) && $type === 'grid') ? 'grid' : 'incentive';
|
||||
$data = $this->AgentIncentiveFileModel->where('agent_id',$agent_id)->where('is_active',1)->where('file_type', $fileType)->findAll();
|
||||
|
||||
return $this->respond(['status' => 'success', 'code' => 200, 'data' => $data], 200);
|
||||
|
||||
@ -287,7 +289,7 @@ class AgentController extends ResourceController
|
||||
$data = $this->request->getPost();
|
||||
|
||||
//duplicate check
|
||||
$duplicateData = $this->AgentIncentiveFileModel->where('agent_id',$data['agent_id'])->where('incentive_month',$data['incentive_month'])->first();
|
||||
$duplicateData = $this->AgentIncentiveFileModel->where('agent_id',$data['agent_id'])->where('incentive_month',$data['incentive_month'])->where('file_type','incentive')->first();
|
||||
if(!empty($duplicateData)){
|
||||
return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'Duplicate Entry.'], 200);
|
||||
}
|
||||
@ -311,6 +313,7 @@ class AgentController extends ResourceController
|
||||
'agent_id' => $data['agent_id'],
|
||||
'incentive_month' => $data['incentive_month'],
|
||||
'incentive_file_name' => $incentiveFileName,
|
||||
'file_type' => 'incentive',
|
||||
'created_by' => $data['created_by'] ?? null
|
||||
];
|
||||
|
||||
@ -332,7 +335,7 @@ class AgentController extends ResourceController
|
||||
|
||||
|
||||
// check if agent exists
|
||||
$file = $this->AgentIncentiveFileModel->find((int)$id);
|
||||
$file = $this->AgentIncentiveFileModel->where('file_type','incentive')->find((int)$id);
|
||||
if (!$file) {
|
||||
return $this->respond(['status' => 'failed','code' => 200, 'data' => 'File not found'], 200);
|
||||
}
|
||||
@ -353,13 +356,15 @@ class AgentController extends ResourceController
|
||||
{
|
||||
try {
|
||||
$id = $this->request->getGet('id');
|
||||
$type = $this->request->getGet('type'); // 'incentive' or 'grid'
|
||||
|
||||
if (!$id) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'ID is required'], 200);
|
||||
}
|
||||
|
||||
$fileType = (!empty($type) && $type === 'grid') ? 'grid' : 'incentive';
|
||||
// Fetch record from DB
|
||||
$fileRecord = $this->AgentIncentiveFileModel->where('is_active',1)->find((int)$id);
|
||||
$fileRecord = $this->AgentIncentiveFileModel->where('is_active',1)->where('file_type',$fileType)->find((int)$id);
|
||||
|
||||
if (!$fileRecord) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File not found'], 200);
|
||||
@ -379,10 +384,6 @@ class AgentController extends ResourceController
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//Empty Commit
|
||||
|
||||
}
|
||||
|
||||
614
app/Controllers/AgentIncentiveController.php
Normal file
614
app/Controllers/AgentIncentiveController.php
Normal file
@ -0,0 +1,614 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use CodeIgniter\RESTful\ResourceController;
|
||||
use App\Controllers\BaseController;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
|
||||
use App\Models\AgentIncentiveFileModel;
|
||||
use App\Models\PartnerInsurancePayoutGridModel;
|
||||
use App\Models\AgentModel;
|
||||
|
||||
|
||||
class AgentIncentiveController extends ResourceController
|
||||
{
|
||||
protected $AgentIncentiveFileModel;
|
||||
protected $PayoutGridModel;
|
||||
protected $PartnerAgentModel;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->AgentIncentiveFileModel = new AgentIncentiveFileModel();
|
||||
$this->PayoutGridModel = new PartnerInsurancePayoutGridModel();
|
||||
$this->PartnerAgentModel = new AgentModel();
|
||||
}
|
||||
|
||||
// GET /agent/gridFileList
|
||||
public function gridFileList()
|
||||
{
|
||||
try {
|
||||
$builder = $this->AgentIncentiveFileModel->builder();
|
||||
|
||||
// 1. Explicitly select and format dates in the SQL layer (Faster + Indian Format)
|
||||
$builder->select("
|
||||
paif.id,
|
||||
paif.agent_id,
|
||||
paif.incentive_month,
|
||||
DATE_FORMAT(paif.incentive_month, '%d-%m-%Y') as vaild_from,
|
||||
paif.incentive_file_name,
|
||||
ps.name as created_by_name,
|
||||
paif.created_on,
|
||||
DATE_FORMAT(paif.created_on, '%d-%m-%Y %h:%i %p') as created_date
|
||||
");
|
||||
|
||||
$builder->from('partner_agent_incentive_file paif');
|
||||
|
||||
// 2. The Join
|
||||
$builder->join('partner_staff ps', 'ps.id = paif.created_by', 'left');
|
||||
|
||||
// 3. Filters
|
||||
$builder->where('paif.is_active', 1);
|
||||
$builder->where('paif.file_type', 'grid');
|
||||
|
||||
// 4. THE FIX: Group by the primary ID to stop the "5 rows" duplication
|
||||
$builder->groupBy('paif.id');
|
||||
|
||||
// 5. Order
|
||||
$builder->orderBy('paif.id', 'DESC');
|
||||
|
||||
$query = $builder->get();
|
||||
$result = $query->getResult();
|
||||
|
||||
// Use $this->respond to maintain consistency with your other API methods
|
||||
return $this->respond([
|
||||
'status' => 'success', // Changed to 'success' to match your other methods
|
||||
'code' => 200,
|
||||
'data' => $result
|
||||
], 200);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'code' => 500,
|
||||
'data' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Upload Grid File (file_type = 'grid')
|
||||
// Parses Excel and UPSERTs rows into partner_insurance_payout_grid
|
||||
// -------------------------------------------------------------------------
|
||||
public function uploadGridFile()
|
||||
{
|
||||
try {
|
||||
/* ====================================================================
|
||||
* STEP 1 — Validate POST input (Agent ID removed)
|
||||
* ==================================================================== */
|
||||
$data = $this->request->getPost();
|
||||
$month = $data['incentive_month'] ?? null;
|
||||
$createdBy = $data['created_by'] ?? null;
|
||||
|
||||
/* ====================================================================
|
||||
* STEP 2 — Validation now only checks for month
|
||||
* ==================================================================== */
|
||||
if (empty($month)) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'code' => 400,
|
||||
'data' => 'Incentive month is required.',
|
||||
], 200);
|
||||
}
|
||||
|
||||
/* ====================================================================
|
||||
* STEP 3 — Validate uploaded file
|
||||
* ==================================================================== */
|
||||
$gridFile = $this->request->getFile('incentive_file_name');
|
||||
if (!$gridFile || !$gridFile->isValid()) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'code' => 400,
|
||||
'data' => 'No valid file uploaded.',
|
||||
], 200);
|
||||
}
|
||||
|
||||
/* ====================================================================
|
||||
* STEP 4 — Move file
|
||||
* ==================================================================== */
|
||||
$uploadPath = WRITEPATH . 'uploads/agent/incentive_file/';
|
||||
if (!is_dir($uploadPath)) mkdir($uploadPath, 0777, true);
|
||||
|
||||
$gridFileName = time() . '_' . $gridFile->getRandomName();
|
||||
$gridFile->move($uploadPath, $gridFileName);
|
||||
|
||||
/* ====================================================================
|
||||
* STEP 5 — Save File record & GET THE ID
|
||||
* ==================================================================== */
|
||||
$fileData = [
|
||||
'incentive_month' => $month,
|
||||
'incentive_file_name' => $gridFileName,
|
||||
'file_type' => 'grid',
|
||||
'is_active' => 1,
|
||||
'created_by' => $createdBy,
|
||||
];
|
||||
|
||||
// insert() with returnID enabled returns the inserted primary key in CI4.
|
||||
$fileId = $this->AgentIncentiveFileModel->insert($fileData, true);
|
||||
if (empty($fileId) || (int)$fileId <= 0) {
|
||||
$modelErrors = $this->AgentIncentiveFileModel->errors();
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'code' => 400,
|
||||
'data' => !empty($modelErrors) ? $modelErrors : 'Unable to create incentive file record.',
|
||||
], 200);
|
||||
}
|
||||
|
||||
/* ====================================================================
|
||||
* STEP 6 — Parse Excel
|
||||
* ==================================================================== */
|
||||
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($uploadPath . $gridFileName);
|
||||
$rows = $spreadsheet->getActiveSheet()->toArray(null, true, true, false);
|
||||
|
||||
if (empty($rows)) {
|
||||
return $this->respond(['status' => 'success', 'code' => 200, 'data' => 'File empty.'], 200);
|
||||
}
|
||||
|
||||
/* ====================================================================
|
||||
* STEP 7 — Clean Numeric Helper (Modified to store only numbers)
|
||||
* ==================================================================== */
|
||||
$extractNumeric = static function ($value): ?float {
|
||||
$str = strtolower(trim((string) $value));
|
||||
if ($str === '' || $str === 'nan') return null;
|
||||
|
||||
if (str_contains($str, '+')) {
|
||||
$sum = 0.0; $hasValid = false;
|
||||
foreach (explode('+', $str) as $part) {
|
||||
$cleanPart = preg_replace('/[^0-9.]/', '', $part);
|
||||
if (is_numeric($cleanPart)) { $sum += (float) $cleanPart; $hasValid = true; }
|
||||
}
|
||||
return $hasValid ? $sum : null;
|
||||
}
|
||||
// Strip everything except numbers and decimals
|
||||
$str = preg_replace('/[^0-9.]/', '', $str);
|
||||
return is_numeric($str) ? (float) $str : null;
|
||||
};
|
||||
|
||||
/* ====================================================================
|
||||
* STEP 8 — Process Rows
|
||||
* ==================================================================== */
|
||||
$currentVehicleType = null;
|
||||
$layoutHasComp = true;
|
||||
$insertedCount = 0;
|
||||
|
||||
foreach ($rows as $row) {
|
||||
while (count($row) < 9) $row[] = null;
|
||||
|
||||
$col0 = trim((string) $row[0]);
|
||||
$col1 = trim((string) $row[1]);
|
||||
|
||||
// Section Header Detection
|
||||
if ($col0 !== '' && ($col1 === '' || strtolower($col1) === 'nan') && strtoupper($col0) !== 'INSURER') {
|
||||
$currentVehicleType = strtoupper($col0);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Column Header Detection (Layout A vs B)
|
||||
if (strtoupper($col0) === 'INSURER') {
|
||||
$col4Upper = strtoupper((string)$row[4]);
|
||||
$layoutHasComp = !preg_match('/(FUEL|PETROL|DIESEL|TP)/', $col4Upper);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($col0) || $currentVehicleType === null) continue;
|
||||
|
||||
// Mapping with Clean Numbers
|
||||
if ($layoutHasComp) {
|
||||
$insurer = strtoupper($col0);
|
||||
$rto = strtoupper((string)$row[1]);
|
||||
$segment = strtoupper((string)$row[2]);
|
||||
// We extract numeric values only for the storage
|
||||
$comp = $extractNumeric($row[3]);
|
||||
$tp = $extractNumeric($row[4]);
|
||||
$od = null;
|
||||
$fuel = null;
|
||||
$brokerName = trim((string)$row[6]);
|
||||
$brokerExcelComp = $extractNumeric($row[7]);
|
||||
$brokerExcelTp = $extractNumeric($row[8]);
|
||||
$brokerExcelOd = null;
|
||||
|
||||
} else {
|
||||
$insurer = strtoupper($col0);
|
||||
$rto = strtoupper((string)$row[1]);
|
||||
$segment = strtoupper((string)$row[2]);
|
||||
$comp = $extractNumeric($row[3]);
|
||||
$tp = null;
|
||||
$od = null;
|
||||
$fuel = strtoupper((string)$row[4]);
|
||||
$brokerName = trim((string)$row[6]);
|
||||
$brokerExcelComp = null;
|
||||
$brokerExcelTp = $extractNumeric($row[7]);
|
||||
$brokerExcelOd = null;
|
||||
}
|
||||
|
||||
// OD logic (if COMP contains an OD value)
|
||||
if ($comp !== null && stripos((string)$row[3], 'OD') !== false && $tp === null) {
|
||||
$od = $comp; $comp = null;
|
||||
$brokerExcelOd = $brokerExcelComp; $brokerExcelComp = null;
|
||||
}
|
||||
|
||||
$gridRow = [
|
||||
'partner_agent_incentive_file_id' => $fileId, // STORE THE FILE ID HERE
|
||||
'vehicle_type' => $currentVehicleType,
|
||||
'fuel' => $fuel,
|
||||
'insurer' => $insurer,
|
||||
'rto' => $rto,
|
||||
'broker_name' => $brokerName,
|
||||
'segment' => $segment,
|
||||
'comp' => $comp,
|
||||
'tp' => $tp,
|
||||
'od' => $od,
|
||||
'remarks' => trim((string)($row[5] ?? '')),
|
||||
'broker_comp' => $brokerExcelComp,
|
||||
'broker_tp' => $brokerExcelTp,
|
||||
'broker_od' => $brokerExcelOd,
|
||||
];
|
||||
|
||||
|
||||
$gridRow['created_by'] = $createdBy;
|
||||
$this->PayoutGridModel->insert($gridRow);
|
||||
$insertedCount++;
|
||||
|
||||
}
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'data' => [
|
||||
'file_id' => $fileId,
|
||||
'inserted' => $insertedCount,
|
||||
'message' => "Processed: {$insertedCount} new",
|
||||
],
|
||||
], 200);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// List all grid records (optional utility endpoint)
|
||||
// -------------------------------------------------------------------------
|
||||
// GET /agent/payoutGrid?role=Manager means all list
|
||||
// GET /agent/payoutGrid?role=Manager&insurer=HDFC&vehicle_type=TWO%20WHEELER&plan_type=comp
|
||||
// GET /agent/payoutGrid?role=Manager&insurer=HDFC&rto=TN&segment=BIKE&plan_type=comp
|
||||
// GET /agent/payoutGrid?role=Manager&insurer=HDFC&segment=BIKE&vehicle_type=TWO%20WHEELER&plan_type=comp
|
||||
public function getGridData()
|
||||
{
|
||||
try {
|
||||
$request = $this->request;
|
||||
$role = $request->getGet('role');
|
||||
$file_id = $request->getGet('file_id');
|
||||
|
||||
// 1. Check if Role is provided
|
||||
if (!$role) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'code' => 400,
|
||||
'data' => 'Role is required to fetch payout data.'
|
||||
], 400);
|
||||
}
|
||||
|
||||
// 2. Extract Filters
|
||||
$insurer = $request->getGet('insurer');
|
||||
$rto = $request->getGet('rto');
|
||||
$segment = $request->getGet('segment');
|
||||
$vehicle_type = $request->getGet('vehicle_type');
|
||||
$plan_type = $request->getGet('plan_type');
|
||||
|
||||
// 3. Build the Grid Query
|
||||
$builder = $this->PayoutGridModel->builder();
|
||||
$builder->orderBy('vehicle_type', 'ASC')->orderBy('insurer', 'ASC');
|
||||
|
||||
// Apply filters only for authorized roles
|
||||
if (in_array($role, ['Manager', 'Accounts'])) {
|
||||
if (!empty($insurer)) $builder->where('insurer', $insurer);
|
||||
if (!empty($rto)) $builder->where('rto', $rto);
|
||||
if (!empty($segment)) $builder->where('segment', $segment);
|
||||
if (!empty($vehicle_type)) $builder->where('vehicle_type', $vehicle_type);
|
||||
|
||||
// Handle dynamic column selection (comp/tp/od)
|
||||
if (!empty($plan_type) && in_array($plan_type, ['comp', 'tp', 'od'])) {
|
||||
$builder->select("id, insurer, vehicle_type, segment, rto, $plan_type as payout,fuel,broker_name,comp,tp,od,remarks,broker_comp,
|
||||
broker_tp,broker_od,created_by,created_at,updated_by,updated_at");
|
||||
} else {
|
||||
// Default selection if no plan_type or invalid plan_type
|
||||
$builder->select('id, insurer, vehicle_type, segment, rto, comp, tp, od, fuel,broker_name,comp,tp,od,remarks,broker_comp,
|
||||
broker_tp,broker_od,created_by,created_at,updated_by,updated_at');
|
||||
}
|
||||
}
|
||||
|
||||
if ($file_id) {
|
||||
$builder->where('partner_agent_incentive_file_id', $file_id);
|
||||
} else {
|
||||
$maxRow = $this->PayoutGridModel
|
||||
->selectMax('partner_agent_incentive_file_id')
|
||||
->first();
|
||||
|
||||
// Model may return array or object based on global returnType.
|
||||
$maxFileId = null;
|
||||
if (is_array($maxRow)) {
|
||||
$maxFileId = $maxRow['partner_agent_incentive_file_id'] ?? null;
|
||||
} elseif (is_object($maxRow)) {
|
||||
$maxFileId = $maxRow->partner_agent_incentive_file_id ?? null;
|
||||
}
|
||||
|
||||
if (!empty($maxFileId)) {
|
||||
$builder->where('partner_agent_incentive_file_id', $maxFileId);
|
||||
} else {
|
||||
// No data case
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'code' => 404,
|
||||
'data' => 'No data found'
|
||||
], 404);
|
||||
}
|
||||
}
|
||||
|
||||
$gridResults = $builder->get()->getResult();
|
||||
|
||||
// 4. Combine Grid Results with Dropdown Meta-data
|
||||
$responseData = [
|
||||
'grid' => $gridResults,
|
||||
'rtos' => $this->getUniqueColumnValues('rto'),
|
||||
'segments' => $this->getUniqueColumnValues('segment'),
|
||||
'vehicle_types' => $this->getUniqueColumnValues('vehicle_type'),
|
||||
'insurers' => $this->getUniqueColumnValues('insurer'),
|
||||
'plan_types' => [
|
||||
['value' => 'comp', 'label' => 'Comprehensive'],
|
||||
['value' => 'tp', 'label' => 'Third Party'],
|
||||
['value' => 'od', 'label' => 'Own Damage']
|
||||
]
|
||||
];
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'data' => $responseData
|
||||
], 200);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'code' => 500,
|
||||
'data' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to fetch unique non-empty values for a column
|
||||
*/
|
||||
private function getUniqueColumnValues($column)
|
||||
{
|
||||
$results = $this->PayoutGridModel->select($column)
|
||||
->distinct()
|
||||
->where("$column IS NOT NULL")
|
||||
->where("$column !=", '')
|
||||
->orderBy($column, 'ASC')
|
||||
->findAll();
|
||||
|
||||
return array_column($results, $column);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Update grid row by id
|
||||
// Route: POST /agent/updateGrid
|
||||
// -------------------------------------------------------------------------
|
||||
public function updateGrid()
|
||||
{
|
||||
try {
|
||||
$jsonData = (array) ($this->request->getJSON(true) ?? []);
|
||||
$postData = $this->request->getPost() ?? [];
|
||||
$data = !empty($jsonData) ? $jsonData : $postData;
|
||||
|
||||
$id = isset($data['id']) ? (int) $data['id'] : 0;
|
||||
if ($id <= 0) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'code' => 400,
|
||||
'data' => 'id is required.',
|
||||
], 200);
|
||||
}
|
||||
|
||||
$existing = $this->PayoutGridModel->find($id);
|
||||
if (empty($existing)) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'code' => 404,
|
||||
'data' => 'Grid record not found.',
|
||||
], 200);
|
||||
}
|
||||
|
||||
$updateData = [
|
||||
'vehicle_type' => array_key_exists('vehicle_type', $data) ? $data['vehicle_type'] : null,
|
||||
'fuel' => array_key_exists('fuel', $data) ? $data['fuel'] : null,
|
||||
'rto' => array_key_exists('rto', $data) ? $data['rto'] : null,
|
||||
'segment' => array_key_exists('segment', $data) ? $data['segment'] : null,
|
||||
'broker_name' => array_key_exists('broker_name', $data) ? $data['broker_name'] : null,
|
||||
'comp' => array_key_exists('comp', $data) ? $data['comp'] : null,
|
||||
'tp' => array_key_exists('tp', $data) ? $data['tp'] : null,
|
||||
'od' => array_key_exists('od', $data) ? $data['od'] : null,
|
||||
'broker_comp' => array_key_exists('broker_comp', $data) ? $data['broker_comp'] : null,
|
||||
'broker_tp' => array_key_exists('broker_tp', $data) ? $data['broker_tp'] : null,
|
||||
'broker_od' => array_key_exists('broker_od', $data) ? $data['broker_od'] : null,
|
||||
|
||||
'remarks' => array_key_exists('remarks', $data) ? $data['remarks'] : null,
|
||||
];
|
||||
|
||||
$hasAnyField = false;
|
||||
foreach (array_keys($updateData) as $field) {
|
||||
if (array_key_exists($field, $data)) {
|
||||
$hasAnyField = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$hasAnyField) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'code' => 400,
|
||||
'data' => 'No update fields provided.',
|
||||
], 200);
|
||||
}
|
||||
|
||||
if (array_key_exists('updated_by', $data)) {
|
||||
$updateData['updated_by'] = $data['updated_by'];
|
||||
}
|
||||
|
||||
$this->PayoutGridModel->updateGridById($id, $updateData);
|
||||
$updated = $this->PayoutGridModel->find($id);
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'data' => $updated,
|
||||
], 200);
|
||||
} catch (\Exception $e) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'code' => 500,
|
||||
'data' => $e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 2. Download Filtered Grid in Excel
|
||||
// Route: $routes->get('agent/downloadGrid', 'AgentIncentiveController::downloadGridInExcel');
|
||||
// Purpose: Applies the exact same filters as getGridData/(:num)(), but instead
|
||||
// of returning JSON, it generates and downloads an Excel file.
|
||||
// -------------------------------------------------------------------------
|
||||
public function downloadGridInExcel()
|
||||
{
|
||||
try {
|
||||
$request = $this->request;
|
||||
|
||||
// Get GET parameters for filtering
|
||||
$role = $request->getGet('role');
|
||||
$insurer = $request->getGet('insurer');
|
||||
$rto = $request->getGet('rto');
|
||||
$segment = $request->getGet('segment');
|
||||
$vehicle_type = $request->getGet('vehicle_type');
|
||||
$plan_type = $request->getGet('plan_type'); // comp, tp, od
|
||||
|
||||
$builder = $this->PayoutGridModel->builder();
|
||||
$builder->orderBy('vehicle_type', 'ASC')->orderBy('insurer', 'ASC');
|
||||
|
||||
// Apply filters if the user is a Manager or Accounts role
|
||||
if (in_array($role, ['Manager', 'Accounts'])) {
|
||||
if (!empty($insurer)) $builder->where('insurer', $insurer);
|
||||
if (!empty($rto)) $builder->where('rto', $rto);
|
||||
if (!empty($segment)) $builder->where('segment', $segment);
|
||||
if (!empty($vehicle_type)) $builder->where('vehicle_type', $vehicle_type);
|
||||
}
|
||||
|
||||
// Fetch the filtered records
|
||||
$records = $builder->get()->getResultArray();
|
||||
|
||||
if (empty($records)) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'code' => 404,
|
||||
'data' => 'No records found to export based on your filters.'
|
||||
], 404);
|
||||
}
|
||||
|
||||
// --- Start Excel Generation ---
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
// Setup Header Row
|
||||
$sheet->setCellValue('A1', 'Vehicle Type');
|
||||
$sheet->setCellValue('B1', 'Insurer');
|
||||
$sheet->setCellValue('C1', 'RTO');
|
||||
$sheet->setCellValue('D1', 'Segment');
|
||||
$sheet->setCellValue('E1', 'Broker Name'); // Changed from 'ID' for clarity
|
||||
|
||||
// Set the dynamic column header based on the selected plan type
|
||||
if ($plan_type === 'comp') {
|
||||
$sheet->setCellValue('F1', 'Comp');
|
||||
$sheet->setCellValue('G1', 'Comp');
|
||||
} elseif ($plan_type === 'tp') {
|
||||
$sheet->setCellValue('F1', 'TP');
|
||||
$sheet->setCellValue('G1', 'TP');
|
||||
} elseif ($plan_type === 'od') {
|
||||
$sheet->setCellValue('F1', 'OD');
|
||||
$sheet->setCellValue('G1', 'OD');
|
||||
} else {
|
||||
// If no specific plan type is selected, show all
|
||||
$sheet->setCellValue('F1', 'Comp');
|
||||
$sheet->setCellValue('G1', 'TP');
|
||||
$sheet->setCellValue('H1', 'OD');
|
||||
|
||||
$sheet->setCellValue('I1', 'Comp');
|
||||
$sheet->setCellValue('J1', 'TP');
|
||||
$sheet->setCellValue('K1', 'OD');
|
||||
}
|
||||
|
||||
// Populate Excel Rows
|
||||
$rowNumber = 2; // Start on row 2 (row 1 is headers)
|
||||
foreach ($records as $row) {
|
||||
$sheet->setCellValue('A' . $rowNumber, $row['vehicle_type']);
|
||||
$sheet->setCellValue('B' . $rowNumber, $row['insurer']);
|
||||
$sheet->setCellValue('C' . $rowNumber, $row['rto']);
|
||||
$sheet->setCellValue('D' . $rowNumber, $row['segment']);
|
||||
$sheet->setCellValue('E' . $rowNumber, $row['broker_name']);
|
||||
|
||||
// Output dynamic columns based on selected plan type
|
||||
if ($plan_type === 'comp') {
|
||||
$sheet->setCellValue('F' . $rowNumber, $row['comp']);
|
||||
|
||||
} elseif ($plan_type === 'tp') {
|
||||
$sheet->setCellValue('F' . $rowNumber, $row['tp']);
|
||||
|
||||
} elseif ($plan_type === 'od') {
|
||||
$sheet->setCellValue('F' . $rowNumber, $row['od']);
|
||||
|
||||
} else {
|
||||
$sheet->setCellValue('F' . $rowNumber, $row['comp']);
|
||||
$sheet->setCellValue('G' . $rowNumber, $row['tp']);
|
||||
$sheet->setCellValue('H' . $rowNumber, $row['od']);
|
||||
|
||||
}
|
||||
$rowNumber++;
|
||||
}
|
||||
|
||||
// Auto-size columns for better readability
|
||||
foreach (range('A', $sheet->getHighestColumn()) as $columnID) {
|
||||
$sheet->getColumnDimension($columnID)->setAutoSize(true);
|
||||
}
|
||||
|
||||
// Write the file to a temporary location
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
$fileName = 'Filtered_Payout_Grid_' . date('Y-m-d_H-i') . '.xlsx';
|
||||
$tempFile = tempnam(sys_get_temp_dir(), 'grid_export');
|
||||
$writer->save($tempFile);
|
||||
|
||||
// Return the file as a direct download response
|
||||
return $this->response->download($tempFile, null)->setFileName($fileName);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'code' => 500,
|
||||
'data' => 'Failed to generate Excel file: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1172,5 +1172,413 @@ class DashboardController extends ResourceController
|
||||
|
||||
return array_values($final);
|
||||
}
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// DashboardController.php — Partner Portal API methods
|
||||
// Routes:
|
||||
// GET partner/(:num)/details → partnerDetails($id)
|
||||
// GET partner/(:num)/policies → partnerPolicies($id)
|
||||
// GET partner/(:num)/renewals → partnerRenewals($id) ?days=20
|
||||
// GET partner/(:num)/earnings → partnerEarnings($id)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
// GET partner/{id}/details
|
||||
// agent_id = partner_agent.id
|
||||
// Joins: partner_policy (agent_id), partner_enquiry (agent_id),
|
||||
// partner_endorsement_request (agent_id)
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
public function partnerDetails($id)
|
||||
{
|
||||
$ref = [];
|
||||
|
||||
try {
|
||||
if (empty($id)) {
|
||||
return $this->respond([
|
||||
'status' => 'error',
|
||||
'code' => 400,
|
||||
'data' => [],
|
||||
'message' => 'Missing required parameter: id',
|
||||
], 200);
|
||||
}
|
||||
|
||||
// ── 1. Agent profile (partner_agent.id = $id)
|
||||
$agent = $this->db->table('partner_agent pa')
|
||||
->select('
|
||||
pa.id,
|
||||
pa.name AS agent_name,
|
||||
pa.agent_code,
|
||||
pa.mobile,
|
||||
pa.email,
|
||||
pa.is_active,
|
||||
ps.name AS manager_name
|
||||
')
|
||||
->join('partner_staff ps', 'ps.id = pa.manager_id', 'left')
|
||||
->where('pa.id', $id)
|
||||
->get()->getRowArray();
|
||||
|
||||
if (empty($agent)) {
|
||||
throw new \RuntimeException('Partner not found.', 404);
|
||||
}
|
||||
|
||||
// -- 2. Policy counts + premium + commission
|
||||
// mapped_policies = ALL policies under this agent (226)
|
||||
// issued_policies = policy_number IS NOT NULL AND is_active = 1 (217)
|
||||
// pending_policies = policy_number IS NULL (9 — raised but not yet issued)
|
||||
// total_premium / commission = from issued policies only
|
||||
$policyStats = $this->db->table('partner_policy pp')
|
||||
->select('
|
||||
COUNT(pp.id) AS mapped_policies,
|
||||
SUM(CASE WHEN pp.policy_number IS NOT NULL AND pp.is_active = 1 AND pp.premium_amount IS NOT NULL THEN 1 ELSE 0 END) AS issued_policies,
|
||||
SUM(CASE WHEN pp.policy_number IS NULL THEN 1 ELSE 0 END) AS pending_policies,
|
||||
COALESCE(SUM(CASE WHEN pp.policy_number IS NOT NULL THEN pp.premium_amount ELSE 0 END), 0) AS total_premium,
|
||||
COALESCE(SUM(CASE WHEN pp.policy_number IS NOT NULL THEN pp.premium_amount * 0.15 ELSE 0 END), 0) AS commission_earned
|
||||
')
|
||||
->where('pp.agent_id', $id)
|
||||
->get()->getRowArray();
|
||||
|
||||
// ── 3. Enquiry counts
|
||||
// enquiry_status enum: To be assigned | Assigned | In progress | Completed
|
||||
$enquiryStats = $this->db->table('partner_enquiry pe')
|
||||
->select('
|
||||
COUNT(pe.id) AS enquiry_total,
|
||||
SUM(CASE WHEN pe.enquiry_status = "Completed" THEN 1 ELSE 0 END) AS enquiry_completed,
|
||||
SUM(CASE WHEN pe.enquiry_status != "Completed" THEN 1 ELSE 0 END) AS enquiry_pending
|
||||
')
|
||||
->where('pe.agent_id', $id)
|
||||
->where('pe.is_active', 1)
|
||||
->get()->getRowArray();
|
||||
|
||||
// ── 4. Endorsement counts
|
||||
// status is varchar(20) — adjust "Completed" to match your actual values
|
||||
$endorseStats = $this->db->table('partner_endorsement_request per')
|
||||
->select('
|
||||
COUNT(per.id) AS endorsement_total,
|
||||
SUM(CASE WHEN per.status = "Completed" THEN 1 ELSE 0 END) AS endorsement_done,
|
||||
SUM(CASE WHEN per.status != "Completed" THEN 1 ELSE 0 END) AS endorsement_pending
|
||||
')
|
||||
->where('per.agent_id', $id)
|
||||
->where('per.is_active', 1)
|
||||
->get()->getRowArray();
|
||||
|
||||
// ── Merge everything
|
||||
$data = array_merge(
|
||||
$agent,
|
||||
[
|
||||
'status' => $agent['is_active'] ? 'Active' : 'Inactive',
|
||||
'commission_rate' => 15,
|
||||
],
|
||||
$policyStats ?? [],
|
||||
$enquiryStats ?? [],
|
||||
$endorseStats ?? [],
|
||||
);
|
||||
|
||||
$ref['message'] = 'Partner details retrieved successfully.';
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'data' => $data,
|
||||
'ref' => $ref,
|
||||
], 200);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
if ($e instanceof \RuntimeException && $e->getCode() === 404) {
|
||||
$ref['message'] = 'No Data Found';
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'data' => [],
|
||||
'message' => 'No Data Found',
|
||||
'ref' => $ref,
|
||||
], 200);
|
||||
}
|
||||
|
||||
$isDbError = $e instanceof \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
|| $e instanceof \mysqli_sql_exception
|
||||
|| $e instanceof \PDOException;
|
||||
|
||||
if ($isDbError) {
|
||||
$ref['debug_info'] = $e->getFile() . ' / LN : ' . $e->getLine();
|
||||
$ref['message'] = 'Database Error Occurred.';
|
||||
} else {
|
||||
$ref['message'] = 'An unexpected error occurred: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'error',
|
||||
'code' => 500,
|
||||
'data' => [],
|
||||
'ref' => $ref,
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
// GET partner/{id}/policies
|
||||
// partner_policy.agent_id = $id
|
||||
// holder_name → pp.insured_name (the actual insured person, NOT agent)
|
||||
// product → pp.product (varchar 50), falls back to pp.vehicle_type
|
||||
// policy_no → pp.policy_number
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
public function partnerPolicies($id)
|
||||
{
|
||||
$ref = [];
|
||||
|
||||
try {
|
||||
if (empty($id)) {
|
||||
return $this->respond([
|
||||
'status' => 'error',
|
||||
'code' => 400,
|
||||
'data' => [],
|
||||
'message' => 'Missing required parameter: id',
|
||||
], 200);
|
||||
}
|
||||
|
||||
$results = $this->db->table('partner_policy pp')
|
||||
->select('
|
||||
pp.policy_number AS policy_no,
|
||||
pp.insured_name AS holder_name,
|
||||
COALESCE(NULLIF(pp.product, ""), pp.vehicle_type) AS product,
|
||||
pp.premium_amount AS premium,
|
||||
DATE_FORMAT(pp.issued_date, "%d-%m-%Y") AS issued_date,
|
||||
DATE_FORMAT(pp.end_date, "%d-%m-%Y") AS expiry_date
|
||||
')
|
||||
->where('pp.agent_id', $id)
|
||||
->where('pp.policy_number IS NOT NULL')
|
||||
->where('pp.premium_amount IS NOT NULL')
|
||||
->where('pp.is_active', 1)
|
||||
->orderBy('pp.issued_date', 'DESC')
|
||||
->get()->getResultArray();
|
||||
|
||||
$ref['total_records'] = count($results);
|
||||
|
||||
if (empty($results)) {
|
||||
throw new \RuntimeException('No policies found for this partner.', 404);
|
||||
}
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'data' => $results,
|
||||
'ref' => $ref,
|
||||
], 200);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
if ($e instanceof \RuntimeException && $e->getCode() === 404) {
|
||||
$ref['message'] = 'No Data Found';
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'data' => [],
|
||||
'message' => 'No Data Found',
|
||||
'ref' => $ref,
|
||||
], 200);
|
||||
}
|
||||
|
||||
$isDbError = $e instanceof \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
|| $e instanceof \mysqli_sql_exception
|
||||
|| $e instanceof \PDOException;
|
||||
|
||||
if ($isDbError) {
|
||||
$ref['debug_info'] = $e->getFile() . ' / LN : ' . $e->getLine();
|
||||
$ref['message'] = 'Database Error Occurred.';
|
||||
} else {
|
||||
$ref['message'] = 'An unexpected error occurred: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'error',
|
||||
'code' => 500,
|
||||
'data' => [],
|
||||
'ref' => $ref,
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
// GET partner/{id}/renewals?days=20
|
||||
// partner_policy.agent_id = $id
|
||||
// holder_name → pp.insured_name
|
||||
// premium → pp.premium_amount
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
public function partnerRenewals($id)
|
||||
{
|
||||
$ref = [];
|
||||
$days = (int) ($this->request->getGet('days') ?? 20);
|
||||
|
||||
try {
|
||||
if (empty($id)) {
|
||||
return $this->respond([
|
||||
'status' => 'error',
|
||||
'code' => 400,
|
||||
'data' => [],
|
||||
'message' => 'Missing required parameter: id',
|
||||
], 200);
|
||||
}
|
||||
|
||||
$results = $this->db->table('partner_policy pp')
|
||||
->select('
|
||||
pp.policy_number AS policy_no,
|
||||
pp.insured_name AS holder_name,
|
||||
pp.premium_amount AS premium,
|
||||
DATE_FORMAT(pp.end_date, "%d-%m-%Y") AS end_date,
|
||||
DATEDIFF(pp.end_date, CURDATE()) AS days_left
|
||||
')
|
||||
->where('pp.agent_id', $id)
|
||||
->where('pp.is_active', 1)
|
||||
->where('pp.policy_number IS NOT NULL')
|
||||
->where('pp.premium_amount IS NOT NULL')
|
||||
->where('pp.end_date >= CURDATE()', null, false)
|
||||
->where("pp.end_date <= DATE_ADD(CURDATE(), INTERVAL {$days} DAY)", null, false)
|
||||
->orderBy('pp.end_date', 'ASC')
|
||||
->get()->getResultArray();
|
||||
|
||||
$ref['days_filter'] = $days;
|
||||
$ref['total_records'] = count($results);
|
||||
|
||||
if (empty($results)) {
|
||||
throw new \RuntimeException('No renewals due within ' . $days . ' days.', 404);
|
||||
}
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'data' => $results,
|
||||
'ref' => $ref,
|
||||
], 200);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
if ($e instanceof \RuntimeException && $e->getCode() === 404) {
|
||||
$ref['message'] = 'No Data Found';
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'data' => [],
|
||||
'message' => 'No Data Found',
|
||||
'ref' => $ref,
|
||||
], 200);
|
||||
}
|
||||
|
||||
$isDbError = $e instanceof \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
|| $e instanceof \mysqli_sql_exception
|
||||
|| $e instanceof \PDOException;
|
||||
|
||||
if ($isDbError) {
|
||||
$ref['debug_info'] = $e->getFile() . ' / LN : ' . $e->getLine();
|
||||
$ref['message'] = 'Database Error Occurred.';
|
||||
} else {
|
||||
$ref['message'] = 'An unexpected error occurred: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'error',
|
||||
'code' => 500,
|
||||
'data' => [],
|
||||
'ref' => $ref,
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
// GET partner/{id}/earnings
|
||||
// partner_policy.agent_id = $id
|
||||
// Groups by issued_date month → month_key (YYYY-MM), month_label (Month YYYY)
|
||||
// paid = MAX(is_data_accuracy_checked) — 1 if all policies in month are checked
|
||||
// No month_key param → returns ALL months (FY filtering done client-side in Dart)
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
public function partnerEarnings($id)
|
||||
{
|
||||
$ref = [];
|
||||
$monthKey = $this->request->getGet('month_key') ?? null;
|
||||
|
||||
try {
|
||||
if (empty($id)) {
|
||||
return $this->respond([
|
||||
'status' => 'error',
|
||||
'code' => 400,
|
||||
'data' => [],
|
||||
'message' => 'Missing required parameter: id',
|
||||
], 200);
|
||||
}
|
||||
|
||||
$builder = $this->db->table('partner_policy pp');
|
||||
|
||||
$builder->select("
|
||||
DATE_FORMAT(pp.issued_date, '%Y-%m') AS month_key,
|
||||
DATE_FORMAT(pp.issued_date, '%M %Y') AS month_label,
|
||||
COUNT(pp.id) AS policies,
|
||||
COALESCE(SUM(pp.premium_amount), 0) AS premium,
|
||||
COALESCE(SUM(pp.premium_amount * 0.15), 0) AS commission,
|
||||
COALESCE(SUM(pp.premium_amount * 0.15 * 0.10), 0) AS tds,
|
||||
COALESCE(SUM(pp.premium_amount * 0.15 * 0.90), 0) AS net_payout,
|
||||
MAX(pp.is_data_accuracy_checked) AS paid
|
||||
");
|
||||
|
||||
$builder->where('pp.agent_id', $id);
|
||||
$builder->where('pp.is_active', 1);
|
||||
$builder->where('pp.policy_number IS NOT NULL');
|
||||
$builder->where('pp.premium_amount IS NOT NULL');
|
||||
|
||||
if (!empty($monthKey)) {
|
||||
$builder->where("DATE_FORMAT(pp.issued_date, '%Y-%m')", $monthKey);
|
||||
}
|
||||
|
||||
$builder->groupBy("DATE_FORMAT(pp.issued_date, '%Y-%m')");
|
||||
$builder->orderBy('month_key', 'ASC');
|
||||
|
||||
$results = $builder->get()->getResultArray();
|
||||
|
||||
$ref['month_filter'] = $monthKey ?? 'all';
|
||||
$ref['total_records'] = count($results);
|
||||
|
||||
if (empty($results)) {
|
||||
throw new \RuntimeException('No earning data found for this partner.', 404);
|
||||
}
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'data' => $results,
|
||||
'ref' => $ref,
|
||||
], 200);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
if ($e instanceof \RuntimeException && $e->getCode() === 404) {
|
||||
$ref['message'] = 'No Data Found';
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'code' => 200,
|
||||
'data' => [],
|
||||
'message' => 'No Data Found',
|
||||
'ref' => $ref,
|
||||
], 200);
|
||||
}
|
||||
|
||||
$isDbError = $e instanceof \CodeIgniter\Database\Exceptions\DatabaseException
|
||||
|| $e instanceof \mysqli_sql_exception
|
||||
|| $e instanceof \PDOException;
|
||||
|
||||
if ($isDbError) {
|
||||
$ref['debug_info'] = $e->getFile() . ' / LN : ' . $e->getLine();
|
||||
$ref['message'] = 'Database Error Occurred.';
|
||||
} else {
|
||||
$ref['message'] = 'An unexpected error occurred: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'error',
|
||||
'code' => 500,
|
||||
'data' => [],
|
||||
'ref' => $ref,
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -47,10 +47,24 @@ class EndorsementController extends ResourceController
|
||||
|
||||
$builder = $this->EndorsementModel
|
||||
->select('partner_endorsement_request.*,
|
||||
CASE
|
||||
WHEN partner_endorsement_request.policy_start_date = "0000-00-00"
|
||||
OR YEAR(partner_endorsement_request.policy_start_date) < 2000
|
||||
THEN NULL
|
||||
ELSE DATE_FORMAT(partner_endorsement_request.policy_start_date, "%Y-%m-%d")
|
||||
END as policy_start_date,
|
||||
CASE
|
||||
WHEN partner_endorsement_request.policy_end_date = "0000-00-00"
|
||||
OR YEAR(partner_endorsement_request.policy_end_date) < 2000
|
||||
THEN NULL
|
||||
ELSE DATE_FORMAT(partner_endorsement_request.policy_end_date, "%Y-%m-%d")
|
||||
END as policy_end_date,
|
||||
i.name as insurer_name,
|
||||
i.short_name as insurer_short_name,
|
||||
ib.branch_name as insurer_branch,
|
||||
c.client_name, c.phone as client_phone, c.email as client_email,
|
||||
partner_endorsement_request.agent_id,
|
||||
pa.agent_code as agent_code,
|
||||
pa.name as agent_name,
|
||||
pp.enquiry_id,
|
||||
etm.endorsement_type as endorsement_type_value,
|
||||
@ -125,8 +139,14 @@ class EndorsementController extends ResourceController
|
||||
foreach ($data as $key => $row) {
|
||||
$data[$key]['created_at'] = date('d-m-Y h:i A', strtotime($row['created_at']));
|
||||
$data[$key]['updated_at'] = date('d-m-Y h:i A', strtotime($row['updated_at']));
|
||||
$data[$key]['policy_start_date'] = date('d-m-Y', strtotime($row['policy_start_date']));
|
||||
$data[$key]['policy_end_date'] = date('d-m-Y', strtotime($row['policy_end_date']));
|
||||
// ✅ NULL-safe date formatting
|
||||
$data[$key]['policy_start_date'] = (!empty($row['policy_start_date']) && $row['policy_start_date'] !== '0000-00-00')
|
||||
? date('d-m-Y', strtotime($row['policy_start_date']))
|
||||
: null;
|
||||
|
||||
$data[$key]['policy_end_date'] = (!empty($row['policy_end_date']) && $row['policy_end_date'] !== '0000-00-00')
|
||||
? date('d-m-Y', strtotime($row['policy_end_date']))
|
||||
: null;
|
||||
}
|
||||
|
||||
return $this->respond(['status' => 'success','code' => 200,'data' => $data], 200);
|
||||
@ -268,26 +288,58 @@ class EndorsementController extends ResourceController
|
||||
if (!$endorsement) { return $this->respond([ 'status' => 'failed', 'code' => 404,'data' => 'Endorsement not found' ], 404); }
|
||||
|
||||
// FILE UPLOAD
|
||||
$uploadedCompletionFile = $endorsement['endorsement_file_name'];
|
||||
$uploadedOriginalCompletionFile = $endorsement['endorsement_file_name'];
|
||||
|
||||
$uploadFile = $this->request->getFile('endorsement_file_name');
|
||||
$uploadOriginalFile = $this->request->getFile('endorsement_file_name');
|
||||
|
||||
if ($uploadFile && $uploadFile->isValid()) {
|
||||
if ($uploadOriginalFile && $uploadOriginalFile->isValid()) {
|
||||
|
||||
$uploadPath = WRITEPATH . 'uploads/endorsement/';
|
||||
if (!is_dir($uploadPath)) {
|
||||
mkdir($uploadPath, 0777, true);
|
||||
$uploadOriginalPath = WRITEPATH . 'uploads/endorsement/';
|
||||
if (!is_dir($uploadOriginalPath)) {
|
||||
mkdir($uploadOriginalPath, 0777, true);
|
||||
}
|
||||
|
||||
$uploadedCompletionFile = time() . '_' . $uploadFile->getRandomName();
|
||||
$uploadFile->move($uploadPath, $uploadedCompletionFile);
|
||||
$uploadedOriginalCompletionFile = time() . '_' . $uploadOriginalFile->getRandomName();
|
||||
$uploadOriginalFile->move($uploadOriginalPath, $uploadedOriginalCompletionFile); // ✅ fixed variable
|
||||
}
|
||||
|
||||
//COMMON UPDATE FIELDS
|
||||
// FILE REVISED UPLOAD
|
||||
$uploadedRevisedCompletionFile = $endorsement['endorsement_completion_file'];
|
||||
$uploadRevisedFile = $this->request->getFile('endorsement_completion_file');
|
||||
|
||||
if ($uploadRevisedFile && $uploadRevisedFile->isValid()) {
|
||||
$uploadRevisedPath = WRITEPATH . 'uploads/endorsement/endorsement_pdf/';
|
||||
if (!is_dir($uploadRevisedPath)) {
|
||||
mkdir($uploadRevisedPath, 0777, true);
|
||||
}
|
||||
$uploadedRevisedCompletionFile = time() . '_' . $uploadRevisedFile->getRandomName();
|
||||
$uploadRevisedFile->move($uploadRevisedPath, $uploadedRevisedCompletionFile);
|
||||
}
|
||||
|
||||
/*
|
||||
* COMMON UPDATE FIELDS
|
||||
* Keep update mapping explicit so fields sent by frontend are not silently ignored.
|
||||
*/
|
||||
|
||||
$updateData = [];
|
||||
|
||||
// Update only if provided (avoid null overwrite)
|
||||
if (isset($reqData['policy_from'])) {
|
||||
$updateData['policy_from'] = $reqData['policy_from'];
|
||||
}
|
||||
|
||||
if (isset($reqData['policy_number'])) {
|
||||
$updateData['policy_number'] = $reqData['policy_number'];
|
||||
}
|
||||
|
||||
if (isset($reqData['manager_id'])) {
|
||||
$updateData['manager_id'] = $reqData['manager_id'];
|
||||
}
|
||||
|
||||
if (isset($reqData['agent_id'])) {
|
||||
$updateData['agent_id'] = $reqData['agent_id'];
|
||||
}
|
||||
|
||||
if (isset($reqData['endorsement_type'])) {
|
||||
$updateData['endorsement_type'] = $reqData['endorsement_type'];
|
||||
}
|
||||
@ -316,17 +368,26 @@ class EndorsementController extends ResourceController
|
||||
$updateData['endorsement_premium'] = $reqData['endorsement_premium'];
|
||||
}
|
||||
|
||||
if (isset($reqData['commission_amount'])) {
|
||||
$updateData['commission_amount'] = $reqData['commission_amount'];
|
||||
}
|
||||
|
||||
if (isset($reqData['pending_days'])) {
|
||||
$updateData['pending_days'] = $reqData['pending_days'];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Always update both files (new or existing)
|
||||
$updateData['endorsement_file_name'] = $uploadedOriginalCompletionFile;
|
||||
$updateData['endorsement_completion_file'] = $uploadedRevisedCompletionFile; // ✅ fixed variable
|
||||
|
||||
// Always update file (either old or new)
|
||||
$updateData['endorsement_file_name'] = $uploadedCompletionFile;
|
||||
|
||||
// External Policy Editable Fields
|
||||
if ($endorsement['policy_from'] === 'External') {
|
||||
/*
|
||||
* External Policy Editable Fields
|
||||
* Use incoming policy_from first (if provided), fallback to stored value.
|
||||
* This prevents missing updates when stored value casing differs (external/External).
|
||||
*/
|
||||
$effectivePolicyFrom = $reqData['policy_from'] ?? $endorsement['policy_from'] ?? '';
|
||||
if (strcasecmp(trim((string)$effectivePolicyFrom), 'External') === 0) {
|
||||
|
||||
if (isset($reqData['insurer_id'])) {
|
||||
$updateData['insurer_id'] = $reqData['insurer_id'] ?? null;
|
||||
@ -372,6 +433,7 @@ class EndorsementController extends ResourceController
|
||||
|
||||
if (!empty($staff) && $staff['role_id'] == 4) {
|
||||
$updateData['is_data_accuracy_checked'] = 1;
|
||||
$updateData['status'] = "Closed" ;
|
||||
}
|
||||
}
|
||||
|
||||
@ -408,6 +470,30 @@ class EndorsementController extends ResourceController
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteEndorsement()
|
||||
{
|
||||
try {
|
||||
|
||||
$id = $this->request->getGet('id');
|
||||
|
||||
|
||||
// check if EndorsementModel exists
|
||||
$file = $this->EndorsementModel->find((int)$id);
|
||||
if (!$file) {
|
||||
return $this->respond(['status' => 'failed','code' => 200, 'data' => 'Endorsement not found'], 200);
|
||||
}
|
||||
|
||||
// update status
|
||||
$this->EndorsementModel->update($id, ['is_active' => 0]);
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success', 'code' => 200,'data' => "Endorsement Deleted"], 200);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->respond(['status' => 'failed','code' => 500,'data' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// public function createEndorsement()
|
||||
// {
|
||||
@ -556,6 +642,7 @@ class EndorsementController extends ResourceController
|
||||
{
|
||||
try {
|
||||
$id = $this->request->getGet('id');
|
||||
$type = $this->request->getGet('type'); // 'completion' or 'original'
|
||||
|
||||
if (!$id) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'ID is required'], 200);
|
||||
@ -568,7 +655,23 @@ class EndorsementController extends ResourceController
|
||||
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File not found'], 200);
|
||||
}
|
||||
|
||||
$filePath = WRITEPATH . 'uploads/endorsement/' . $fileRecord['endorsement_file_name'];
|
||||
// $filePath = WRITEPATH . 'uploads/endorsement/' . $fileRecord['endorsement_file_name'];
|
||||
|
||||
// ✅ If type is null/empty → original (default)
|
||||
// ✅ If type = 'completion' → endorsement_pdf/ subfolder
|
||||
if (!$type || $type === 'original') {
|
||||
$fileName = $fileRecord['endorsement_file_name'] ?? null;
|
||||
$subPath = '';
|
||||
} else if ($type === 'completion') {
|
||||
$fileName = $fileRecord['endorsement_completion_file'] ?? null;
|
||||
$subPath = 'endorsement_pdf/';
|
||||
}
|
||||
|
||||
if (!$fileName) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'No file uploaded yet'], 200);
|
||||
}
|
||||
|
||||
$filePath = WRITEPATH . 'uploads/endorsement/' . $subPath . $fileName;
|
||||
|
||||
if (!file_exists($filePath)) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File missing on server'], 200);
|
||||
@ -582,7 +685,7 @@ class EndorsementController extends ResourceController
|
||||
}
|
||||
}
|
||||
|
||||
public function EndorsementFilePath()
|
||||
public function EndorsementFilePathGOWTHAM()
|
||||
{
|
||||
try {
|
||||
$endorsementId = $this->request->getGet('endorsement_id');
|
||||
@ -623,11 +726,138 @@ class EndorsementController extends ResourceController
|
||||
}
|
||||
}
|
||||
|
||||
public function EndorsementFilePath()
|
||||
{
|
||||
try {
|
||||
$endorsementId = $this->request->getGet('endorsement_id');
|
||||
|
||||
if (!$endorsementId) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'endorsement_id is required'], 200);
|
||||
}
|
||||
|
||||
// Fetch record from DB
|
||||
$fileRecord = $this->EndorsementModel->where('is_active', 1)->find((int)$endorsementId);
|
||||
|
||||
if (!$fileRecord) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File not found in database'], 200);
|
||||
}
|
||||
|
||||
// ✅ STEP 1: Check completion file first
|
||||
$completionFile = $fileRecord['endorsement_completion_file'] ?? null;
|
||||
$originalFile = $fileRecord['endorsement_file_name'] ?? null;
|
||||
|
||||
|
||||
if (!empty($completionFile)) {
|
||||
// ✅ Use completion file path
|
||||
$fileName = $completionFile;
|
||||
$filePath = WRITEPATH . 'uploads/endorsement/endorsement_pdf/' . $fileName;
|
||||
} elseif (!empty($originalFile)) {
|
||||
// ✅ Fallback to original file path
|
||||
$fileName = $originalFile;
|
||||
$filePath = WRITEPATH . 'uploads/endorsement/' . $fileName;
|
||||
} else {
|
||||
// ❌ Neither file exists in DB
|
||||
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'No file uploaded yet'], 200);
|
||||
}
|
||||
|
||||
// ✅ STEP 2: Check file exists on disk
|
||||
if (!file_exists($filePath)) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File missing on server'], 200);
|
||||
}
|
||||
|
||||
// ✅ STEP 3: Stream file to browser / Flutter
|
||||
$mime = mime_content_type($filePath);
|
||||
|
||||
return $this->response
|
||||
->setHeader('Content-Type', $mime)
|
||||
->setHeader('Content-Disposition', 'inline; filename="' . $fileName . '"')
|
||||
->setBody(file_get_contents($filePath));
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 500, 'message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function uploadEndorsementFile()
|
||||
{
|
||||
try {
|
||||
$data = $this->request->getPost();
|
||||
|
||||
// ✅ Validate required POST fields first
|
||||
if (empty($data['id']) || empty($data['updated_by'])) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'Missing required fields'], 400);
|
||||
}
|
||||
|
||||
$uploadPath = WRITEPATH . 'uploads/endorsement/';
|
||||
$allowedTypes = ['application/pdf'];
|
||||
$pdfPath = $uploadPath . 'endorsement_pdf/';
|
||||
|
||||
$endorsementPdf = $this->request->getFile('endorsement_completion_file');
|
||||
|
||||
// ✅ Check file existence and validity BEFORE accessing its properties
|
||||
if (!$endorsementPdf || !$endorsementPdf->isValid()) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'No valid file received'], 400);
|
||||
}
|
||||
|
||||
// ✅ MIME validation only after confirming file exists
|
||||
if (!in_array($endorsementPdf->getMimeType(), $allowedTypes)) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'Only PDF files are allowed'], 400);
|
||||
}
|
||||
|
||||
// ✅ Fetch existing record to get old file name BEFORE uploading
|
||||
$existingRecord = $this->EndorsementModel->find($data['id']);
|
||||
$oldFileName = $existingRecord['endorsement_completion_file'] ?? null;
|
||||
|
||||
// ✅ Ensure upload directory exists
|
||||
if (!is_dir($pdfPath)) {
|
||||
mkdir($pdfPath, 0777, true);
|
||||
}
|
||||
|
||||
// ✅ Upload new file
|
||||
$pdfFileName = time() . '_' . $endorsementPdf->getRandomName();
|
||||
$endorsementPdf->move($pdfPath, $pdfFileName);
|
||||
|
||||
// ✅ Guard: ensure file was actually saved on disk
|
||||
if (!$pdfFileName || !file_exists($pdfPath . $pdfFileName)) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'File upload failed'], 400);
|
||||
}
|
||||
|
||||
// ✅ Update DB with new file name
|
||||
$updateData = [
|
||||
'endorsement_completion_file' => $pdfFileName,
|
||||
'updated_by' => $data['updated_by'],
|
||||
];
|
||||
|
||||
$updated = $this->EndorsementModel->update($data['id'], $updateData);
|
||||
|
||||
// ✅ DB update failed → delete the newly uploaded file to avoid orphan files
|
||||
if (!$updated) {
|
||||
if (file_exists($pdfPath . $pdfFileName)) {
|
||||
unlink($pdfPath . $pdfFileName);
|
||||
}
|
||||
log_message('error', 'DB update failed for endorsement ID: ' . $data['id'] . '. New file removed.');
|
||||
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => 'DB update failed, file not saved'], 500);
|
||||
}
|
||||
|
||||
// ✅ DB success → NOW safe to delete old file
|
||||
if ($oldFileName) {
|
||||
$oldFilePath = $pdfPath . $oldFileName;
|
||||
if (file_exists($oldFilePath)) {
|
||||
if (!unlink($oldFilePath)) {
|
||||
log_message('warning', 'DB updated but failed to delete old file: ' . $oldFilePath);
|
||||
} else {
|
||||
log_message('info', 'Old file deleted after successful DB update: ' . $oldFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_message('info', 'Endorsement PDF uploaded successfully. ID: ' . $data['id'] . ', File: ' . $pdfFileName);
|
||||
|
||||
return $this->respond(['status' => 'success', 'code' => 200, 'data' => $data['id']], 200);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
log_message('error', 'uploadEndorsementFile error: ' . $e->getMessage());
|
||||
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -186,11 +186,13 @@ class EnquiryController extends ResourceController
|
||||
}
|
||||
|
||||
//Get enquiry details
|
||||
$enquiry = $this->enquiryModel->select('partner_enquiry.* , I.name as insurer_name , I.short_name , VT.vehicle_type as vehicle_type ,PB.name as broker_name ,PA.name as agent_name,PA.agent_code')
|
||||
$enquiry = $this->enquiryModel->select('partner_enquiry.* , I.name as insurer_name , I.short_name , VT.vehicle_type as vehicle_type ,PB.name as broker_name ,PA.name as agent_name,PA.agent_code,PPMM.value as payment_mode_value')
|
||||
->join('insurers I', 'I.id = partner_enquiry.insurer_id', 'left')
|
||||
->join('vehicle_type VT', 'VT.id = partner_enquiry.vehicle_type_id', 'left')
|
||||
->join('partner_brokers PB', 'PB.id = partner_enquiry.broker_id', 'left')
|
||||
->join('partner_agent PA', 'PA.id = partner_enquiry.agent_id', 'left')
|
||||
->join('partner_quotation PQ', 'PQ.enquiry_id = partner_enquiry.id', 'left')
|
||||
->join('partner_payment_mode_master PPMM', 'PPMM.id = PQ.payment_mode_id', 'left')
|
||||
->where('partner_enquiry.id', $enquiry_id)
|
||||
->where('partner_enquiry.is_active', 1)
|
||||
->first();
|
||||
@ -834,7 +836,7 @@ class EnquiryController extends ResourceController
|
||||
try {
|
||||
$data = $this->request->getJSON(true);
|
||||
|
||||
print_r($data);die;
|
||||
// print_r($data);die;
|
||||
|
||||
if (!isset($data['id'])) {
|
||||
return $this->respond(['status' => 'failed', 'code' => 200, 'error' => 'ID Required'], 200);
|
||||
|
||||
@ -1546,11 +1546,12 @@ class ExcelExportController extends ResourceController
|
||||
'Insured Name',
|
||||
'Insurer',
|
||||
'Broker',
|
||||
'Partner Code And Name',
|
||||
'Contact Person',
|
||||
'Remarks',
|
||||
'Is Financial',
|
||||
'Endorsement Premium Amount',
|
||||
'Commission Amount'
|
||||
'Commission Amount',
|
||||
];
|
||||
|
||||
$finalData = [];
|
||||
@ -2479,6 +2480,7 @@ class ExcelExportController extends ResourceController
|
||||
per.insured_name,
|
||||
I.name AS insurer_name,
|
||||
pb.name AS broker_name,
|
||||
CONCAT(pa.agent_code, ' - ', pa.name) AS agent_details,
|
||||
per.contact_person,
|
||||
per.endorsement_description,
|
||||
per.financia_or_non_financial,
|
||||
@ -2548,4 +2550,179 @@ class ExcelExportController extends ResourceController
|
||||
}
|
||||
}
|
||||
|
||||
// grid/download?role=Manager means all list
|
||||
// grid/download?role=Manager&insurer=HDFC&vehicle_type=TWO%20WHEELER&plan_type=comp
|
||||
// grid/download?role=Manager&insurer=HDFC&rto=TN&segment=BIKE&plan_type=comp
|
||||
// grid/download?role=Manager&insurer=HDFC&segment=BIKE&vehicle_type=TWO%20WHEELER&plan_type=comp
|
||||
public function downloadExcelGrid()
|
||||
{
|
||||
try {
|
||||
$request = $this->request;
|
||||
|
||||
// ✅ Inputs
|
||||
$role = trim((string) ($request->getGet('role') ?? ''));
|
||||
$fileId = trim((string) ($request->getGet('file_id') ?? ''));
|
||||
$insurer = trim((string) ($request->getGet('insurer') ?? ''));
|
||||
$rto = trim((string) ($request->getGet('rto') ?? ''));
|
||||
$segment = trim((string) ($request->getGet('segment') ?? ''));
|
||||
$vehicleType = trim((string) ($request->getGet('vehicle_type') ?? ''));
|
||||
$planType = strtolower(trim((string) ($request->getGet('plan_type') ?? '')));
|
||||
$search = trim((string) ($request->getGet('search') ?? ''));
|
||||
$loggedId = trim((string) ($request->getGet('logged_id') ?? ''));
|
||||
|
||||
if ($role === '') {
|
||||
return $this->response->setJSON([
|
||||
'status' => 'failed',
|
||||
'code' => 400,
|
||||
'data' => 'Role is required to export payout grid.',
|
||||
]);
|
||||
}
|
||||
|
||||
$isAgent = strtolower($role) === 'agent';
|
||||
|
||||
// ✅ Get retention rate (only for Agent)
|
||||
$retentionRate = 0;
|
||||
if ($isAgent && $loggedId !== '') {
|
||||
$agent = $this->db->table('partner_agent')
|
||||
->select('retention_rate')
|
||||
->where('id', $loggedId)
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
$retentionRate = isset($agent['retention_rate']) ? (float)$agent['retention_rate'] : 0;
|
||||
}
|
||||
|
||||
// ✅ Main query
|
||||
$builder = $this->db->table('partner_insurance_payout_grid');
|
||||
|
||||
if ($fileId !== '') {
|
||||
$builder->where('partner_agent_incentive_file_id', $fileId);
|
||||
} else {
|
||||
$maxFileId = $this->db->table('partner_insurance_payout_grid')
|
||||
->selectMax('partner_agent_incentive_file_id')
|
||||
->get()
|
||||
->getRow()
|
||||
->partner_agent_incentive_file_id ?? null;
|
||||
|
||||
if (!empty($maxFileId)) {
|
||||
$builder->where('partner_agent_incentive_file_id', $maxFileId);
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Filters
|
||||
if ($insurer !== '') $builder->where('insurer', $insurer);
|
||||
if ($rto !== '') $builder->where('rto', $rto);
|
||||
if ($segment !== '') $builder->where('segment', $segment);
|
||||
if ($vehicleType !== '') $builder->where('vehicle_type', $vehicleType);
|
||||
|
||||
if (in_array($planType, ['comp', 'tp', 'od'])) {
|
||||
$builder->where("$planType IS NOT NULL", null, false)
|
||||
->where("$planType !=", '')
|
||||
->where("$planType !=", '0');
|
||||
}
|
||||
|
||||
// ✅ Search
|
||||
if ($search !== '') {
|
||||
$builder->groupStart()
|
||||
->like('insurer', $search)
|
||||
->orLike('vehicle_type', $search)
|
||||
->orLike('segment', $search)
|
||||
->orLike('rto', $search)
|
||||
->orLike('remarks', $search)
|
||||
->groupEnd();
|
||||
}
|
||||
|
||||
// ✅ Fetch rows
|
||||
$rows = $builder
|
||||
->select('id, insurer, vehicle_type, segment, rto, comp, tp, od, remarks')
|
||||
->orderBy('id', 'DESC')
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
// =========================
|
||||
// ✅ HEADERS
|
||||
// =========================
|
||||
$headers = ['S.No', 'Insurer', 'Vehicle Type', 'Segment', 'RTO'];
|
||||
|
||||
if ($isAgent) {
|
||||
if ($planType === 'comp') {
|
||||
$headers[] = 'Comp';
|
||||
} elseif ($planType === 'tp') {
|
||||
$headers[] = 'TP';
|
||||
} elseif ($planType === 'od') {
|
||||
$headers[] = 'OD';
|
||||
}
|
||||
} else {
|
||||
// ✅ Manager / Accounts
|
||||
$headers[] = 'Comp';
|
||||
$headers[] = 'TP';
|
||||
$headers[] = 'OD';
|
||||
}
|
||||
|
||||
$headers[] = 'Remarks';
|
||||
|
||||
// =========================
|
||||
// ✅ DATA
|
||||
// =========================
|
||||
$data = [];
|
||||
$sNo = 1;
|
||||
|
||||
foreach ($rows as $row) {
|
||||
|
||||
$comp = isset($row['comp']) ? (float)$row['comp'] : 0;
|
||||
$tp = isset($row['tp']) ? (float)$row['tp'] : 0;
|
||||
$od = isset($row['od']) ? (float)$row['od'] : 0;
|
||||
|
||||
// ✅ Apply retention only for Agent
|
||||
if ($isAgent) {
|
||||
$comp -= $retentionRate;
|
||||
$tp -= $retentionRate;
|
||||
$od -= $retentionRate;
|
||||
}
|
||||
|
||||
$line = [
|
||||
$sNo++,
|
||||
$row['insurer'] ?? '-',
|
||||
$row['vehicle_type'] ?? '-',
|
||||
$row['segment'] ?? '-',
|
||||
$row['rto'] ?? '-',
|
||||
];
|
||||
|
||||
// ✅ Dynamic columns (Agent only)
|
||||
if ($isAgent) {
|
||||
if ($planType === 'comp') {
|
||||
$line[] = $comp;
|
||||
} elseif ($planType === 'tp') {
|
||||
$line[] = $tp;
|
||||
} elseif ($planType === 'od') {
|
||||
$line[] = $od;
|
||||
}
|
||||
} else {
|
||||
// ✅ Manager / Accounts → always all
|
||||
$line[] = $comp;
|
||||
$line[] = $tp;
|
||||
$line[] = $od;
|
||||
}
|
||||
|
||||
$line[] = $row['remarks'] ?? '-';
|
||||
|
||||
$data[] = $line;
|
||||
}
|
||||
|
||||
// ✅ Export
|
||||
$fileName = "grid_" . date('Ymd_His') . ".xlsx";
|
||||
|
||||
return $this->streamExcelFile($headers, $data, 'Payout Grid', $fileName, false);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
return $this->response->setJSON([
|
||||
'status' => 'failed',
|
||||
'code' => 500,
|
||||
'data' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -413,6 +413,82 @@ class PolicyController extends ResourceController
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@ -181,8 +181,9 @@ if (!function_exists('createBDS')) {
|
||||
|
||||
if (!function_exists('format_date_for_database')) {
|
||||
|
||||
function format_date_for_database(string $date): ?string
|
||||
function format_date_for_database(?string $date): ?string
|
||||
{
|
||||
if (empty($date)) return null; // ✅ handle null/empty
|
||||
$dt = DateTime::createFromFormat('d-m-Y', $date);
|
||||
if ($dt) {
|
||||
return $dt->format('Y-m-d');
|
||||
|
||||
@ -15,6 +15,7 @@ class AgentIncentiveFileModel extends Model
|
||||
'agent_id',
|
||||
'incentive_month',
|
||||
'incentive_file_name',
|
||||
'file_type',
|
||||
'is_active',
|
||||
'created_by',
|
||||
'created_on',
|
||||
@ -32,8 +33,9 @@ class AgentIncentiveFileModel extends Model
|
||||
|
||||
// Validation rules (optional, add as per your need)
|
||||
protected $validationRules = [
|
||||
'agent_id' => 'required|integer',
|
||||
'incentive_month' => 'required|valid_date',
|
||||
'incentive_file_name'=> 'required|string|max_length[150]'
|
||||
'agent_id' => 'permit_empty|integer',
|
||||
'incentive_month' => 'required|valid_date',
|
||||
'incentive_file_name' => 'required|string|max_length[150]',
|
||||
'file_type' => 'permit_empty|max_length[50]'
|
||||
];
|
||||
}
|
||||
|
||||
78
app/Models/PartnerInsurancePayoutGridModel.php
Normal file
78
app/Models/PartnerInsurancePayoutGridModel.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use CodeIgniter\Model;
|
||||
|
||||
class PartnerInsurancePayoutGridModel extends Model
|
||||
{
|
||||
protected $table = 'partner_insurance_payout_grid';
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
protected $useAutoIncrement = true;
|
||||
protected $returnType = 'array';
|
||||
protected $useSoftDeletes = false;
|
||||
|
||||
protected $allowedFields = [
|
||||
'vehicle_type',
|
||||
'fuel',
|
||||
'insurer',
|
||||
'rto',
|
||||
'broker_name',
|
||||
'segment',
|
||||
'comp',
|
||||
'tp',
|
||||
'od',
|
||||
'remarks',
|
||||
'broker_comp',
|
||||
'broker_tp',
|
||||
'broker_od',
|
||||
'partner_agent_incentive_file_id',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
// Timestamps — map to the column names in your DDL
|
||||
protected $useTimestamps = true;
|
||||
protected $createdField = 'created_at';
|
||||
protected $updatedField = 'updated_at';
|
||||
|
||||
// Validation
|
||||
protected $validationRules = [
|
||||
'vehicle_type' => 'required|max_length[100]',
|
||||
'insurer' => 'required|max_length[45]',
|
||||
'segment' => 'required|max_length[100]',
|
||||
];
|
||||
|
||||
protected $validationMessages = [
|
||||
'vehicle_type' => ['required' => 'Vehicle type is required.'],
|
||||
'insurer' => ['required' => 'Insurer is required.'],
|
||||
'segment' => ['required' => 'Segment is required.'],
|
||||
];
|
||||
|
||||
protected $skipValidation = false;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Fetch grid filtered by vehicle_type
|
||||
// -------------------------------------------------------------------------
|
||||
public function getByVehicleType(string $vehicleType): array
|
||||
{
|
||||
return $this
|
||||
->where('vehicle_type', $vehicleType)
|
||||
->orderBy('insurer', 'ASC')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Fetch grid filtered by insurer + rto
|
||||
// -------------------------------------------------------------------------
|
||||
public function getByInsurerAndRto(string $insurer, string $rto): array
|
||||
{
|
||||
return $this
|
||||
->where('insurer', $insurer)
|
||||
->where('rto', $rto)
|
||||
->orderBy('vehicle_type', 'ASC')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
}
|
||||
@ -30,6 +30,7 @@ class PolicyModel extends Model
|
||||
'pt_oc_share_details_id',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'updated_on',
|
||||
|
||||
// newly added
|
||||
'tp',
|
||||
|
||||
BIN
writable/uploads/sample_partner_grid_file.xls
Normal file
BIN
writable/uploads/sample_partner_grid_file.xls
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user