FIX_Changes and Additional Requirements 3,4,5

This commit is contained in:
sanjeev.p 2026-03-24 17:57:34 +05:30
parent d9f533ac74
commit ce7033654a
7 changed files with 1460 additions and 16 deletions

View File

@ -73,6 +73,10 @@ $routes->group('api', ['filter' => ["jwtAuth:1,2,3,4,agent","appSignature"] ], f
$routes->post('agent/uploadAgentIncentiveFile', 'AgentController::uploadAgentIncentiveFile');
$routes->get('agent/deleteAgentIncentiveFile', 'AgentController::deleteAgentIncentiveFile');
$routes->get('agent/downloadAgentIncentiveFile', 'AgentController::downloadAgentIncentiveFile');
$routes->get('agent/downloadSamplePartnerGridExcel', 'AgentController::downloadSamplePartnerGridExcel');
$routes->get('agent/monthlyCommissionGridFilters', 'AgentController::monthlyCommissionGridFilters');
$routes->get('agent/monthlyCommissionGridList', 'AgentController::monthlyCommissionGridList');
$routes->get('agent/downloadMonthlyCommissionGrid', 'AgentController::downloadMonthlyCommissionGrid');
//Staff
@ -201,9 +205,14 @@ $routes->group('api', ['filter' => ["jwtAuth:1,2,3,4,agent","appSignature"] ], f
$routes->get('invoice/list', 'InvoiceController::invoiceList');
$routes->get('invoice/details', 'InvoiceController::findInvoiceWithItems');
$routes->post('invoice/create-or-update', 'InvoiceController::createOrUpdateInvoice');
$routes->post('invoice/add-payment', 'InvoiceController::addInvoicePayment');
$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

View File

@ -5,16 +5,41 @@ use CodeIgniter\RESTful\ResourceController;
use App\Controllers\BaseController;
use App\Models\AgentModel;
use App\Models\AgentIncentiveFileModel;
use App\Models\PartnerGridDetailsModel;
use PhpOffice\PhpSpreadsheet\IOFactory;
class AgentController extends ResourceController
{
protected $AgentModel;
protected $AgentIncentiveFileModel;
protected $PartnerGridDetailsModel;
public function __construct()
{
helper('jwt_helper');
$this->AgentModel = new AgentModel();
$this->AgentIncentiveFileModel = new AgentIncentiveFileModel();
$this->PartnerGridDetailsModel = new PartnerGridDetailsModel();
}
private function getAuthenticatedUserData(): ?object
{
$header = $this->request->getHeaderLine('Authorization');
if (!$header || !preg_match('/Bearer\s(\S+)/', $header, $matches)) {
return null;
}
$decodedToken = validateJWT($matches[1]);
if (!$decodedToken || !isset($decodedToken['data'])) {
return null;
}
return $decodedToken['data'];
}
private function canManagePartnerGrid(?string $role): bool
{
return in_array((string) $role, ['1', '4'], true);
}
// List of all agents
@ -267,10 +292,30 @@ class AgentController extends ResourceController
public function agentIncentiveFileList()
{
try{
$authUser = $this->getAuthenticatedUserData();
if (!$authUser) {
return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401);
}
$agent_id = $this->request->getGet('agent_id');
$roleId = (string) ($authUser->role_id ?? 'agent');
$agentIdFromRequest = $this->request->getGet('agent_id');
$fileType = trim((string) ($this->request->getGet('file_type') ?? ''));
$data = $this->AgentIncentiveFileModel->where('agent_id',$agent_id)->where('is_active',1)->findAll();
// Partner can only view own files; manager/accounts can view all.
$agentId = $this->canManagePartnerGrid($roleId) ? $agentIdFromRequest : ($authUser->id ?? null);
if (empty($agentId)) {
return $this->respond(['status' => 'failed', 'code' => 400, 'data' => 'agent_id is required'], 200);
}
$builder = $this->AgentIncentiveFileModel
->where('agent_id', (int) $agentId)
->where('is_active', 1);
if ($fileType !== '') {
$builder->where('file_type', $fileType);
}
$data = $builder->orderBy('id', 'DESC')->findAll();
return $this->respond(['status' => 'success', 'code' => 200, 'data' => $data], 200);
@ -285,13 +330,39 @@ class AgentController extends ResourceController
{
try{
$data = $this->request->getPost();
//duplicate check
$duplicateData = $this->AgentIncentiveFileModel->where('agent_id',$data['agent_id'])->where('incentive_month',$data['incentive_month'])->first();
if(!empty($duplicateData)){
return $this->respond(['status' => 'failed', 'code' => 200, 'data' => 'Duplicate Entry.'], 200);
$authUser = $this->getAuthenticatedUserData();
if (!$authUser) {
return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401);
}
$roleId = (string) ($authUser->role_id ?? 'agent');
$isGridUpload = isset($data['file_type']) && $data['file_type'] === 'grid';
if ($isGridUpload && !$this->canManagePartnerGrid($roleId)) {
return $this->respond([
'status' => 'failed',
'code' => 403,
'data' => 'Only Manager and Accounts can upload/edit partner grid files'
], 403);
}
if ($isGridUpload) {
$this->uploadGridFile($data);
} else {
$duplicateData = $this->AgentIncentiveFileModel
->where('agent_id', $data['agent_id'])
->where('incentive_month', $data['incentive_month'])
->first();
if (!empty($duplicateData)) {
return $this->respond([
'status' => 'failed',
'code' => 200,
'data' => 'Duplicate Entry.'
], 200);
}
}
// handle file uploads
$incentiveFile = $this->request->getFile('incentive_file_name');
@ -311,6 +382,7 @@ class AgentController extends ResourceController
'agent_id' => $data['agent_id'],
'incentive_month' => $data['incentive_month'],
'incentive_file_name' => $incentiveFileName,
'file_type' => $data['file_type'] ?? 'incentive',
'created_by' => $data['created_by'] ?? null
];
@ -323,10 +395,292 @@ class AgentController extends ResourceController
}
}
// ─────────────────────────────────────────
// Grid Functionality
// ─────────────────────────────────────────
public function uploadGridFile($data){
$gridFile = $this->request->getFile('incentive_file_name');
if (!$gridFile || !$gridFile->isValid()) {
throw new \RuntimeException('Valid grid file is required');
}
$extension = strtolower((string) $gridFile->getExtension());
if (!in_array($extension, ['xlsx', 'xls', 'csv'], true)) {
throw new \RuntimeException('Only xlsx, xls, csv grid files are allowed');
}
$spreadsheet = IOFactory::load($gridFile->getTempName());
$rows = $spreadsheet->getActiveSheet()->toArray(null, true, true, false);
if (empty($rows)) {
throw new \RuntimeException('Grid file is empty');
}
$normalize = static function ($value): string {
$value = strtolower(trim((string) $value));
return preg_replace('/[^a-z0-9]+/', '', $value) ?? '';
};
$toNumber = static function ($value): float {
if (is_numeric($value)) {
return (float) $value;
}
if (preg_match('/-?\d+(\.\d+)?/', (string) $value, $matches)) {
return (float) $matches[0];
}
return 0.0;
};
$toNullable = static function ($value): ?string {
$value = trim((string) $value);
return $value === '' ? null : $value;
};
$formatNumber = static function (float $value): string {
return rtrim(rtrim(number_format($value, 2, '.', ''), '0'), '.');
};
$parsePremium = static function ($value) use ($formatNumber): ?string {
$source = trim((string) $value);
if ($source === '') {
return null;
}
if (preg_match('/-?\d+(?:\.\d+)?/', $source, $matches) !== 1) {
return null;
}
return $formatNumber((float) $matches[0]);
};
// Fixed format: first row is header and data starts from row 2.
$headerRowIndex = 0;
$headerMap = [];
$headerRow = $rows[$headerRowIndex] ?? [];
foreach ($headerRow as $colIndex => $cell) {
$normalizedHeader = $normalize($cell);
if ($normalizedHeader === 'type') {
$headerMap['vehicle_type_id'] = $colIndex;
} elseif ($normalizedHeader === 'insurer') {
$headerMap['insurer_id'] = $colIndex;
} elseif ($normalizedHeader === 'rto') {
$headerMap['rto_id'] = $colIndex;
} elseif ($normalizedHeader === 'segment') {
$headerMap['segment_id'] = $colIndex;
} elseif ($normalizedHeader === 'comp') {
$headerMap['comp'] = $colIndex;
} elseif ($normalizedHeader === 'tp') {
$headerMap['tp'] = $colIndex;
} elseif ($normalizedHeader === 'fuel') {
$headerMap['fuel'] = $colIndex;
} elseif ($normalizedHeader === 'remarks') {
$headerMap['remarks'] = $colIndex;
}
}
if (!isset($headerMap['insurer_id'], $headerMap['rto_id'], $headerMap['segment_id'], $headerMap['vehicle_type_id'])) {
throw new \RuntimeException('Invalid grid header. Required: TYPE, INSURER, RTO, SEGMENT');
}
$defaultPartnerId = !empty($data['agent_id']) ? (int) $data['agent_id'] : null;
$createdBy = $data['created_by'] ?? null;
$db = \Config\Database::connect();
$vehicleTypeMaster = $db->table('vehicle_type')->select('id, vehicle_type')->where('is_active', 1)->get()->getResultArray();
$insurerMaster = $db->table('insurers')->select('id, name, short_name')->where('is_active', 1)->get()->getResultArray();
$rtoMaster = $db->table('rto_master')->select('id, rto_code, rto_name')->where('is_active', 1)->get()->getResultArray();
$partnerMaster = $db->table('partner_agent')->select('id, name, agent_code, retention_rate')->where('is_active', 1)->get()->getResultArray();
$insurerMap = [];
foreach ($insurerMaster as $item) {
$insurerMap[$normalize($item['name'])] = (int) $item['id'];
$insurerMap[$normalize($item['short_name'])] = (int) $item['id'];
$insurerMap[(string) $item['id']] = (int) $item['id'];
}
$rtoMap = [];
foreach ($rtoMaster as $item) {
$rtoMap[$normalize($item['rto_code'])] = (int) $item['id'];
$rtoMap[$normalize($item['rto_name'])] = (int) $item['id'];
$rtoMap[(string) $item['id']] = (int) $item['id'];
}
$partnerMap = [];
$partnerRetentionMap = [];
foreach ($partnerMaster as $item) {
$id = (int) $item['id'];
$partnerMap[$normalize($item['name'])] = $id;
$partnerMap[$normalize($item['agent_code'])] = $id;
$partnerMap[(string) $id] = $id;
$partnerRetentionMap[$id] = $toNumber($item['retention_rate'] ?? 0);
}
$insertCount = 0;
$updateCount = 0;
$skipStats = [
'empty_row' => 0,
'missing_required_columns' => 0,
'master_mapping_failed' => 0,
'partner_not_found' => 0,
];
$skipSamples = [];
for ($i = $headerRowIndex + 1, $count = count($rows); $i < $count; $i++) {
$row = $rows[$i];
$hasAnyData = false;
foreach ($row as $cellValue) {
if (trim((string) $cellValue) !== '') {
$hasAnyData = true;
break;
}
}
if (!$hasAnyData) {
$skipStats['empty_row']++;
continue;
}
$insurerRaw = trim((string) ($row[$headerMap['insurer_id']] ?? ''));
$rtoRaw = trim((string) ($row[$headerMap['rto_id']] ?? ''));
$segmentRaw = trim((string) ($row[$headerMap['segment_id']] ?? ''));
$vehicleTypeRaw = trim((string) ($row[$headerMap['vehicle_type_id']] ?? $segmentRaw));
if ($insurerRaw === '' || $rtoRaw === '' || $segmentRaw === '' || $vehicleTypeRaw === '') {
$skipStats['missing_required_columns']++;
if (count($skipSamples) < 15) {
$skipSamples[] = [
'row' => $i + 1,
'reason' => 'missing_required_columns',
'insurer' => $insurerRaw,
'rto' => $rtoRaw,
'segment' => $segmentRaw,
'vehicle_type' => $vehicleTypeRaw,
];
}
continue;
}
$insurerId = $insurerMap[$normalize($insurerRaw)] ?? null;
if ($insurerId === null && ctype_digit($insurerRaw) && isset($insurerMap[$insurerRaw])) {
$insurerId = $insurerMap[$insurerRaw];
}
$rtoId = $rtoMap[$normalize($rtoRaw)] ?? null;
if ($rtoId === null && ctype_digit($rtoRaw) && isset($rtoMap[$rtoRaw])) {
$rtoId = $rtoMap[$rtoRaw];
}
if (empty($insurerId) || empty($rtoId)) {
$skipStats['master_mapping_failed']++;
if (count($skipSamples) < 15) {
$skipSamples[] = [
'row' => $i + 1,
'reason' => 'master_mapping_failed',
'insurer' => $insurerRaw,
'rto' => $rtoRaw,
'segment' => $segmentRaw,
'vehicle_type' => $vehicleTypeRaw,
];
}
continue;
}
$comp = trim((string) ($row[$headerMap['comp']] ?? ''));
$tp = trim((string) ($row[$headerMap['tp']] ?? ''));
$remarks = trim((string) ($row[$headerMap['remarks']] ?? ''));
$fuelRaw = trim((string) ($row[$headerMap['fuel']] ?? ''));
$fuel = $toNullable($fuelRaw);
$partnerRaw = trim((string) ($row[$headerMap['partner_id']] ?? ''));
$partnerId = null;
if ($partnerRaw !== '') {
$partnerId = $partnerMap[$normalize($partnerRaw)] ?? null;
if ($partnerId === null && ctype_digit($partnerRaw) && isset($partnerMap[$partnerRaw])) {
$partnerId = $partnerMap[$partnerRaw];
}
}
if (empty($partnerId) && !empty($defaultPartnerId)) {
$partnerId = $defaultPartnerId;
}
if (empty($partnerId)) {
$skipStats['partner_not_found']++;
if (count($skipSamples) < 15) {
$skipSamples[] = [
'row' => $i + 1,
'reason' => 'partner_not_found',
'partner' => $partnerRaw,
];
}
continue;
}
$retentionRate = $partnerRetentionMap[(int) $partnerId] ?? 0.0;
$compSanitized = $parsePremium($comp);
$tpSanitized = $parsePremium($tp);
$partnerComp = $compSanitized !== null ? $formatNumber($retentionRate + (float) $compSanitized) : null;
$partnerTp = $tpSanitized !== null ? $formatNumber($retentionRate + (float) $tpSanitized) : null;
$recordData = [
'vehicle_type_id' => $vehicleTypeRaw,
'insurer_id' => (string) $insurerId,
'rto_id' => (string) $rtoId,
'segment_id' => $segmentRaw,
'comp' => $compSanitized,
'tp' => $tpSanitized,
'fuel' => $fuel,
'remarks' => $toNullable($remarks),
'partner_id' => (string) $partnerId,
'partner_comp' => $partnerComp,
'partner_tp' => $partnerTp,
];
$existing = $this->PartnerGridDetailsModel
->where('vehicle_type_id', $vehicleTypeRaw)
->where('insurer_id', (string) $insurerId)
->where('rto_id', (string) $rtoId)
->where('segment_id', $segmentRaw)
->where('partner_id', (string) $partnerId)
->first();
if ($existing) {
$recordData['updated_by'] = $createdBy;
$this->PartnerGridDetailsModel->update((int) $existing['id'], $recordData);
$updateCount++;
} else {
$recordData['created_by'] = $createdBy;
$this->PartnerGridDetailsModel->insert($recordData);
$insertCount++;
}
}
if ($insertCount === 0 && $updateCount === 0) {
$debugData = [
'message' => 'No valid rows found in grid file',
'header_row' => $headerRowIndex + 1,
'detected_headers' => array_keys($headerMap),
'skip_stats' => $skipStats,
'skip_samples' => $skipSamples,
];
throw new \RuntimeException(json_encode($debugData, JSON_UNESCAPED_SLASHES));
}
return true;
}
// Delete agent incentive file
public function deleteAgentIncentiveFile()
{
try {
$authUser = $this->getAuthenticatedUserData();
if (!$authUser) {
return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401);
}
$roleId = (string) ($authUser->role_id ?? 'agent');
$id = $this->request->getGet('id');
@ -337,6 +691,14 @@ class AgentController extends ResourceController
return $this->respond(['status' => 'failed','code' => 200, 'data' => 'File not found'], 200);
}
if (($file['file_type'] ?? '') === 'grid' && !$this->canManagePartnerGrid($roleId)) {
return $this->respond([
'status' => 'failed',
'code' => 403,
'data' => 'Only Manager and Accounts can edit/delete partner grid files'
], 403);
}
// update status
$this->AgentIncentiveFileModel->update($id, ['is_active' => 0]);
@ -352,6 +714,11 @@ class AgentController extends ResourceController
public function downloadAgentIncentiveFile()
{
try {
$authUser = $this->getAuthenticatedUserData();
if (!$authUser) {
return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401);
}
$roleId = (string) ($authUser->role_id ?? 'agent');
$id = $this->request->getGet('id');
if (!$id) {
@ -365,6 +732,10 @@ class AgentController extends ResourceController
return $this->respond(['status' => 'failed', 'code' => 404, 'data' => 'File not found'], 200);
}
if (!$this->canManagePartnerGrid($roleId) && (int) $fileRecord['agent_id'] !== (int) ($authUser->id ?? 0)) {
return $this->respond(['status' => 'failed', 'code' => 403, 'data' => 'Access denied'], 403);
}
$filePath = WRITEPATH . 'uploads/agent/incentive_file/' . $fileRecord['incentive_file_name'];
if (!file_exists($filePath)) {
@ -379,10 +750,344 @@ class AgentController extends ResourceController
}
}
// Download sample partner grid excel
public function downloadSamplePartnerGridExcel()
{
try {
$filePath = WRITEPATH . 'uploads/sample_partner_grid_file.xlsx';
if (!file_exists($filePath)) {
return $this->respond([
'status' => 'failed',
'code' => 404,
'data' => 'Sample partner grid file not found on server',
], 200);
}
return $this->response->download($filePath, null);
} catch (\Exception $e) {
return $this->respond([
'status' => 'failed',
'code' => 500,
'data' => $e->getMessage(),
], 500);
}
}
// Monthly commission grid filters
public function monthlyCommissionGridFilters()
{
try {
$authUser = $this->getAuthenticatedUserData();
if (!$authUser) {
return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401);
}
$db = \Config\Database::connect();
$rtoMaster = $db->table('rto_master')
->select('id, rto_code, rto_name')
->where('is_active', 1)
->orderBy('rto_code', 'ASC')
->get()
->getResultArray();
$segmentMaster = $db->table('vehicle_type')
->select('id, vehicle_type')
->where('is_active', 1)
->orderBy('vehicle_type', 'ASC')
->get()
->getResultArray();
$planMaster = $db->table('partner_insurance_plan_type_master')
->select('id, insurance_plan_type')
->where('is_active', 1)
->orderBy('insurance_plan_type', 'ASC')
->get()
->getResultArray();
$partnerMaster = $db->table('partner_agent')
->select('id, name, agent_code, retention_rate')
->where('is_active', 1)
->orderBy('name', 'ASC')
->get()
->getResultArray();
$monthRows = $db->table('partner_grid_details')
->select("DATE_FORMAT(created_at, '%Y-%m') AS month_key")
->where('created_at IS NOT NULL', null, false)
->groupBy("DATE_FORMAT(created_at, '%Y-%m')")
->orderBy('month_key', 'DESC')
->get()
->getResultArray();
$months = array_values(array_filter(array_map(static function ($row) {
return $row['month_key'] ?? null;
}, $monthRows)));
$latestMonth = !empty($months) ? $months[0] : date('Y-m');
return $this->respond([
'status' => 'success',
'code' => 200,
'data' => [
'rto_master' => $rtoMaster,
'segment_master' => $segmentMaster,
'plan_master' => $planMaster,
'partner_master' => $partnerMaster,
'months' => $months,
'default_month' => $latestMonth,
],
], 200);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
}
}
// Monthly commission grid list
public function monthlyCommissionGridList()
{
try {
$authUser = $this->getAuthenticatedUserData();
if (!$authUser) {
return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401);
}
$partnerId = (int) ($this->request->getGet('partner_id') ?? 0);
$rtoId = (int) ($this->request->getGet('rto_id') ?? 0);
$segmentId = (int) ($this->request->getGet('segment_id') ?? 0);
$planType = trim((string) ($this->request->getGet('insurance_plan_type') ?? ''));
$month = trim((string) ($this->request->getGet('month') ?? ''));
if ($partnerId <= 0 || $rtoId <= 0 || $segmentId <= 0 || $planType === '') {
return $this->respond([
'status' => 'failed',
'code' => 200,
'data' => 'partner_id, rto_id, segment_id and insurance_plan_type are required',
], 200);
}
$normalizedPlan = strtolower($planType);
$db = \Config\Database::connect();
$latestRow = $db->table('partner_grid_details')
->select("DATE_FORMAT(created_at, '%Y-%m') AS month_key")
->where('created_at IS NOT NULL', null, false)
->orderBy('created_at', 'DESC')
->get(1)
->getRowArray();
$effectiveMonth = $month !== '' ? $month : (($latestRow['month_key'] ?? '') ?: date('Y-m'));
$rows = $db->table('partner_grid_details pgd')
->select("
pgd.id,
pgd.partner_id,
pa.name AS partner_name,
pa.agent_code,
COALESCE(pa.retention_rate, 0) AS retention_rate,
pgd.rto_id,
rm.rto_code,
rm.rto_name,
pgd.segment,
pgd.vehicle_type_id,
vt.vehicle_type AS vehicle_type_name,
pgd.comp,
pgd.tp,
pgd.fuel,
pgd.remarks,
DATE_FORMAT(pgd.created_at, '%Y-%m') AS month_key
")
->join('partner_agent pa', 'pa.id = pgd.partner_id', 'left')
->join('rto_master rm', 'rm.id = pgd.rto_id', 'left')
->join('vehicle_type vt', 'vt.id = pgd.vehicle_type_id', 'left')
->where('pgd.partner_id', $partnerId)
->where('pgd.rto_id', $rtoId)
->where('pgd.segment_id', $segmentId)
->where("DATE_FORMAT(pgd.created_at, '%Y-%m') =", $effectiveMonth)
->orderBy('pgd.id', 'DESC')
->get()
->getResultArray();
$toPercent = static function ($value): float {
if ($value === null) {
return 0.0;
}
if (is_numeric($value)) {
return (float) $value;
}
if (preg_match('/-?\d+(\.\d+)?/', (string) $value, $matches)) {
return (float) $matches[0];
}
return 0.0;
};
$result = [];
foreach ($rows as $row) {
$retention = $toPercent($row['retention_rate'] ?? 0);
$compRate = $toPercent($row['comp'] ?? 0);
$tpRate = $toPercent($row['tp'] ?? 0);
if (strpos($normalizedPlan, 'third') !== false || $normalizedPlan === 'tp') {
$gridRate = $tpRate;
} else {
// Comprehensive + Own Damage use COMP column.
$gridRate = $compRate;
}
$netRate = $gridRate - $retention;
$row['insurance_plan_type'] = $planType;
$row['month'] = $effectiveMonth;
$row['grid_rate_percentage'] = round($gridRate, 2);
$row['retention_rate_percentage'] = round($retention, 2);
$row['final_commission_percentage'] = round($netRate, 2);
$result[] = $row;
}
return $this->respond([
'status' => 'success',
'code' => 200,
'data' => $result,
'meta' => [
'month' => $effectiveMonth,
'insurance_plan_type' => $planType,
],
], 200);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
}
}
// Monthly commission grid download
public function downloadMonthlyCommissionGrid()
{
try {
$authUser = $this->getAuthenticatedUserData();
if (!$authUser) {
return $this->respond(['status' => 'failed', 'code' => 401, 'data' => 'Unauthorized'], 401);
}
$partnerId = (int) ($this->request->getGet('partner_id') ?? 0);
$rtoId = (int) ($this->request->getGet('rto_id') ?? 0);
$segmentId = (int) ($this->request->getGet('segment_id') ?? 0);
$planType = trim((string) ($this->request->getGet('insurance_plan_type') ?? ''));
$month = trim((string) ($this->request->getGet('month') ?? ''));
if ($partnerId <= 0 || $rtoId <= 0 || $segmentId <= 0 || $planType === '') {
return $this->respond([
'status' => 'failed',
'code' => 200,
'data' => 'partner_id, rto_id, segment_id and insurance_plan_type are required',
], 200);
}
$normalizedPlan = strtolower($planType);
$db = \Config\Database::connect();
$latestRow = $db->table('partner_grid_details')
->select("DATE_FORMAT(created_at, '%Y-%m') AS month_key")
->where('created_at IS NOT NULL', null, false)
->orderBy('created_at', 'DESC')
->get(1)
->getRowArray();
$effectiveMonth = $month !== '' ? $month : (($latestRow['month_key'] ?? '') ?: date('Y-m'));
$sourceRows = $db->table('partner_grid_details pgd')
->select("
pa.name AS partner_name,
pa.agent_code,
COALESCE(pa.retention_rate, 0) AS retention_rate,
rm.rto_code,
rm.rto_name,
vt.vehicle_type AS segment_name,
pgd.comp,
pgd.tp
")
->join('partner_agent pa', 'pa.id = pgd.partner_id', 'left')
->join('rto_master rm', 'rm.id = pgd.rto_id', 'left')
->join('vehicle_type vt', 'vt.id = pgd.segment_id', 'left')
->where('pgd.partner_id', $partnerId)
->where('pgd.rto_id', $rtoId)
->where('pgd.segment_id', $segmentId)
->where("DATE_FORMAT(pgd.created_at, '%Y-%m') =", $effectiveMonth)
->orderBy('pgd.id', 'DESC')
->get()
->getResultArray();
$toPercent = static function ($value): float {
if ($value === null) {
return 0.0;
}
if (is_numeric($value)) {
return (float) $value;
}
if (preg_match('/-?\d+(\.\d+)?/', (string) $value, $matches)) {
return (float) $matches[0];
}
return 0.0;
};
$rows = [];
foreach ($sourceRows as $row) {
$retention = $toPercent($row['retention_rate'] ?? 0);
$compRate = $toPercent($row['comp'] ?? 0);
$tpRate = $toPercent($row['tp'] ?? 0);
$gridRate = (strpos($normalizedPlan, 'third') !== false || $normalizedPlan === 'tp') ? $tpRate : $compRate;
$row['month'] = $effectiveMonth;
$row['insurance_plan_type'] = $planType;
$row['grid_rate_percentage'] = round($gridRate, 2);
$row['retention_rate_percentage'] = round($retention, 2);
$row['final_commission_percentage'] = round($gridRate - $retention, 2);
$rows[] = $row;
}
if (empty($rows)) {
return $this->respond([
'status' => 'failed',
'code' => 404,
'data' => 'No data found for the selected filters',
], 200);
}
$safePlan = preg_replace('/[^a-zA-Z0-9]+/', '_', strtolower($planType)) ?: 'plan';
$fileName = "monthly_commission_{$effectiveMonth}_{$safePlan}.csv";
$tmpFile = WRITEPATH . 'uploads/temp/' . $fileName;
if (!is_dir(dirname($tmpFile))) {
mkdir(dirname($tmpFile), 0777, true);
}
$fp = fopen($tmpFile, 'w');
fputcsv($fp, [
'Month',
'Partner',
'Partner Code',
'RTO',
'Segment',
'Insurance Plan',
'Grid %',
'Retention %',
'Final Commission %',
]);
foreach ($rows as $row) {
fputcsv($fp, [
$row['month'] ?? $effectiveMonth,
$row['partner_name'] ?? '',
$row['agent_code'] ?? '',
trim((string) (($row['rto_code'] ?? '') . ' - ' . ($row['rto_name'] ?? '')), ' -'),
$row['segment_name'] ?? '',
$row['insurance_plan_type'] ?? $planType,
$row['grid_rate_percentage'] ?? 0,
$row['retention_rate_percentage'] ?? 0,
$row['final_commission_percentage'] ?? 0,
]);
}
fclose($fp);
return $this->response->download($tmpFile, null)->setFileName($fileName);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'code' => 500, 'data' => $e->getMessage()], 500);
}
}
}

