FEAT_COMMISSION_CALC
This commit is contained in:
parent
5f99271492
commit
fbd532258d
@ -13,6 +13,7 @@ use App\Filters\AuthMVC;
|
||||
use App\Filters\HttpRequestLog;
|
||||
use App\Filters\CloseDbConnection;
|
||||
use App\Filters\AuthClientApi;
|
||||
use App\Filters\CommissionApiFilter;
|
||||
|
||||
use App\Filters\AuthJWT;
|
||||
|
||||
@ -36,7 +37,8 @@ class Filters extends BaseConfig
|
||||
'HttpRequestLog' => HttpRequestLog::class,
|
||||
'authJWT' => AuthJWT::class,
|
||||
'AuthClientApi' => AuthClientApi::class,
|
||||
'CloseDbConnection' => CloseDbConnection::class
|
||||
'CloseDbConnection' => CloseDbConnection::class,
|
||||
'CommissionApiFilter' => CommissionApiFilter::class
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@ -34,6 +34,7 @@ $routes->get("updateRenewalData", "ClientController::updateRenewalData");
|
||||
$routes->get("updateRenewalDataNotExistingClient", "ClientController::updateRenewalDataNotExistingClient");
|
||||
$routes->get("updateRenewalInsurerData", "ClientController::updateRenewalInsurerData");
|
||||
$routes->get("sendMutipleToEmails", "MasterController::sendMutipleToEmails");
|
||||
$routes->post("getCommission", "InsuranceCommissionController::initiateCommissionCalc",['filter' => 'CommissionApiFilter']);
|
||||
// $routes->post("iAgreeForAddOn", "EmployeeRestController::iAgreeForAddOn");
|
||||
// $routes->get("sendCroneRemainderMail", "DashboardController::sendCroneRemainderMail");
|
||||
// $routes->post("employeeUpload", "EmployeeRestController::employeeUpload");
|
||||
|
||||
274
app/Controllers/InsuranceCommissionController.php
Normal file
274
app/Controllers/InsuranceCommissionController.php
Normal file
@ -0,0 +1,274 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use CodeIgniter\Controller;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
|
||||
class InsuranceCommissionController extends AdminController
|
||||
{
|
||||
use ResponseTrait;
|
||||
|
||||
private $rules = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
set_session_context('Client');
|
||||
$this->myLogger = \Config\Services::mylogger();
|
||||
// Load rules file if present in writable config path
|
||||
// $rulesPath = WRITEPATH . 'config/insurance_rules.json';
|
||||
// if (file_exists($rulesPath)) {
|
||||
// $this->loadRulesFromFile($rulesPath);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /insurance/calculate
|
||||
* Accepts JSON body with policy data and returns commission calculation
|
||||
*/
|
||||
public function initiateCommissionCalc()
|
||||
{
|
||||
// Accept POST params (JSON, form-data, x-www-form-urlencoded)
|
||||
$input = $this->request->getPost();
|
||||
|
||||
if (empty($input)) {
|
||||
$json = $this->request->getJSON(true);
|
||||
if ($json) {
|
||||
$input = $json;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($input)) {
|
||||
return $this->failValidationError('No input data received');
|
||||
}
|
||||
// -------- Required Params Check --------
|
||||
if (empty($input['policy_issue_date'])) {
|
||||
return $this->failValidationError('policy_issue_date is required');
|
||||
}
|
||||
|
||||
if (empty($input['department'])) {
|
||||
return $this->failValidationError('department is required');
|
||||
}
|
||||
|
||||
if (empty($input['insurer_id'])) {
|
||||
return $this->failValidationError('insurer_id is required');
|
||||
}
|
||||
|
||||
|
||||
// -------- Build Dynamic Rules Path --------
|
||||
$policyDate = strtotime($input['policy_issue_date']);
|
||||
if (!$policyDate) {
|
||||
return $this->failValidationError('Invalid policy_issue_date');
|
||||
}
|
||||
|
||||
$month = strtoupper(date('M', $policyDate)); // SEP
|
||||
$year = date('Y', $policyDate); // 2025
|
||||
$folderName = $month . $year; // SEP2025
|
||||
|
||||
$insurerId = $input['insurer_id']; // 5
|
||||
$department = ucfirst(strtolower($input['department'])); // Motor, Health, Fire
|
||||
|
||||
// Final Path: WRITEPATH/rules/SEP2025/5_Motor.json
|
||||
$rulesPath = WRITEPATH . "uploads/commission/rules/{$folderName}/{$insurerId}_{$department}.json";
|
||||
// echo $rulesPath;die();
|
||||
|
||||
if (!file_exists($rulesPath)) {
|
||||
return $this->fail("Rules file not found at: {$rulesPath}");
|
||||
}
|
||||
|
||||
// Load the dynamic rule set
|
||||
$this->loadRulesFromFile($rulesPath);
|
||||
|
||||
// -------- Execute Rule Matching & Commission Calculation --------
|
||||
try {
|
||||
$result = $this->calculateCommission($input);
|
||||
|
||||
$comment = isset($result['rule']['name'])
|
||||
? "Matched rule: " . $result['rule']['name']
|
||||
: "Matched rule: (unnamed rule)";
|
||||
|
||||
return $this->respond([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'payout' => $result['payout'],
|
||||
'rule' => $result['rule'],
|
||||
'comment' => $comment,
|
||||
// 'rules_path_used' => $rulesPath
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load rules JSON and normalise department keys to lowercase for lookups
|
||||
*/
|
||||
private function loadRulesFromFile(string $filePath)
|
||||
{
|
||||
if (!file_exists($filePath)) {
|
||||
throw new \Exception("Rules file not found: {$filePath}");
|
||||
}
|
||||
|
||||
$json = file_get_contents($filePath);
|
||||
$parsed = json_decode($json, true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
throw new \Exception('Invalid JSON in rules file: ' . json_last_error_msg());
|
||||
}
|
||||
|
||||
// Normalise department keys to lowercase for consistent lookups
|
||||
$this->rules = [];
|
||||
foreach ($parsed as $dept => $rules) {
|
||||
$this->rules[strtolower($dept)] = $rules;
|
||||
}
|
||||
}
|
||||
|
||||
public function calculateCommission(array $policyData)
|
||||
{
|
||||
$department = $policyData['department'] ?? '';
|
||||
$deptKey = strtolower($department);
|
||||
|
||||
if (!isset($this->rules[$deptKey])) {
|
||||
throw new \Exception("No rules found for department: {$department}");
|
||||
}
|
||||
|
||||
$matchingRules = [];
|
||||
|
||||
foreach ($this->rules[$deptKey] as $rule) {
|
||||
if ($this->evaluateConditions($rule['conditions'] ?? [], $policyData)) {
|
||||
$matchingRules[] = $rule;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($matchingRules)) {
|
||||
throw new \Exception('No matching rules found for the policy data');
|
||||
}
|
||||
|
||||
// Use the first matching rule. In future you can implement priority/weighting
|
||||
$applicableRule = $matchingRules[0];
|
||||
|
||||
$payout = $this->applyCalculation($applicableRule['calculation'], $policyData);
|
||||
|
||||
return ['rule' => $applicableRule, 'payout' => $payout];
|
||||
}
|
||||
|
||||
private function evaluateConditions(array $conditions, array $data): bool
|
||||
{
|
||||
foreach ($conditions as $condition) {
|
||||
$field = $condition['field'];
|
||||
$operator = $condition['operator'];
|
||||
$expectedValue = $condition['value'];
|
||||
|
||||
if (!array_key_exists($field, $data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$actualValue = $data[$field];
|
||||
|
||||
if (!$this->compareValues($actualValue, $operator, $expectedValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function compareValues($actual, string $operator, $expected): bool
|
||||
{
|
||||
switch ($operator) {
|
||||
case '==':
|
||||
return $actual == $expected;
|
||||
case '!=':
|
||||
return $actual != $expected;
|
||||
case '>':
|
||||
return $actual > $expected;
|
||||
case '>=':
|
||||
return $actual >= $expected;
|
||||
case '<':
|
||||
return $actual < $expected;
|
||||
case '<=':
|
||||
return $actual <= $expected;
|
||||
case 'between':
|
||||
return is_array($expected) && $actual >= $expected[0] && $actual <= $expected[1];
|
||||
case 'in':
|
||||
return is_array($expected) && in_array($actual, $expected);
|
||||
default:
|
||||
throw new \Exception("Unsupported operator: {$operator}");
|
||||
}
|
||||
}
|
||||
|
||||
private function applyCalculation(array $calculation, array $policyData)
|
||||
{
|
||||
$type = $calculation['type'] ?? null;
|
||||
|
||||
switch ($type) {
|
||||
case 'percentage':
|
||||
$percentage = $calculation['value'] ?? 0;
|
||||
$base = $calculation['on'] ?? null;
|
||||
|
||||
if ($base === null || !isset($policyData[$base])) {
|
||||
throw new \Exception("Base value for calculation not found: {$base}");
|
||||
}
|
||||
|
||||
return ($percentage / 100) * $policyData[$base];
|
||||
|
||||
case 'composite':
|
||||
$total = 0;
|
||||
|
||||
foreach ($calculation['components'] as $component) {
|
||||
$percentage = $component['percentage'] ?? 0;
|
||||
$base = $component['on'] ?? null;
|
||||
|
||||
if ($base === null || !isset($policyData[$base])) {
|
||||
throw new \Exception("Base value for calculation not found: {$base}");
|
||||
}
|
||||
|
||||
if (!empty($component['only_first_year'])) {
|
||||
if (!empty($policyData['is_renewal'])) {
|
||||
continue; // Skip this component for renewals
|
||||
}
|
||||
}
|
||||
|
||||
$total += ($percentage / 100) * $policyData[$base];
|
||||
}
|
||||
|
||||
return $total;
|
||||
|
||||
case 'fixed':
|
||||
$fixedAmount = $calculation['value'] ?? 0;
|
||||
|
||||
// If 'on' specified but not needed, return fixed amount as-is
|
||||
return $fixedAmount;
|
||||
|
||||
default:
|
||||
throw new \Exception('Unsupported calculation type: ' . $type);
|
||||
}
|
||||
}
|
||||
|
||||
public function getVolumeReward(array $premiumData)
|
||||
{
|
||||
$annualPremium = $premiumData['annual_premium'] ?? 0;
|
||||
$department = $premiumData['department'] ?? '';
|
||||
|
||||
if ($department === 'Fire' || $department === 'Marine' || $department === 'Engineering') {
|
||||
if ($annualPremium > 20000000) {
|
||||
return 0.01 * $annualPremium;
|
||||
} elseif ($annualPremium > 10000000) {
|
||||
return 0.005 * $annualPremium;
|
||||
} elseif ($annualPremium > 5000000) {
|
||||
return 0.0025 * $annualPremium;
|
||||
}
|
||||
} elseif ($department === 'Motor') {
|
||||
if ($annualPremium > 15000000) {
|
||||
return 0.02 * $annualPremium;
|
||||
} elseif ($annualPremium > 7500000) {
|
||||
return 0.01 * $annualPremium;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1976,6 +1976,9 @@ class MasterController extends AdminController
|
||||
'lead_files' => WRITEPATH . 'uploads/lead_files/',
|
||||
'claim_files' => WRITEPATH . 'uploads/claim_files/',
|
||||
'claim_dump_excel' => WRITEPATH . 'uploads/claim_dump_excel/',
|
||||
'commission' => WRITEPATH . 'uploads/commission/',
|
||||
'files' => WRITEPATH . 'uploads/commission/files',
|
||||
'rules' => WRITEPATH . 'uploads/commission/rules',
|
||||
'claim_sample_forms' => ROOTPATH . 'public/claim_sample_forms/',
|
||||
];
|
||||
|
||||
|
||||
55
app/Filters/CommissionApiFilter.php
Normal file
55
app/Filters/CommissionApiFilter.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use CodeIgniter\Filters\FilterInterface;
|
||||
|
||||
class CommissionApiFilter implements FilterInterface
|
||||
{
|
||||
public function before(RequestInterface $request, $arguments = null)
|
||||
{
|
||||
|
||||
// Read API key from header
|
||||
// $authHeader = $request->getHeaderLine('X');
|
||||
$authHeader = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
|
||||
// echo $authHeader;die();
|
||||
if (empty($authHeader)) {
|
||||
return service('response')->setJSON([
|
||||
'success' => false,
|
||||
'error' => 'Authorization header missing'
|
||||
])->setStatusCode(403);
|
||||
}
|
||||
|
||||
// Expected format: Bearer YOUR_API_KEY
|
||||
if (stripos($authHeader, 'Bearer ') !== 0) {
|
||||
return service('response')->setJSON([
|
||||
'success' => false,
|
||||
'error' => 'Invalid Authorization format. Expected: Bearer <token>'
|
||||
])->setStatusCode(403);
|
||||
}
|
||||
|
||||
$apiKey = trim(substr($authHeader, 7)); // extract token after 'Bearer '
|
||||
|
||||
$envKeys = getenv('ALLOWED_COMMISSION_API_KEYS');
|
||||
// Convert CSV -> Array
|
||||
$allowedKeys = array_map('trim', explode(',', $envKeys));
|
||||
// print_r($allowedKeys);die();
|
||||
// Validate
|
||||
if (!in_array($apiKey, $allowedKeys, true)) {
|
||||
return service('response')->setJSON([
|
||||
'success' => false,
|
||||
'error' => 'Invalid API Key'
|
||||
])->setStatusCode(403);
|
||||
}
|
||||
|
||||
// Allow request to proceed
|
||||
return null;
|
||||
}
|
||||
|
||||
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
|
||||
{
|
||||
// Not needed
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user