280 lines
9.1 KiB
PHP
280 lines
9.1 KiB
PHP
<?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('InsuranceCommissionController');
|
|
$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());
|
|
}
|
|
// print_r($parsed);die();
|
|
// Normalise department keys to lowercase for consistent lookups
|
|
$this->rules = [];
|
|
foreach ($parsed as $dept => $rules) {
|
|
if($rules['is_deleted'] === false)
|
|
{
|
|
$this->rules[strtolower($dept)] = $rules;
|
|
}
|
|
}
|
|
|
|
// print_r($this->rules);die();
|
|
}
|
|
|
|
public function calculateCommission(array $policyData)
|
|
{
|
|
$department = $policyData['department'] ?? '';
|
|
$deptKey = strtolower($department);
|
|
// print_r($this->rules);die();
|
|
// if (!isset($this->rules[$deptKey])) {
|
|
// throw new \Exception("No rules found for department: {$department}");
|
|
// }
|
|
|
|
$matchingRules = [];
|
|
|
|
foreach ($this->rules as $rule) {
|
|
if ($this->evaluateConditions($rule['conditions'] ?? [], $policyData)) {
|
|
$matchingRules[] = $rule;
|
|
}
|
|
}
|
|
|
|
if (empty($matchingRules)) {
|
|
throw new \Exception('No matching rules found for the policy data');
|
|
}
|
|
// print_r($matchingRules);die;
|
|
// 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;
|
|
}
|
|
}
|