View File

@ -8,6 +8,7 @@ use App\Models\QuotationModel;
use App\Models\InvoiceModel;
use App\Models\InvoiceItemModel;
use App\Models\InvoiceUtrModel;
use App\Models\PartnerAccountHistoryModel;
use CodeIgniter\Database\Exceptions\DataException;
class InvoiceController extends ResourceController
@ -18,6 +19,7 @@ class InvoiceController extends ResourceController
protected $InvoiceModel;
protected $InvoiceItemModel;
protected $InvoiceUtrModel;
protected $PartnerAccountHistoryModel;
protected $db;
public function __construct()
@ -28,6 +30,7 @@ class InvoiceController extends ResourceController
$this->InvoiceModel = new InvoiceModel();
$this->InvoiceItemModel = new InvoiceItemModel();
$this->InvoiceUtrModel = new InvoiceUtrModel();
$this->PartnerAccountHistoryModel = new PartnerAccountHistoryModel();
$this->db = \Config\Database::connect();
}
@ -53,19 +56,37 @@ class InvoiceController extends ResourceController
AND piu.is_active = 1
) AS utr_numbers,
partner_invoice.invoice_amount AS invoiced_amount,
COALESCE((
SELECT SUM(piu.amount)
FROM partner_invoice_utr piu
WHERE piu.invoice_id = partner_invoice.id
AND piu.is_active = 1
), 0.00) AS payout_amount,
(
partner_invoice.invoice_amount - COALESCE((
COALESCE((
SELECT SUM(pah.paid_amount)
FROM partner_account_history pah
WHERE pah.invoice_id = partner_invoice.id
AND pah.is_active = 1
), 0.00)
+
COALESCE((
SELECT SUM(piu.amount)
FROM partner_invoice_utr piu
WHERE piu.invoice_id = partner_invoice.id
AND piu.is_active = 1
), 0.00)
) AS payout_amount,
(
partner_invoice.invoice_amount - (
COALESCE((
SELECT SUM(pah.paid_amount)
FROM partner_account_history pah
WHERE pah.invoice_id = partner_invoice.id
AND pah.is_active = 1
), 0.00)
+
COALESCE((
SELECT SUM(piu.amount)
FROM partner_invoice_utr piu
WHERE piu.invoice_id = partner_invoice.id
AND piu.is_active = 1
), 0.00)
)
) AS balance_amount',
false)
->join('partner_brokers PB', 'PB.id = partner_invoice.broker_id', 'left')
@ -104,6 +125,29 @@ class InvoiceController extends ResourceController
->where('partner_invoice_items.is_active', 1)
->findAll();
$paymentHistory = $this->PartnerAccountHistoryModel
->where('invoice_id', $id)
->where('is_active', 1)
->orderBy('paid_date', 'DESC')
->orderBy('id', 'DESC')
->findAll();
$paidAmount = 0.00;
foreach ($paymentHistory as $payment) {
$paidAmount += (float) ($payment['paid_amount'] ?? 0);
}
$utrPaidAmount = (float) (
$this->InvoiceUtrModel
->selectSum('amount', 'total')
->where('invoice_id', $id)
->where('is_active', 1)
->first()['total'] ?? 0
);
$totalPaidAmount = $paidAmount + $utrPaidAmount;
$invoiceAmount = (float) ($invoice['invoice_amount'] ?? 0);
$balanceAmount = max($invoiceAmount - $totalPaidAmount, 0);
return $this->respond([
@ -111,7 +155,10 @@ class InvoiceController extends ResourceController
'code' => 200,
'data' => [
'invoice' => $invoice,
'items' => $items
'items' => $items,
'payment_history' => $paymentHistory,
'paid_amount' => $totalPaidAmount,
'balance_amount' => $balanceAmount
]
], 200);
@ -415,6 +462,461 @@ class InvoiceController extends ResourceController
}
}
public function addInvoicePayment()
{
$this->db->transBegin();
try {
$input = $this->request->getJSON(true);
$invoiceId = (int)($input['invoice_id'] ?? 0);
$paidAmount = (float)($input['paid_amount'] ?? 0);
$paidDateRaw = $input['paid_date'] ?? null;
if ($invoiceId <= 0 || $paidAmount <= 0 || empty($paidDateRaw)) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'message'=> 'invoice_id, paid_amount and paid_date are required'
], 400);
}
$invoice = $this->InvoiceModel
->where('id', $invoiceId)
->where('is_active', 1)
->first();
if (empty($invoice)) {
return $this->respond([
'status' => 'failed',
'code' => 404,
'message'=> 'Invoice not found'
], 404);
}
$paidDateTs = strtotime($paidDateRaw);
if ($paidDateTs === false) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'message'=> 'Invalid paid_date format'
], 400);
}
$paidDate = date('Y-m-d', $paidDateTs);
$historyPaidAmount = (float) (
$this->PartnerAccountHistoryModel
->selectSum('paid_amount', 'total')
->where('invoice_id', $invoiceId)
->where('is_active', 1)
->first()['total'] ?? 0
);
$utrPaidAmount = (float) (
$this->InvoiceUtrModel
->selectSum('amount', 'total')
->where('invoice_id', $invoiceId)
->where('is_active', 1)
->first()['total'] ?? 0
);
$currentTotalPaid = $historyPaidAmount + $utrPaidAmount;
$invoiceAmount = (float) ($invoice['invoice_amount'] ?? 0);
$remainingBalance = max($invoiceAmount - $currentTotalPaid, 0);
if ($paidAmount > $remainingBalance) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'message'=> 'Paid amount exceeds invoice balance'
], 400);
}
$historyData = [
'invoice_id' => $invoiceId,
'paid_amount' => $paidAmount,
'paid_date' => $paidDate,
'is_active' => 1,
'created_at' => date('Y-m-d H:i:s'),
'created_by' => (int)($input['created_by'] ?? 0),
'updated_at' => date('Y-m-d H:i:s'),
'updated_by' => (int)($input['updated_by'] ?? 0),
];
$historyId = $this->PartnerAccountHistoryModel->insert($historyData);
if ($historyId === false) {
$this->db->transRollback();
return $this->respond([
'status' => 'failed',
'code' => 500,
'message'=> 'Failed to record payment',
'error' => $this->PartnerAccountHistoryModel->errors()
], 500);
}
$latestTotalPaid = $currentTotalPaid + $paidAmount;
$newBalance = max($invoiceAmount - $latestTotalPaid, 0);
$payoutStatus = $newBalance == 0.0 ? 2 : 1;
$this->InvoiceModel->update($invoiceId, [
'payout_status' => $payoutStatus,
'updated_at' => date('Y-m-d H:i:s'),
'updated_by' => (int)($input['updated_by'] ?? 0),
]);
if ($this->db->transStatus() === false) {
$this->db->transRollback();
throw new DataException('Transaction failed');
}
$this->db->transCommit();
return $this->respond([
'status' => 'success',
'code' => 200,
'data' => [
'history_id' => $historyId,
'invoice_id' => $invoiceId,
'paid_amount' => $latestTotalPaid,
'balance_amount' => $newBalance
]
], 200);
} catch (\Exception $e) {
$this->db->transRollback();
return $this->respond([
'status' => 'failed',
'code' => 500,
'message'=> $e->getMessage()
], 500);
}
}
public function bulkUploadCommission()
{
$this->db->transBegin();
try {
$input = $this->request->getJSON(true);
$rows = $input['rows'] ?? [];
$updatedBy = (int)($input['updated_by'] ?? 0);
if (empty($rows) || !is_array($rows)) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'message' => 'rows is required',
], 400);
}
$mismatchRows = [];
$validRows = [];
$skippedRows = 0;
foreach ($rows as $row) {
$policyNo = trim((string)($row['policy_number'] ?? ''));
$invoiceNo = trim((string)($row['invoice_no'] ?? ''));
$commissionAmount = (float)($row['commission_amount'] ?? 0);
$lineNo = (int)($row['line_no'] ?? 0);
if ($policyNo === '' || $invoiceNo === '') {
$skippedRows++;
continue;
}
$record = $this->db->table('partner_invoice_items pii')
->select('
pii.id as item_id,
pii.invoice_id,
pii.policy_id,
pii.policy_no,
pi.invoice_no,
pi.agent_id as invoice_agent_json,
pe.agent_id as policy_agent_id
')
->join('partner_invoice pi', 'pi.id = pii.invoice_id', 'inner')
->join('partner_policy pp', 'pp.id = pii.policy_id', 'left')
->join('partner_enquiry pe', 'pe.id = pp.enquiry_id', 'left')
->where('pii.is_active', 1)
->where('pi.is_active', 1)
->where('pi.invoice_no', $invoiceNo)
->where('pii.policy_no', $policyNo)
->get()
->getRowArray();
if (empty($record)) {
$skippedRows++;
continue;
}
$invoiceAgentIds = $this->extractAgentIdsFromInvoice($record['invoice_agent_json'] ?? '');
$policyAgentId = isset($record['policy_agent_id']) ? (int)$record['policy_agent_id'] : 0;
$targetAgentId = !empty($invoiceAgentIds) ? (int)$invoiceAgentIds[0] : 0;
$isMismatch = $policyAgentId > 0 && !in_array($policyAgentId, $invoiceAgentIds, true);
if ($isMismatch) {
$mismatchRows[] = [
'line_no' => $lineNo,
'item_id' => (int)$record['item_id'],
'invoice_id' => (int)$record['invoice_id'],
'policy_id' => (int)$record['policy_id'],
'policy_number' => $policyNo,
'invoice_no' => $invoiceNo,
'commission_amount' => $commissionAmount,
'policy_agent_id' => $policyAgentId,
'target_agent_id' => $targetAgentId,
];
continue;
}
$validRows[] = [
'item_id' => (int)$record['item_id'],
'invoice_id' => (int)$record['invoice_id'],
'policy_id' => (int)$record['policy_id'],
'commission_amount' => $commissionAmount,
];
}
if (!empty($mismatchRows)) {
$this->ensureBulkUploadStagingTable();
$token = $this->generateProceedToken();
$this->db->table('invoice_bulk_upload_staging')->insert([
'proceed_token' => $token,
'payload_json' => json_encode([
'rows' => $rows,
'updated_by' => $updatedBy,
]),
'mismatch_json' => json_encode($mismatchRows),
'created_at' => date('Y-m-d H:i:s'),
'created_by' => $updatedBy,
'is_active' => 1,
]);
$this->db->transRollback();
return $this->respond([
'status' => 'partner_mismatch',
'code' => 200,
'message' => 'Partner mismatch detected',
'mismatch_count' => count($mismatchRows),
'mismatches' => $mismatchRows,
'proceed_token' => $token,
], 200);
}
$updatedCount = $this->applyBulkCommissionRows($validRows, $updatedBy);
if ($this->db->transStatus() === false) {
$this->db->transRollback();
throw new DataException('Transaction failed');
}
$this->db->transCommit();
return $this->respond([
'status' => 'success',
'code' => 200,
'message' => 'Bulk upload completed',
'updated_count' => $updatedCount,
'skipped_count' => $skippedRows,
], 200);
} catch (\Exception $e) {
$this->db->transRollback();
return $this->respond([
'status' => 'failed',
'code' => 500,
'message' => $e->getMessage(),
], 500);
}
}
public function bulkUploadCommissionProceed()
{
$this->db->transBegin();
try {
$input = $this->request->getJSON(true);
$proceedToken = trim((string)($input['proceed_token'] ?? ''));
$forceReassign = (int)($input['force_reassign_partner'] ?? 0);
$updatedBy = (int)($input['updated_by'] ?? 0);
if ($proceedToken === '' || $forceReassign !== 1) {
return $this->respond([
'status' => 'failed',
'code' => 400,
'message' => 'proceed_token and force_reassign_partner=1 are required',
], 400);
}
$this->ensureBulkUploadStagingTable();
$staging = $this->db->table('invoice_bulk_upload_staging')
->where('proceed_token', $proceedToken)
->where('is_active', 1)
->get()
->getRowArray();
if (empty($staging)) {
return $this->respond([
'status' => 'failed',
'code' => 404,
'message' => 'Invalid or expired proceed token',
], 404);
}
$payload = json_decode($staging['payload_json'] ?? '{}', true);
$rows = $payload['rows'] ?? [];
$mismatchRows = json_decode($staging['mismatch_json'] ?? '[]', true);
foreach ($mismatchRows as $mismatch) {
$policyId = (int)($mismatch['policy_id'] ?? 0);
$targetAgentId = (int)($mismatch['target_agent_id'] ?? 0);
if ($policyId <= 0 || $targetAgentId <= 0) {
continue;
}
$policy = $this->PolicyModel->select('enquiry_id')->where('id', $policyId)->first();
if (!empty($policy['enquiry_id'])) {
$this->EnquiryModel->update((int)$policy['enquiry_id'], [
'agent_id' => $targetAgentId,
'updated_at' => date('Y-m-d H:i:s'),
'updated_by' => $updatedBy,
]);
}
}
$finalRows = [];
foreach ($rows as $row) {
$policyNo = trim((string)($row['policy_number'] ?? ''));
$invoiceNo = trim((string)($row['invoice_no'] ?? ''));
$commissionAmount = (float)($row['commission_amount'] ?? 0);
if ($policyNo === '' || $invoiceNo === '') {
continue;
}
$record = $this->db->table('partner_invoice_items pii')
->select('pii.id as item_id, pii.invoice_id, pii.policy_id')
->join('partner_invoice pi', 'pi.id = pii.invoice_id', 'inner')
->where('pii.is_active', 1)
->where('pi.is_active', 1)
->where('pi.invoice_no', $invoiceNo)
->where('pii.policy_no', $policyNo)
->get()
->getRowArray();
if (empty($record)) {
continue;
}
$finalRows[] = [
'item_id' => (int)$record['item_id'],
'invoice_id' => (int)$record['invoice_id'],
'policy_id' => (int)$record['policy_id'],
'commission_amount' => $commissionAmount,
];
}
$updatedCount = $this->applyBulkCommissionRows($finalRows, $updatedBy);
$this->db->table('invoice_bulk_upload_staging')
->where('id', (int)$staging['id'])
->update([
'is_active' => 0,
'updated_at' => date('Y-m-d H:i:s'),
'updated_by' => $updatedBy,
]);
if ($this->db->transStatus() === false) {
$this->db->transRollback();
throw new DataException('Transaction failed');
}
$this->db->transCommit();
return $this->respond([
'status' => 'success',
'code' => 200,
'message' => 'Bulk upload completed with partner reassignment',
'updated_count' => $updatedCount,
], 200);
} catch (\Exception $e) {
$this->db->transRollback();
return $this->respond([
'status' => 'failed',
'code' => 500,
'message' => $e->getMessage(),
], 500);
}
}
private function applyBulkCommissionRows(array $rows, int $updatedBy): int
{
$updatedCount = 0;
foreach ($rows as $row) {
$itemId = (int)($row['item_id'] ?? 0);
$invoiceId = (int)($row['invoice_id'] ?? 0);
$policyId = (int)($row['policy_id'] ?? 0);
$commissionAmount = (float)($row['commission_amount'] ?? 0);
if ($itemId <= 0) {
continue;
}
$affected = $this->db->table('partner_invoice_items')
->where('id', $itemId)
->where('invoice_id', $invoiceId)
->where('policy_id', $policyId)
->where('is_active', 1)
->update([
'commission_amount' => $commissionAmount,
'updated_at' => date('Y-m-d H:i:s'),
'updated_by' => $updatedBy,
]);
if ($affected) {
$updatedCount++;
}
}
return $updatedCount;
}
private function extractAgentIdsFromInvoice($agentJson): array
{
if (is_array($agentJson)) {
return array_values(array_map('intval', $agentJson));
}
if ($agentJson === null || $agentJson === '') {
return [];
}
$decoded = json_decode((string)$agentJson, true);
if (is_array($decoded)) {
return array_values(array_map('intval', $decoded));
}
if (is_numeric($agentJson)) {
return [(int)$agentJson];
}
return [];
}
private function generateProceedToken(): string
{
return bin2hex(random_bytes(16));
}
private function ensureBulkUploadStagingTable(): void
{
$sql = "CREATE TABLE IF NOT EXISTS invoice_bulk_upload_staging (
id INT AUTO_INCREMENT PRIMARY KEY,
proceed_token VARCHAR(64) NOT NULL UNIQUE,
payload_json LONGTEXT NULL,
mismatch_json LONGTEXT NULL,
is_active TINYINT(1) NOT NULL DEFAULT 1,
created_at DATETIME NULL,
created_by INT NULL,
updated_at DATETIME NULL,
updated_by INT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
$this->db->query($sql);
}
// public function getCommissionRateList()
// {

