nhance/app/Controllers/DigitMotorController.php
2026-07-30 14:03:40 +05:30

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;
}
}