427 lines
16 KiB
PHP
427 lines
16 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers;
|
|
|
|
use App\Libraries\DigitMotor\DigitApiException;
|
|
use App\Libraries\DigitMotor\DigitExecutorService;
|
|
use App\Models\MotorMasterNcbModel;
|
|
use App\Models\MotorMasterPincodeModel;
|
|
use App\Models\MotorMasterPreviousInsurerModel;
|
|
use App\Models\MotorMasterPreviousPolicyTypeModel;
|
|
use App\Models\MotorMasterProductModel;
|
|
use App\Models\MotorMasterVehicleModel;
|
|
use App\Models\MotorMasterVoluntaryDeductibleModel;
|
|
use App\Models\MotorQuoteModel;
|
|
use CodeIgniter\API\ResponseTrait;
|
|
|
|
class DigitMotorController extends BaseController
|
|
{
|
|
use ResponseTrait;
|
|
|
|
protected MotorQuoteModel $quoteModel;
|
|
protected DigitExecutorService $executor;
|
|
protected MotorMasterVehicleModel $vehicleMaster;
|
|
protected MotorMasterProductModel $productMaster;
|
|
protected MotorMasterPreviousInsurerModel $insurerMaster;
|
|
protected MotorMasterNcbModel $ncbMaster;
|
|
protected MotorMasterPreviousPolicyTypeModel $prevPolicyTypeMaster;
|
|
protected MotorMasterVoluntaryDeductibleModel $deductibleMaster;
|
|
protected MotorMasterPincodeModel $pincodeMaster;
|
|
|
|
public function __construct()
|
|
{
|
|
set_session_context('Digit Motor Controller');
|
|
$this->quoteModel = new MotorQuoteModel();
|
|
$this->executor = new DigitExecutorService();
|
|
$this->vehicleMaster = new MotorMasterVehicleModel();
|
|
$this->productMaster = new MotorMasterProductModel();
|
|
$this->insurerMaster = new MotorMasterPreviousInsurerModel();
|
|
$this->ncbMaster = new MotorMasterNcbModel();
|
|
$this->prevPolicyTypeMaster = new MotorMasterPreviousPolicyTypeModel();
|
|
$this->deductibleMaster = new MotorMasterVoluntaryDeductibleModel();
|
|
$this->pincodeMaster = new MotorMasterPincodeModel();
|
|
}
|
|
|
|
// ===================== LIST =====================
|
|
|
|
public function list()
|
|
{
|
|
if (strtolower($this->request->getMethod()) === 'post') {
|
|
$filters = [
|
|
'status' => $this->request->getPost('status'),
|
|
'enquiry_id' => $this->request->getPost('enquiry_id'),
|
|
'quote_number' => $this->request->getPost('quote_number'),
|
|
'license_plate' => $this->request->getPost('license_plate'),
|
|
];
|
|
$data['quotes'] = $this->quoteModel->getListWithVehicle($filters);
|
|
return view('digit_motor/list_table', $data);
|
|
}
|
|
|
|
$data['quotes'] = $this->quoteModel->getListWithVehicle();
|
|
$data['statuses'] = [
|
|
'DRAFT' => 'Draft',
|
|
'QUOTED' => 'Quoted',
|
|
'CREATED' => 'Created',
|
|
'KYC_DONE' => 'KYC Done',
|
|
'PAID' => 'Paid',
|
|
'EFFECTIVE' => 'Effective',
|
|
'FAILED' => 'Failed',
|
|
];
|
|
$data['title'] = 'Digit Motor Quotes';
|
|
return $this->loadLayout('digit_motor/list', $data);
|
|
}
|
|
|
|
// ===================== JOURNEY UI =====================
|
|
|
|
public function journey($quoteId = null)
|
|
{
|
|
$data['title'] = 'Digit Motor Journey';
|
|
$data['quote'] = null;
|
|
$data['quote_id'] = null;
|
|
$data['vehicle_master'] = null;
|
|
$data['policyholder'] = [];
|
|
|
|
if ($quoteId) {
|
|
$data['quote'] = $this->quoteModel->getDetail((int) $quoteId);
|
|
$data['quote_id'] = (int) $quoteId;
|
|
if (!$data['quote']) {
|
|
return redirect()->to(base_url('digit-motor/list'))->with('error', 'Quote not found.');
|
|
}
|
|
$data['policyholder'] = is_array($data['quote']['policyholder_details'] ?? null)
|
|
? $data['quote']['policyholder_details']
|
|
: [];
|
|
$vcode = $data['quote']['vehicle']['vehicle_maincode'] ?? '';
|
|
if ($vcode !== '') {
|
|
try {
|
|
$data['vehicle_master'] = $this->vehicleMaster->findActive((string) $vcode);
|
|
} catch (\Throwable $e) {
|
|
$data['vehicle_master'] = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $this->loadLayout('digit_motor/journey', $data);
|
|
}
|
|
|
|
public function detail($quoteId)
|
|
{
|
|
$detail = $this->quoteModel->getDetail((int) $quoteId);
|
|
if (!$detail) {
|
|
return $this->respond(['status' => false, 'message' => 'Quote not found.'], 404);
|
|
}
|
|
return $this->respond(['status' => true, 'data' => $detail]);
|
|
}
|
|
|
|
// ===================== MASTER LOOKUPS =====================
|
|
|
|
public function mastersBootstrap()
|
|
{
|
|
try {
|
|
return $this->respond([
|
|
'status' => true,
|
|
'data' => [
|
|
'products' => $this->productMaster->activeList(),
|
|
'previous_insurers' => $this->insurerMaster->activeList(),
|
|
'ncb' => $this->ncbMaster->activeList(),
|
|
'previous_policy_types' => $this->prevPolicyTypeMaster->activeList(),
|
|
'voluntary_deductibles' => $this->deductibleMaster->activeList(),
|
|
],
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
log_message('error', 'DigitMotor mastersBootstrap: ' . $e->getMessage());
|
|
return $this->respond([
|
|
'status' => false,
|
|
'message' => 'Master tables not ready. Run: php spark digit-motor:import-masters --create-tables',
|
|
], 503);
|
|
}
|
|
}
|
|
|
|
public function masterVehicleSearch()
|
|
{
|
|
$q = trim((string) ($this->request->getGet('q') ?? ''));
|
|
if (strlen($q) < 2) {
|
|
return $this->respond(['status' => true, 'data' => []]);
|
|
}
|
|
|
|
return $this->respond([
|
|
'status' => true,
|
|
'data' => $this->vehicleMaster->search($q, 40),
|
|
]);
|
|
}
|
|
|
|
public function masterVehicleMakes()
|
|
{
|
|
$q = trim((string) ($this->request->getGet('q') ?? ''));
|
|
return $this->respond([
|
|
'status' => true,
|
|
'data' => $this->vehicleMaster->distinctMakes($q ?: null, 120),
|
|
]);
|
|
}
|
|
|
|
public function masterVehicleModels()
|
|
{
|
|
$make = trim((string) ($this->request->getGet('make') ?? ''));
|
|
if ($make === '') {
|
|
return $this->respond(['status' => false, 'message' => 'make is required.'], 422);
|
|
}
|
|
$q = trim((string) ($this->request->getGet('q') ?? ''));
|
|
return $this->respond([
|
|
'status' => true,
|
|
'data' => $this->vehicleMaster->distinctModels($make, $q ?: null),
|
|
]);
|
|
}
|
|
|
|
public function masterVehicleVariants()
|
|
{
|
|
$make = trim((string) ($this->request->getGet('make') ?? ''));
|
|
$model = trim((string) ($this->request->getGet('model') ?? ''));
|
|
if ($make === '' || $model === '') {
|
|
return $this->respond(['status' => false, 'message' => 'make and model are required.'], 422);
|
|
}
|
|
return $this->respond([
|
|
'status' => true,
|
|
'data' => $this->vehicleMaster->variants($make, $model),
|
|
]);
|
|
}
|
|
|
|
public function masterPincode($pincode = null)
|
|
{
|
|
$pincode = preg_replace('/\D+/', '', (string) ($pincode ?: $this->request->getGet('pincode')));
|
|
if (strlen($pincode) !== 6) {
|
|
return $this->respond(['status' => false, 'message' => 'Valid 6-digit pincode required.'], 422);
|
|
}
|
|
$row = $this->pincodeMaster->findActive($pincode);
|
|
return $this->respond([
|
|
'status' => (bool) $row,
|
|
'data' => $row,
|
|
'message'=> $row ? null : 'Pincode not found in Digit master.',
|
|
], $row ? 200 : 404);
|
|
}
|
|
|
|
// ===================== API ACTIONS =====================
|
|
|
|
public function quickQuote()
|
|
{
|
|
return $this->runAction(function () {
|
|
$input = $this->request->getJSON(true) ?: $this->request->getPost();
|
|
$rules = [
|
|
'license_plate_number' => 'required|min_length[4]',
|
|
'vehicle_maincode' => 'required',
|
|
'registration_date' => 'required',
|
|
'manufacture_date' => 'required',
|
|
'vehicle_identification_number' => 'required|min_length[5]',
|
|
'engine_number' => 'required|min_length[5]',
|
|
'pincode' => 'required|exact_length[6]',
|
|
'insurance_product_code' => 'required',
|
|
];
|
|
if (!$this->validateDigitInput($input, $rules)) {
|
|
$errors = $this->validator->getErrors();
|
|
$first = reset($errors);
|
|
return $this->respond([
|
|
'status' => false,
|
|
'message' => is_string($first) ? $first : 'Validation failed.',
|
|
'errors' => $errors,
|
|
], 422);
|
|
}
|
|
|
|
$masterError = $this->validateQuickQuoteMasters($input);
|
|
if ($masterError !== null) {
|
|
return $this->respond([
|
|
'status' => false,
|
|
'message' => $masterError,
|
|
], 422);
|
|
}
|
|
|
|
$result = $this->executor->quickQuote($input);
|
|
return $this->respond([
|
|
'status' => true,
|
|
'message' => 'Quick quote generated.',
|
|
'data' => $result,
|
|
]);
|
|
});
|
|
}
|
|
|
|
public function createQuote($quoteId = null)
|
|
{
|
|
return $this->runAction(function () use ($quoteId) {
|
|
$input = $this->request->getJSON(true) ?: $this->request->getPost();
|
|
$quoteId = (int) ($quoteId ?: ($input['quote_id'] ?? 0));
|
|
if ($quoteId <= 0) {
|
|
return $this->respond(['status' => false, 'message' => 'quote_id is required.'], 422);
|
|
}
|
|
|
|
$rules = [
|
|
'first_name' => 'required',
|
|
'mobile' => 'required|exact_length[10]',
|
|
'email' => 'required|valid_email',
|
|
'pan' => 'required',
|
|
'dob' => 'required',
|
|
'address' => 'required',
|
|
'vehicle_identification_number' => 'required|min_length[5]',
|
|
'engine_number' => 'required|min_length[5]',
|
|
];
|
|
if (!$this->validateDigitInput($input, $rules)) {
|
|
$errors = $this->validator->getErrors();
|
|
$first = reset($errors);
|
|
return $this->respond([
|
|
'status' => false,
|
|
'message' => is_string($first) ? $first : 'Validation failed.',
|
|
'errors' => $errors,
|
|
], 422);
|
|
}
|
|
|
|
$result = $this->executor->createQuote($quoteId, $input);
|
|
return $this->respond([
|
|
'status' => true,
|
|
'message' => 'Quote created.',
|
|
'data' => $result,
|
|
]);
|
|
});
|
|
}
|
|
|
|
public function kycStatus($quoteId)
|
|
{
|
|
return $this->runAction(function () use ($quoteId) {
|
|
$result = $this->executor->kycStatus((int) $quoteId);
|
|
return $this->respond([
|
|
'status' => true,
|
|
'message' => 'KYC status fetched.',
|
|
'data' => $result,
|
|
]);
|
|
});
|
|
}
|
|
|
|
public function paymentLink($quoteId)
|
|
{
|
|
return $this->runAction(function () use ($quoteId) {
|
|
$input = $this->request->getJSON(true) ?: $this->request->getPost();
|
|
$result = $this->executor->paymentLink((int) $quoteId, $input ?: []);
|
|
return $this->respond([
|
|
'status' => true,
|
|
'message' => 'Payment link generated.',
|
|
'data' => $result,
|
|
]);
|
|
});
|
|
}
|
|
|
|
public function policyStatus($quoteId)
|
|
{
|
|
return $this->runAction(function () use ($quoteId) {
|
|
$result = $this->executor->policyStatus((int) $quoteId);
|
|
return $this->respond([
|
|
'status' => true,
|
|
'message' => 'Policy status fetched.',
|
|
'data' => $result,
|
|
]);
|
|
});
|
|
}
|
|
|
|
public function policyPdf($quoteId)
|
|
{
|
|
return $this->runAction(function () use ($quoteId) {
|
|
$result = $this->executor->policyPdf((int) $quoteId);
|
|
return $this->respond([
|
|
'status' => true,
|
|
'message' => 'Policy PDF fetched.',
|
|
'data' => $result,
|
|
]);
|
|
});
|
|
}
|
|
|
|
public function paymentCallback($outcome, $quoteId)
|
|
{
|
|
$quoteId = (int) $quoteId;
|
|
if ($outcome === 'success' && $quoteId > 0) {
|
|
try {
|
|
$this->executor->policyStatus($quoteId);
|
|
} catch (\Throwable $e) {
|
|
log_message('error', 'DigitMotor payment callback policyStatus: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
return redirect()->to(base_url('digit-motor/journey/' . $quoteId))
|
|
->with($outcome === 'success' ? 'success' : 'error', $outcome === 'success'
|
|
? 'Payment return received. Refreshing policy status.'
|
|
: 'Payment was cancelled or failed.');
|
|
}
|
|
|
|
protected function runAction(callable $fn)
|
|
{
|
|
try {
|
|
return $fn();
|
|
} catch (DigitApiException $e) {
|
|
log_message('error', $e->toLogMessage());
|
|
$http = $e->getHttpStatus() >= 400 ? $e->getHttpStatus() : 502;
|
|
if ($e->isInfraError()) {
|
|
$http = 502;
|
|
}
|
|
return $this->respond([
|
|
'status' => false,
|
|
'message' => $e->getMessage(),
|
|
'digit_code' => $e->getDigitCode(),
|
|
'infra' => $e->isInfraError(),
|
|
'data' => is_array($e->getResponseBody())
|
|
? ($e->getResponseBody()['_nhance_shell'] ?? null)
|
|
: null,
|
|
], min($http, 599));
|
|
} catch (\Throwable $e) {
|
|
log_message('error', 'DigitMotor unexpected error: ' . $e->getMessage());
|
|
return $this->respond([
|
|
'status' => false,
|
|
'message' => 'Something went wrong. Please try again.',
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
protected function validateDigitInput(array $data, array $rules): bool
|
|
{
|
|
$this->validator = \Config\Services::validation();
|
|
$this->validator->setRules($rules);
|
|
return $this->validator->run($data);
|
|
}
|
|
|
|
/**
|
|
* Ensure vehicleMaincode / pincode / product exist in Digit masters before API call.
|
|
*/
|
|
protected function validateQuickQuoteMasters(array $input): ?string
|
|
{
|
|
try {
|
|
$code = trim((string) ($input['vehicle_maincode'] ?? ''));
|
|
if ($code === '' || !$this->vehicleMaster->findActive($code)) {
|
|
return 'Vehicle code "' . $code . '" not found in Digit vehicle master. Select Make → Model → Variant.';
|
|
}
|
|
|
|
$pin = preg_replace('/\D+/', '', (string) ($input['pincode'] ?? ''));
|
|
if (strlen($pin) !== 6 || !$this->pincodeMaster->findActive($pin)) {
|
|
return 'Pincode "' . ($input['pincode'] ?? '') . '" not found in Digit pin master (may not be serviceable).';
|
|
}
|
|
|
|
$product = trim((string) ($input['insurance_product_code'] ?? ''));
|
|
if ($product !== '') {
|
|
$prod = $this->productMaster->where('product_code', $product)->where('is_active', 1)->first();
|
|
if (!$prod) {
|
|
return 'Product code "' . $product . '" is not a valid Digit motor product.';
|
|
}
|
|
}
|
|
|
|
$insurer = trim((string) ($input['previous_insurer_code'] ?? ''));
|
|
if ($insurer !== '') {
|
|
$row = $this->insurerMaster->where('insurer_code', str_pad($insurer, 3, '0', STR_PAD_LEFT))
|
|
->where('is_active', 1)->first();
|
|
if (!$row) {
|
|
// also try raw code
|
|
$row = $this->insurerMaster->where('insurer_code', $insurer)->where('is_active', 1)->first();
|
|
}
|
|
if (!$row) {
|
|
return 'Previous insurer code "' . $insurer . '" not found in Digit previous-insurer master.';
|
|
}
|
|
}
|
|
} catch (\Throwable $e) {
|
|
log_message('error', 'DigitMotor master validation skipped: ' . $e->getMessage());
|
|
// If masters table missing, don't block — Digit will still validate
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|