View File

@ -15,6 +15,7 @@ class AgentIncentiveFileModel extends Model
'agent_id',
'incentive_month',
'incentive_file_name',
'file_type',
'is_active',
'created_by',
'created_on',
@ -34,6 +35,7 @@ class AgentIncentiveFileModel extends Model
protected $validationRules = [
'agent_id' => 'required|integer',
'incentive_month' => 'required|valid_date',
'incentive_file_name'=> 'required|string|max_length[150]'
'incentive_file_name'=> 'required|string|max_length[150]',
'file_type' => 'permit_empty|max_length[50]'
];
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Models;
use CodeIgniter\Model;
class PartnerAccountHistoryModel extends Model
{
protected $table = 'partner_account_history';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $allowedFields = [
'invoice_id',
'paid_amount',
'paid_date',
'is_active',
'created_at',
'created_by',
'updated_at',
'updated_by',
];
protected $useTimestamps = false;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $validationRules = [
'invoice_id' => 'required|integer',
'paid_amount' => 'required|decimal',
'paid_date' => 'required|valid_date',
];
protected $validationMessages = [];
protected $skipValidation = false;
}

View File

@ -0,0 +1,187 @@
<?php
namespace App\Models;
use CodeIgniter\Model;
class PartnerGridDetailsModel extends Model
{
// ─────────────────────────────────────────
// Table Configuration
// ─────────────────────────────────────────
protected $table = 'partner_grid_details';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
// ─────────────────────────────────────────
// Allowed Fields
// ─────────────────────────────────────────
protected $allowedFields = [
'vehicle_type_id',
'insurer_id',
'rto_id',
'segment_id',
'comp',
'tp',
'fuel',
'remarks',
'partner_id',
'partner_comp',
'partner_tp',
'created_by',
'updated_by',
];
// ─────────────────────────────────────────
// Timestamps
// ─────────────────────────────────────────
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
// ─────────────────────────────────────────
// Validation Rules
// ─────────────────────────────────────────
protected $validationRules = [
'vehicle_type_id' => 'permit_empty|max_length[50]',
'insurer_id' => 'required|max_length[50]',
'rto_id' => 'required|max_length[50]',
'segment_id' => 'required|max_length[100]',
'comp' => 'permit_empty|max_length[50]',
'tp' => 'permit_empty|max_length[50]',
'remarks' => 'permit_empty|max_length[255]',
'partner_id' => 'required|max_length[50]',
'fuel' => 'permit_empty|max_length[50]',
'partner_comp' => 'permit_empty|max_length[20]',
'partner_tp' => 'permit_empty|max_length[20]',
'created_by' => 'permit_empty|integer',
'updated_by' => 'permit_empty|integer',
];
protected $validationMessages = [
'insurer_id' => [
'required' => 'Insurer is required.',
'max_length' => 'Insurer name must not exceed 50 characters.',
],
'rto_id' => [
'required' => 'RTO is required.',
'max_length' => 'RTO code must not exceed 50 characters.',
],
'segment_id' => [
'required' => 'Segment is required.',
'max_length' => 'Segment must not exceed 100 characters.',
],
'partner_id' => [
'required' => 'Partner is required.',
'max_length' => 'Partner ID must not exceed 50 characters.',
],
];
protected $skipValidation = false;
// ─────────────────────────────────────────
// Custom Methods
// ─────────────────────────────────────────
/**
* Get all active partner grid records
*/
public function getAllRecords()
{
return $this->orderBy('id', 'ASC')->findAll();
}
/**
* Get records by Insurer ID
*/
public function getByInsurer(string $insurerId)
{
return $this->where('insurer_id', $insurerId)->findAll();
}
/**
* Get records by RTO ID
*/
public function getByRTO(string $rtoId)
{
return $this->where('rto_id', $rtoId)->findAll();
}
/**
* Get records by Segment ID
*/
public function getBySegment(string $segmentId)
{
return $this->where('segment_id', $segmentId)->findAll();
}
/**
* Get records by Partner ID
*/
public function getByPartner(string $partnerId)
{
return $this->where('partner_id', $partnerId)->findAll();
}
/**
* Get records by Insurer and RTO
*/
public function getByInsurerAndRTO(string $insurerId, string $rtoId)
{
return $this->where('insurer_id', $insurerId)
->where('rto_id', $rtoId)
->findAll();
}
/**
* Search with multiple filters
*/
public function search(array $filters = [])
{
$builder = $this->builder();
if (!empty($filters['insurer_id'])) {
$builder->where('insurer_id', $filters['insurer_id']);
}
if (!empty($filters['rto_id'])) {
$builder->where('rto_id', $filters['rto_id']);
}
if (!empty($filters['segment_id'])) {
$builder->where('segment_id', $filters['segment_id']);
}
if (!empty($filters['partner_id'])) {
$builder->where('partner_id', $filters['partner_id']);
}
return $builder->get()->getResultArray();
}
/**
* Insert with created_by
*/
public function insertRecord(array $data, int $userId)
{
$data['created_by'] = $userId;
$data['updated_by'] = $userId;
return $this->insert($data);
}
/**
* Update with updated_by
*/
public function updateRecord(int $id, array $data, int $userId)
{
$data['updated_by'] = $userId;
return $this->update($id, $data);
}
/**
* Delete a record by ID
*/
public function deleteRecord(int $id)
{
return $this->delete($id);
}
}

Binary file not shown.