MERGE_UAT_BUG_FIXES
This commit is contained in:
commit
fc17b216f0
@ -194,7 +194,7 @@ ICICI_PRIMARY_KEY_CONSTANT =
|
|||||||
# Digit Motor (OneAPI)
|
# Digit Motor (OneAPI)
|
||||||
#--------------------------------------------------------------------
|
#--------------------------------------------------------------------
|
||||||
DIGIT_MOTOR_BASE_URL = https://preprod-oneapi.godigit.com
|
DIGIT_MOTOR_BASE_URL = https://preprod-oneapi.godigit.com
|
||||||
DIGIT_MOTOR_AUTH_PATH = /OneAPI/digit/generateAuthKey
|
DIGIT_MOTOR_AUTH_PATH = /OneAPI/v1/auth
|
||||||
DIGIT_MOTOR_EXECUTOR_PATH = /OneAPI/v1/executor
|
DIGIT_MOTOR_EXECUTOR_PATH = /OneAPI/v1/executor
|
||||||
DIGIT_MOTOR_USERNAME =
|
DIGIT_MOTOR_USERNAME =
|
||||||
DIGIT_MOTOR_PASSWORD =
|
DIGIT_MOTOR_PASSWORD =
|
||||||
@ -202,6 +202,7 @@ DIGIT_MOTOR_ENVIRONMENT = staging
|
|||||||
DIGIT_MOTOR_TIMEOUT = 30
|
DIGIT_MOTOR_TIMEOUT = 30
|
||||||
DIGIT_MOTOR_TOKEN_LEEWAY_SEC = 60
|
DIGIT_MOTOR_TOKEN_LEEWAY_SEC = 60
|
||||||
DIGIT_MOTOR_PDF_AUTH_KEY =
|
DIGIT_MOTOR_PDF_AUTH_KEY =
|
||||||
|
DIGIT_MOTOR_MASTERS_PATH =
|
||||||
DIGIT_MOTOR_IID_QUICK_QUOTE = 29266-0100
|
DIGIT_MOTOR_IID_QUICK_QUOTE = 29266-0100
|
||||||
DIGIT_MOTOR_IID_CREATE_QUOTE = 29268-0100
|
DIGIT_MOTOR_IID_CREATE_QUOTE = 29268-0100
|
||||||
DIGIT_MOTOR_IID_KYC = 29269-0100
|
DIGIT_MOTOR_IID_KYC = 29269-0100
|
||||||
|
|||||||
103
app/Commands/DigitMotorImportMasters.php
Normal file
103
app/Commands/DigitMotorImportMasters.php
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Commands;
|
||||||
|
|
||||||
|
use App\Libraries\DigitMotor\DigitMasterImportService;
|
||||||
|
use CodeIgniter\CLI\BaseCommand;
|
||||||
|
use CodeIgniter\CLI\CLI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Digit motor master tables and import kit Excel files.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* php spark digit-motor:import-masters --create-tables
|
||||||
|
* php spark digit-motor:import-masters --path="/path/to/Masters"
|
||||||
|
* php spark digit-motor:import-masters --only=products,previous_insurers,ncb
|
||||||
|
* php spark digit-motor:import-masters --skip-vehicles
|
||||||
|
*/
|
||||||
|
class DigitMotorImportMasters extends BaseCommand
|
||||||
|
{
|
||||||
|
protected $group = 'DigitMotor';
|
||||||
|
protected $name = 'digit-motor:import-masters';
|
||||||
|
protected $description = 'Create motor_master_* tables and import Digit Masters Excel files.';
|
||||||
|
protected $usage = 'digit-motor:import-masters [--path] [--create-tables] [--only] [--skip-vehicles]';
|
||||||
|
protected $options = [
|
||||||
|
'--path' => 'Path to Digit Masters folder (xlsx files).',
|
||||||
|
'--create-tables' => 'Run digit_motor_master_tables.sql first.',
|
||||||
|
'--only' => 'Comma-separated master keys to import.',
|
||||||
|
'--skip-vehicles' => 'Skip large vehicle master import.',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function run(array $params)
|
||||||
|
{
|
||||||
|
$defaultPath = '/home/smart/Downloads/Digit API Integration_API kit (1)/Masters';
|
||||||
|
$path = CLI::getOption('path') ?: env('DIGIT_MOTOR_MASTERS_PATH', $defaultPath);
|
||||||
|
$path = rtrim((string) $path, '/');
|
||||||
|
|
||||||
|
if (!is_dir($path)) {
|
||||||
|
CLI::error('Masters path not found: ' . $path);
|
||||||
|
return EXIT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
$service = new DigitMasterImportService($path);
|
||||||
|
$service->setProgressCallback(static function (string $msg) {
|
||||||
|
CLI::write($msg, 'yellow');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (CLI::getOption('create-tables') !== null) {
|
||||||
|
$sql = ROOTPATH . 'app/Database/digit_motor_master_tables.sql';
|
||||||
|
CLI::write('Creating tables from ' . $sql, 'green');
|
||||||
|
try {
|
||||||
|
$service->createTables($sql);
|
||||||
|
CLI::write('Tables created / verified.', 'green');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
CLI::error('Create tables failed: ' . $e->getMessage());
|
||||||
|
return EXIT_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$onlyOpt = CLI::getOption('only');
|
||||||
|
// CI may return true for `--only=vehicles` when value parsing fails — also check argv
|
||||||
|
if ($onlyOpt === true || $onlyOpt === null || $onlyOpt === '') {
|
||||||
|
foreach ($_SERVER['argv'] ?? [] as $arg) {
|
||||||
|
if (str_starts_with((string) $arg, '--only=')) {
|
||||||
|
$onlyOpt = substr((string) $arg, 7);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$only = [];
|
||||||
|
if (is_string($onlyOpt) && $onlyOpt !== '' && $onlyOpt !== '1') {
|
||||||
|
$only = array_filter(array_map('trim', explode(',', $onlyOpt)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CLI::getOption('skip-vehicles') !== null) {
|
||||||
|
if (!$only) {
|
||||||
|
$only = [
|
||||||
|
'products', 'previous_insurers', 'ncb', 'voluntary_deductible',
|
||||||
|
'previous_policy_type', 'doc_types', 'nominee_relations', 'states',
|
||||||
|
'sub_products', 'addon_age_limits', 'pincodes', 'rtos',
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$only = array_values(array_diff($only, ['vehicles']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CLI::write('Importing from: ' . $path, 'green');
|
||||||
|
$results = $service->importAll($only);
|
||||||
|
|
||||||
|
CLI::newLine();
|
||||||
|
CLI::write(str_pad('Master', 28) . str_pad('Rows', 10) . 'Status', 'white');
|
||||||
|
CLI::write(str_repeat('-', 55));
|
||||||
|
foreach ($results as $key => $res) {
|
||||||
|
$line = str_pad($key, 28) . str_pad((string) $res['rows'], 10) . $res['status'];
|
||||||
|
if (!empty($res['message'])) {
|
||||||
|
$line .= ' — ' . $res['message'];
|
||||||
|
}
|
||||||
|
CLI::write($line, $res['status'] === 'OK' ? 'green' : 'red');
|
||||||
|
}
|
||||||
|
|
||||||
|
$failed = array_filter($results, static fn ($r) => $r['status'] !== 'OK');
|
||||||
|
return $failed ? EXIT_ERROR : EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -23,7 +23,7 @@ class DigitMotor extends BaseConfig
|
|||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
$this->baseUrl = rtrim((string) env('DIGIT_MOTOR_BASE_URL', 'https://preprod-oneapi.godigit.com'), '/');
|
$this->baseUrl = rtrim((string) env('DIGIT_MOTOR_BASE_URL', 'https://preprod-oneapi.godigit.com'), '/');
|
||||||
$this->authPath = (string) env('DIGIT_MOTOR_AUTH_PATH', '/OneAPI/digit/generateAuthKey');
|
$this->authPath = (string) env('DIGIT_MOTOR_AUTH_PATH', '/OneAPI/v1/auth');
|
||||||
$this->executorPath = (string) env('DIGIT_MOTOR_EXECUTOR_PATH', '/OneAPI/v1/executor');
|
$this->executorPath = (string) env('DIGIT_MOTOR_EXECUTOR_PATH', '/OneAPI/v1/executor');
|
||||||
$this->username = (string) env('DIGIT_MOTOR_USERNAME', '');
|
$this->username = (string) env('DIGIT_MOTOR_USERNAME', '');
|
||||||
$this->password = (string) env('DIGIT_MOTOR_PASSWORD', '');
|
$this->password = (string) env('DIGIT_MOTOR_PASSWORD', '');
|
||||||
|
|||||||
@ -1209,6 +1209,15 @@ $routes->group('digit-motor', ['filter' => 'authMVC', 'namespace' => 'App\Contro
|
|||||||
$routes->get('journey/(:num)', 'DigitMotorController::journey/$1');
|
$routes->get('journey/(:num)', 'DigitMotorController::journey/$1');
|
||||||
$routes->get('quotes/(:num)', 'DigitMotorController::detail/$1');
|
$routes->get('quotes/(:num)', 'DigitMotorController::detail/$1');
|
||||||
|
|
||||||
|
// Digit master lookups
|
||||||
|
$routes->get('masters/bootstrap', 'DigitMotorController::mastersBootstrap');
|
||||||
|
$routes->get('masters/vehicles/search', 'DigitMotorController::masterVehicleSearch');
|
||||||
|
$routes->get('masters/vehicles/makes', 'DigitMotorController::masterVehicleMakes');
|
||||||
|
$routes->get('masters/vehicles/models', 'DigitMotorController::masterVehicleModels');
|
||||||
|
$routes->get('masters/vehicles/variants', 'DigitMotorController::masterVehicleVariants');
|
||||||
|
$routes->get('masters/pincode/(:num)', 'DigitMotorController::masterPincode/$1');
|
||||||
|
$routes->get('masters/pincode', 'DigitMotorController::masterPincode');
|
||||||
|
|
||||||
$routes->post('quotes/quick', 'DigitMotorController::quickQuote');
|
$routes->post('quotes/quick', 'DigitMotorController::quickQuote');
|
||||||
$routes->post('quotes/create', 'DigitMotorController::createQuote');
|
$routes->post('quotes/create', 'DigitMotorController::createQuote');
|
||||||
$routes->post('quotes/(:num)/create', 'DigitMotorController::createQuote/$1');
|
$routes->post('quotes/(:num)/create', 'DigitMotorController::createQuote/$1');
|
||||||
|
|||||||
@ -4,6 +4,13 @@ namespace App\Controllers;
|
|||||||
|
|
||||||
use App\Libraries\DigitMotor\DigitApiException;
|
use App\Libraries\DigitMotor\DigitApiException;
|
||||||
use App\Libraries\DigitMotor\DigitExecutorService;
|
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 App\Models\MotorQuoteModel;
|
||||||
use CodeIgniter\API\ResponseTrait;
|
use CodeIgniter\API\ResponseTrait;
|
||||||
|
|
||||||
@ -13,12 +20,26 @@ class DigitMotorController extends BaseController
|
|||||||
|
|
||||||
protected MotorQuoteModel $quoteModel;
|
protected MotorQuoteModel $quoteModel;
|
||||||
protected DigitExecutorService $executor;
|
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()
|
public function __construct()
|
||||||
{
|
{
|
||||||
set_session_context('Digit Motor Controller');
|
set_session_context('Digit Motor Controller');
|
||||||
$this->quoteModel = new MotorQuoteModel();
|
$this->quoteModel = new MotorQuoteModel();
|
||||||
$this->executor = new DigitExecutorService();
|
$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 =====================
|
// ===================== LIST =====================
|
||||||
@ -78,6 +99,92 @@ class DigitMotorController extends BaseController
|
|||||||
return $this->respond(['status' => true, 'data' => $detail]);
|
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 =====================
|
// ===================== API ACTIONS =====================
|
||||||
|
|
||||||
public function quickQuote()
|
public function quickQuote()
|
||||||
@ -85,17 +192,30 @@ class DigitMotorController extends BaseController
|
|||||||
return $this->runAction(function () {
|
return $this->runAction(function () {
|
||||||
$input = $this->request->getJSON(true) ?: $this->request->getPost();
|
$input = $this->request->getJSON(true) ?: $this->request->getPost();
|
||||||
$rules = [
|
$rules = [
|
||||||
'license_plate_number' => 'required|min_length[4]',
|
'license_plate_number' => 'required|min_length[4]',
|
||||||
'vehicle_maincode' => 'required',
|
'vehicle_maincode' => 'required',
|
||||||
'registration_date' => 'required',
|
'registration_date' => 'required',
|
||||||
'manufacture_date' => 'required',
|
'manufacture_date' => 'required',
|
||||||
'pincode' => 'required|exact_length[6]',
|
'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)) {
|
if (!$this->validateDigitInput($input, $rules)) {
|
||||||
|
$errors = $this->validator->getErrors();
|
||||||
|
$first = reset($errors);
|
||||||
return $this->respond([
|
return $this->respond([
|
||||||
'status' => false,
|
'status' => false,
|
||||||
'message' => 'Validation failed.',
|
'message' => is_string($first) ? $first : 'Validation failed.',
|
||||||
'errors' => $this->validator->getErrors(),
|
'errors' => $errors,
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$masterError = $this->validateQuickQuoteMasters($input);
|
||||||
|
if ($masterError !== null) {
|
||||||
|
return $this->respond([
|
||||||
|
'status' => false,
|
||||||
|
'message' => $masterError,
|
||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +237,26 @@ class DigitMotorController extends BaseController
|
|||||||
return $this->respond(['status' => false, 'message' => 'quote_id is required.'], 422);
|
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);
|
$result = $this->executor->createQuote($quoteId, $input);
|
||||||
return $this->respond([
|
return $this->respond([
|
||||||
'status' => true,
|
'status' => true,
|
||||||
@ -197,7 +337,7 @@ class DigitMotorController extends BaseController
|
|||||||
try {
|
try {
|
||||||
return $fn();
|
return $fn();
|
||||||
} catch (DigitApiException $e) {
|
} catch (DigitApiException $e) {
|
||||||
log_message('error', 'DigitMotor API error: ' . $e->getMessage() . ' code=' . $e->getDigitCode());
|
log_message('error', $e->toLogMessage());
|
||||||
$http = $e->getHttpStatus() >= 400 ? $e->getHttpStatus() : 502;
|
$http = $e->getHttpStatus() >= 400 ? $e->getHttpStatus() : 502;
|
||||||
if ($e->isInfraError()) {
|
if ($e->isInfraError()) {
|
||||||
$http = 502;
|
$http = 502;
|
||||||
@ -223,4 +363,48 @@ class DigitMotorController extends BaseController
|
|||||||
$this->validator->setRules($rules);
|
$this->validator->setRules($rules);
|
||||||
return $this->validator->run($data);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
152
app/Database/digit_motor_master_tables.sql
Normal file
152
app/Database/digit_motor_master_tables.sql
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
-- Digit Motor master / lookup tables
|
||||||
|
-- Source: Digit API Integration kit → Masters/
|
||||||
|
-- Run once (or via: php spark digit-motor:import-masters --create-tables)
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_vehicle (
|
||||||
|
vehicle_code VARCHAR(30) NOT NULL,
|
||||||
|
make VARCHAR(80) NOT NULL,
|
||||||
|
model VARCHAR(120) NOT NULL,
|
||||||
|
variant VARCHAR(120) DEFAULT NULL,
|
||||||
|
body_type VARCHAR(60) DEFAULT NULL,
|
||||||
|
seating_capacity SMALLINT DEFAULT NULL,
|
||||||
|
power DECIMAL(10,2) DEFAULT NULL,
|
||||||
|
cubic_capacity DECIMAL(10,2) DEFAULT NULL,
|
||||||
|
gross_vehicle_weight DECIMAL(12,2) DEFAULT NULL,
|
||||||
|
fuel_type VARCHAR(40) DEFAULT NULL,
|
||||||
|
no_of_wheels TINYINT DEFAULT NULL,
|
||||||
|
abs CHAR(1) DEFAULT NULL,
|
||||||
|
air_bags SMALLINT DEFAULT NULL,
|
||||||
|
length_m DECIMAL(10,3) DEFAULT NULL,
|
||||||
|
ex_showroom_price DECIMAL(14,2) DEFAULT NULL,
|
||||||
|
price_year SMALLINT DEFAULT NULL,
|
||||||
|
production_status VARCHAR(60) DEFAULT NULL,
|
||||||
|
manufacturing VARCHAR(40) DEFAULT NULL,
|
||||||
|
vehicle_type VARCHAR(40) DEFAULT NULL,
|
||||||
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (vehicle_code),
|
||||||
|
KEY idx_mmv_make (make),
|
||||||
|
KEY idx_mmv_make_model (make, model),
|
||||||
|
KEY idx_mmv_make_model_variant (make, model, variant),
|
||||||
|
KEY idx_mmv_active (is_active)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_previous_insurer (
|
||||||
|
insurer_code VARCHAR(10) NOT NULL,
|
||||||
|
insurer_name VARCHAR(180) NOT NULL,
|
||||||
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (insurer_code),
|
||||||
|
KEY idx_mmpi_name (insurer_name)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_product (
|
||||||
|
product_code VARCHAR(10) NOT NULL,
|
||||||
|
product_name VARCHAR(120) NOT NULL,
|
||||||
|
vehicle_class VARCHAR(10) DEFAULT NULL COMMENT '2W / 4W / CV',
|
||||||
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (product_code)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_sub_product (
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
business_type VARCHAR(20) NOT NULL COMMENT 'NEW / ROLLOVER',
|
||||||
|
product_label VARCHAR(120) NOT NULL,
|
||||||
|
sub_product_code VARCHAR(20) NOT NULL,
|
||||||
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE KEY uq_mmsp (business_type, product_label, sub_product_code)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_pincode (
|
||||||
|
pincode VARCHAR(6) NOT NULL,
|
||||||
|
city VARCHAR(120) DEFAULT NULL,
|
||||||
|
district VARCHAR(120) DEFAULT NULL,
|
||||||
|
street VARCHAR(180) DEFAULT NULL,
|
||||||
|
taluk VARCHAR(120) DEFAULT NULL,
|
||||||
|
state_code VARCHAR(10) DEFAULT NULL,
|
||||||
|
segment VARCHAR(40) DEFAULT NULL,
|
||||||
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (pincode),
|
||||||
|
KEY idx_mmp_city (city),
|
||||||
|
KEY idx_mmp_state (state_code)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_rto (
|
||||||
|
rto_code VARCHAR(10) NOT NULL,
|
||||||
|
city_state VARCHAR(180) DEFAULT NULL,
|
||||||
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (rto_code)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_ncb (
|
||||||
|
ncb_code VARCHAR(30) NOT NULL,
|
||||||
|
sort_order SMALLINT NOT NULL DEFAULT 0,
|
||||||
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (ncb_code)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_previous_policy_type (
|
||||||
|
policy_type_code VARCHAR(20) NOT NULL,
|
||||||
|
description VARCHAR(120) DEFAULT NULL,
|
||||||
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (policy_type_code)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_voluntary_deductible (
|
||||||
|
deductible_code VARCHAR(40) NOT NULL,
|
||||||
|
sort_order SMALLINT NOT NULL DEFAULT 0,
|
||||||
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (deductible_code)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_doc_type (
|
||||||
|
doc_code VARCHAR(10) NOT NULL,
|
||||||
|
doc_type VARCHAR(60) NOT NULL,
|
||||||
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (doc_code)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_nominee_relation (
|
||||||
|
relation_code VARCHAR(40) NOT NULL,
|
||||||
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (relation_code)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_state (
|
||||||
|
state_code VARCHAR(10) NOT NULL,
|
||||||
|
state_name VARCHAR(120) NOT NULL,
|
||||||
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (state_code)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_addon_age_limit (
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
addon_name VARCHAR(120) NOT NULL,
|
||||||
|
age_limit_4w VARCHAR(120) DEFAULT NULL,
|
||||||
|
age_limit_2w VARCHAR(255) DEFAULT NULL,
|
||||||
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE KEY uq_mmaal_addon (addon_name)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS motor_master_import_log (
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
master_key VARCHAR(60) NOT NULL,
|
||||||
|
source_file VARCHAR(255) DEFAULT NULL,
|
||||||
|
rows_upserted INT NOT NULL DEFAULT 0,
|
||||||
|
status VARCHAR(20) NOT NULL DEFAULT 'OK',
|
||||||
|
message TEXT DEFAULT NULL,
|
||||||
|
imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
KEY idx_mmil_master (master_key),
|
||||||
|
KEY idx_mmil_imported (imported_at)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS motor_quote (
|
|||||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
enquiry_id VARCHAR(64) NOT NULL,
|
enquiry_id VARCHAR(64) NOT NULL,
|
||||||
quote_number VARCHAR(32) DEFAULT NULL,
|
quote_number VARCHAR(32) DEFAULT NULL,
|
||||||
application_id VARCHAR(128) DEFAULT NULL,
|
application_id VARCHAR(255) DEFAULT NULL,
|
||||||
policy_holder_type VARCHAR(20) NOT NULL DEFAULT 'INDIVIDUAL',
|
policy_holder_type VARCHAR(20) NOT NULL DEFAULT 'INDIVIDUAL',
|
||||||
insurance_product_code VARCHAR(10) NOT NULL,
|
insurance_product_code VARCHAR(10) NOT NULL,
|
||||||
sub_insurance_product_code VARCHAR(10) NOT NULL DEFAULT 'PB',
|
sub_insurance_product_code VARCHAR(10) NOT NULL DEFAULT 'PB',
|
||||||
@ -78,7 +78,7 @@ CREATE TABLE IF NOT EXISTS motor_kyc (
|
|||||||
CREATE TABLE IF NOT EXISTS motor_payment (
|
CREATE TABLE IF NOT EXISTS motor_payment (
|
||||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
quote_id BIGINT NOT NULL,
|
quote_id BIGINT NOT NULL,
|
||||||
application_id VARCHAR(128) NOT NULL,
|
application_id VARCHAR(255) NOT NULL,
|
||||||
digit_payment_id VARCHAR(64) DEFAULT NULL,
|
digit_payment_id VARCHAR(64) DEFAULT NULL,
|
||||||
request_reference VARCHAR(64) DEFAULT NULL,
|
request_reference VARCHAR(64) DEFAULT NULL,
|
||||||
payment_mode VARCHAR(5) DEFAULT 'EB',
|
payment_mode VARCHAR(5) DEFAULT 'EB',
|
||||||
|
|||||||
@ -77,7 +77,15 @@ class DigitApiClient
|
|||||||
|
|
||||||
if ($this->isHardFail($httpCode, $digitCode, $response['status'] ?? false, $data)) {
|
if ($this->isHardFail($httpCode, $digitCode, $response['status'] ?? false, $data)) {
|
||||||
$message = $this->extractMessage($data, $httpCode);
|
$message = $this->extractMessage($data, $httpCode);
|
||||||
throw new DigitApiException($message, $digitCode, $httpCode, $data);
|
throw new DigitApiException(
|
||||||
|
$message,
|
||||||
|
$digitCode,
|
||||||
|
$httpCode,
|
||||||
|
$data,
|
||||||
|
null,
|
||||||
|
$url,
|
||||||
|
$this->redact($payload)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return is_array($data) ? $data : [];
|
return is_array($data) ? $data : [];
|
||||||
@ -97,10 +105,18 @@ class DigitApiClient
|
|||||||
}
|
}
|
||||||
// Digit sometimes returns 200 with error object
|
// Digit sometimes returns 200 with error object
|
||||||
if (is_array($data) && isset($data['error']) && !isset($data['grossPremium']) && !isset($data['premium']) && !isset($data['quoteNumber'])) {
|
if (is_array($data) && isset($data['error']) && !isset($data['grossPremium']) && !isset($data['premium']) && !isset($data['quoteNumber'])) {
|
||||||
$errCode = $data['error']['code'] ?? $data['code'] ?? null;
|
$errCode = $data['error']['errorCode']
|
||||||
|
?? $data['error']['code']
|
||||||
|
?? $data['code']
|
||||||
|
?? $data['statusCode']
|
||||||
|
?? null;
|
||||||
if ($errCode !== null && (string) $errCode !== '0' && strtoupper((string) $errCode) !== 'SUCCESS') {
|
if ($errCode !== null && (string) $errCode !== '0' && strtoupper((string) $errCode) !== 'SUCCESS') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
// validationMessages without a success payload
|
||||||
|
if (!empty($data['error']['validationMessages'])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -110,10 +126,12 @@ class DigitApiClient
|
|||||||
if (!is_array($data)) {
|
if (!is_array($data)) {
|
||||||
return $httpCode ?: null;
|
return $httpCode ?: null;
|
||||||
}
|
}
|
||||||
return $data['error']['code']
|
return $data['error']['errorCode']
|
||||||
|
?? $data['error']['code']
|
||||||
?? $data['code']
|
?? $data['code']
|
||||||
?? $data['errorCode']
|
?? $data['errorCode']
|
||||||
?? $data['responseCode']
|
?? $data['responseCode']
|
||||||
|
?? $data['statusCode']
|
||||||
?? ($httpCode >= 400 ? (string) $httpCode : null);
|
?? ($httpCode >= 400 ? (string) $httpCode : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,13 +140,31 @@ class DigitApiClient
|
|||||||
if (!is_array($data)) {
|
if (!is_array($data)) {
|
||||||
return 'Digit API request failed (HTTP ' . $httpCode . ').';
|
return 'Digit API request failed (HTTP ' . $httpCode . ').';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Digit often returns validationMessages[] instead of a single message
|
||||||
|
$validation = $data['error']['validationMessages'] ?? $data['validationMessages'] ?? null;
|
||||||
|
if (is_array($validation) && $validation) {
|
||||||
|
$parts = [];
|
||||||
|
foreach ($validation as $item) {
|
||||||
|
if (is_string($item) && trim($item) !== '') {
|
||||||
|
$parts[] = trim($item);
|
||||||
|
} elseif (is_array($item)) {
|
||||||
|
$parts[] = trim((string) ($item['message'] ?? json_encode($item)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($parts) {
|
||||||
|
return implode(' | ', $parts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$msg = $data['error']['message']
|
$msg = $data['error']['message']
|
||||||
?? $data['message']
|
?? $data['message']
|
||||||
?? $data['errorMessage']
|
?? $data['errorMessage']
|
||||||
?? $data['responseMessage']
|
?? $data['responseMessage']
|
||||||
|
?? $data['statusMessage']
|
||||||
?? null;
|
?? null;
|
||||||
if (is_array($msg)) {
|
if (is_array($msg)) {
|
||||||
$msg = json_encode($msg);
|
$msg = implode(' | ', array_map('strval', $msg));
|
||||||
}
|
}
|
||||||
return $msg ?: 'Digit API request failed (HTTP ' . $httpCode . ').';
|
return $msg ?: 'Digit API request failed (HTTP ' . $httpCode . ').';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,19 +10,25 @@ class DigitApiException extends Exception
|
|||||||
protected $digitMessage;
|
protected $digitMessage;
|
||||||
protected $httpStatus;
|
protected $httpStatus;
|
||||||
protected $responseBody;
|
protected $responseBody;
|
||||||
|
protected $requestUrl;
|
||||||
|
protected $requestBody;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $message,
|
string $message,
|
||||||
$digitCode = null,
|
$digitCode = null,
|
||||||
int $httpStatus = 0,
|
int $httpStatus = 0,
|
||||||
$responseBody = null,
|
$responseBody = null,
|
||||||
?Exception $previous = null
|
?Exception $previous = null,
|
||||||
|
?string $requestUrl = null,
|
||||||
|
$requestBody = null
|
||||||
) {
|
) {
|
||||||
parent::__construct($message, 0, $previous);
|
parent::__construct($message, 0, $previous);
|
||||||
$this->digitCode = $digitCode;
|
$this->digitCode = $digitCode;
|
||||||
$this->digitMessage = $message;
|
$this->digitMessage = $message;
|
||||||
$this->httpStatus = $httpStatus;
|
$this->httpStatus = $httpStatus;
|
||||||
$this->responseBody = $responseBody;
|
$this->responseBody = $responseBody;
|
||||||
|
$this->requestUrl = $requestUrl;
|
||||||
|
$this->requestBody = $requestBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDigitCode()
|
public function getDigitCode()
|
||||||
@ -40,9 +46,43 @@ class DigitApiException extends Exception
|
|||||||
return $this->responseBody;
|
return $this->responseBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getRequestUrl(): ?string
|
||||||
|
{
|
||||||
|
return $this->requestUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestBody()
|
||||||
|
{
|
||||||
|
return $this->requestBody;
|
||||||
|
}
|
||||||
|
|
||||||
public function isInfraError(): bool
|
public function isInfraError(): bool
|
||||||
{
|
{
|
||||||
$code = (string) $this->digitCode;
|
$code = (string) $this->digitCode;
|
||||||
return in_array($code, ['403', '999'], true) || in_array($this->httpStatus, [403], true);
|
return in_array($code, ['403', '999'], true) || in_array($this->httpStatus, [403], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compact log line with url / request / response for debugging.
|
||||||
|
*/
|
||||||
|
public function toLogMessage(): string
|
||||||
|
{
|
||||||
|
$encode = static function ($value): string {
|
||||||
|
if ($value === null) {
|
||||||
|
return 'null';
|
||||||
|
}
|
||||||
|
if (is_string($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
$json = json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||||
|
return $json !== false ? $json : '[unencodable]';
|
||||||
|
};
|
||||||
|
|
||||||
|
return 'DigitMotor API error: ' . $this->getMessage()
|
||||||
|
. ' | code=' . $this->digitCode
|
||||||
|
. ' | http=' . $this->httpStatus
|
||||||
|
. ' | url=' . ($this->requestUrl ?? 'n/a')
|
||||||
|
. ' | request=' . $encode($this->requestBody)
|
||||||
|
. ' | response=' . $encode($this->responseBody);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -85,12 +85,33 @@ class DigitAuthClient
|
|||||||
?? null;
|
?? null;
|
||||||
|
|
||||||
if (empty($accessToken) || empty($response['status'])) {
|
if (empty($accessToken) || empty($response['status'])) {
|
||||||
$msg = $data['message'] ?? $data['error'] ?? 'Digit token generation failed.';
|
$msg = null;
|
||||||
|
if (is_array($data)) {
|
||||||
|
$msg = $data['message'] ?? $data['error'] ?? $data['error_description'] ?? null;
|
||||||
|
if (is_array($msg)) {
|
||||||
|
$msg = json_encode($msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$msg && is_string($response['data'] ?? null) && stripos((string) $response['data'], '502') !== false) {
|
||||||
|
$msg = 'Digit auth gateway is down (HTTP 502). Retry in a few minutes.';
|
||||||
|
}
|
||||||
|
if (!$msg && $httpCode >= 500) {
|
||||||
|
$msg = 'Digit auth service unavailable (HTTP ' . $httpCode . ').';
|
||||||
|
}
|
||||||
|
if (!$msg) {
|
||||||
|
$msg = 'Digit token generation failed.';
|
||||||
|
}
|
||||||
throw new DigitApiException(
|
throw new DigitApiException(
|
||||||
is_string($msg) ? $msg : 'Digit token generation failed.',
|
$msg,
|
||||||
$data['code'] ?? (string) $httpCode,
|
$data['code'] ?? (string) $httpCode,
|
||||||
$httpCode,
|
$httpCode,
|
||||||
$data
|
is_array($data) ? $data : ['raw' => $response['data'] ?? null],
|
||||||
|
null,
|
||||||
|
$url,
|
||||||
|
[
|
||||||
|
'username' => $this->config->username,
|
||||||
|
'password' => '***REDACTED***',
|
||||||
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
namespace App\Libraries\DigitMotor;
|
namespace App\Libraries\DigitMotor;
|
||||||
|
|
||||||
use App\Models\MotorKycModel;
|
use App\Models\MotorKycModel;
|
||||||
|
use App\Models\MotorApiLogModel;
|
||||||
use App\Models\MotorPaymentModel;
|
use App\Models\MotorPaymentModel;
|
||||||
use App\Models\MotorPolicyModel;
|
use App\Models\MotorPolicyModel;
|
||||||
use App\Models\MotorQuoteModel;
|
use App\Models\MotorQuoteModel;
|
||||||
@ -99,6 +100,29 @@ class DigitExecutorService
|
|||||||
throw new DigitApiException('Quote not found.', '404', 404);
|
throw new DigitApiException('Quote not found.', '404', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Digit Create Quote rejects empty / invalid engine + chassis (VIN).
|
||||||
|
$vin = strtoupper(trim((string) ($input['vehicle_identification_number'] ?? ($detail['vehicle']['vehicle_identification_number'] ?? ''))));
|
||||||
|
$engine = strtoupper(trim((string) ($input['engine_number'] ?? ($detail['vehicle']['engine_number'] ?? ''))));
|
||||||
|
if ($vin === '' || strlen($vin) < 5) {
|
||||||
|
throw new DigitApiException('Please enter a valid chassis / VIN number.', '400', 422);
|
||||||
|
}
|
||||||
|
if ($engine === '' || strlen($engine) < 5) {
|
||||||
|
throw new DigitApiException('Please enter a valid engine number.', '400', 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$vehicle = $this->vehicleModel->where('quote_id', $quoteId)->first();
|
||||||
|
if ($vehicle) {
|
||||||
|
$this->vehicleModel->update($vehicle['id'], [
|
||||||
|
'vehicle_identification_number' => $vin,
|
||||||
|
'engine_number' => $engine,
|
||||||
|
]);
|
||||||
|
$detail['vehicle']['vehicle_identification_number'] = $vin;
|
||||||
|
$detail['vehicle']['engine_number'] = $engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
$input['vehicle_identification_number'] = $vin;
|
||||||
|
$input['engine_number'] = $engine;
|
||||||
|
|
||||||
$payload = $this->buildCreateQuotePayload($detail, $input);
|
$payload = $this->buildCreateQuotePayload($detail, $input);
|
||||||
|
|
||||||
$response = $this->api->post(
|
$response = $this->api->post(
|
||||||
@ -206,8 +230,23 @@ class DigitExecutorService
|
|||||||
throw new DigitApiException('Application ID is required for payment. Create quote first.', '400', 400);
|
throw new DigitApiException('Application ID is required for payment. Create quote first.', '400', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
$premiumAmount = $input['premium_amount']
|
// Prefer UI override, then stored premium; Digit expects "INR 1234.56"
|
||||||
?? ('INR ' . number_format((float) ($detail['premium'] ?? 0), 2, '.', ''));
|
$premiumValue = null;
|
||||||
|
if (!empty($input['premium_amount'])) {
|
||||||
|
$premiumValue = $this->parseMoneyAmount($input['premium_amount']);
|
||||||
|
}
|
||||||
|
if ($premiumValue === null) {
|
||||||
|
$premiumValue = $this->parseMoneyAmount($detail['premium'] ?? null);
|
||||||
|
}
|
||||||
|
if ($premiumValue === null || $premiumValue <= 0) {
|
||||||
|
throw new DigitApiException(
|
||||||
|
'Premium amount is missing or zero. Re-run Create Quote before generating payment link.',
|
||||||
|
'400',
|
||||||
|
422
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$premiumAmount = 'INR ' . number_format($premiumValue, 2, '.', '');
|
||||||
|
|
||||||
$successUrl = $input['success_return_url']
|
$successUrl = $input['success_return_url']
|
||||||
?? base_url('digit-motor/payment/callback/success/' . $quoteId);
|
?? base_url('digit-motor/payment/callback/success/' . $quoteId);
|
||||||
@ -222,6 +261,12 @@ class DigitExecutorService
|
|||||||
'applicationId' => $detail['application_id'],
|
'applicationId' => $detail['application_id'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// When Digit's ABS lookup fails, payment API asks for create-quote payload as absContractDetails.
|
||||||
|
$cqResponse = $this->latestCreateQuoteResponse($quoteId);
|
||||||
|
if (is_array($cqResponse) && $cqResponse !== []) {
|
||||||
|
$payload['absContractDetails'] = $cqResponse;
|
||||||
|
}
|
||||||
|
|
||||||
$response = $this->api->post(
|
$response = $this->api->post(
|
||||||
$this->config->executorPath,
|
$this->config->executorPath,
|
||||||
$payload,
|
$payload,
|
||||||
@ -243,13 +288,18 @@ class DigitExecutorService
|
|||||||
'cancel_return_url' => $cancelUrl,
|
'cancel_return_url' => $cancelUrl,
|
||||||
'success_return_url' => $successUrl,
|
'success_return_url' => $successUrl,
|
||||||
'dispatcher_response' => $dispatcher,
|
'dispatcher_response' => $dispatcher,
|
||||||
'premium' => $detail['premium'],
|
'premium' => $premiumValue,
|
||||||
'payment_status' => 'LINK_GENERATED',
|
'payment_status' => 'LINK_GENERATED',
|
||||||
'created_at' => date('Y-m-d H:i:s'),
|
'created_at' => date('Y-m-d H:i:s'),
|
||||||
];
|
];
|
||||||
|
|
||||||
$row['id'] = $this->paymentModel->insert($row);
|
$row['id'] = $this->paymentModel->insert($row);
|
||||||
|
|
||||||
|
// Keep quote premium in sync if it was previously null/zero
|
||||||
|
if (empty($detail['premium']) || (float) $detail['premium'] <= 0) {
|
||||||
|
$this->quoteModel->update($quoteId, ['premium' => $premiumValue]);
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'quote_id' => $quoteId,
|
'quote_id' => $quoteId,
|
||||||
'payment' => $row,
|
'payment' => $row,
|
||||||
@ -258,6 +308,32 @@ class DigitExecutorService
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last successful Create Quote response for this quote (used as absContractDetails).
|
||||||
|
*/
|
||||||
|
protected function latestCreateQuoteResponse(int $quoteId): ?array
|
||||||
|
{
|
||||||
|
$logModel = new MotorApiLogModel();
|
||||||
|
$row = $logModel
|
||||||
|
->where('quote_id', $quoteId)
|
||||||
|
->where('integration_id', $this->config->integrationIds['createQuote'])
|
||||||
|
->where('http_status', 200)
|
||||||
|
->orderBy('id', 'DESC')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (!$row || empty($row['response_body'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $row['response_body'];
|
||||||
|
if (is_string($body)) {
|
||||||
|
$decoded = json_decode($body, true);
|
||||||
|
return is_array($decoded) ? $decoded : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_array($body) ? $body : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Poll policy status.
|
* Poll policy status.
|
||||||
*
|
*
|
||||||
@ -523,9 +599,11 @@ class DigitExecutorService
|
|||||||
'isVehicleNew' => !empty($vehicle['is_vehicle_new']) ? 'false' : 'false',
|
'isVehicleNew' => !empty($vehicle['is_vehicle_new']) ? 'false' : 'false',
|
||||||
'vehicleMaincode' => $vehicle['vehicle_maincode'] ?? '',
|
'vehicleMaincode' => $vehicle['vehicle_maincode'] ?? '',
|
||||||
'licensePlateNumber' => $plate,
|
'licensePlateNumber' => $plate,
|
||||||
'vehicleIdentificationNumber' => $vehicle['vehicle_identification_number'] ?? '',
|
'vehicleIdentificationNumber' => $input['vehicle_identification_number']
|
||||||
|
?? ($vehicle['vehicle_identification_number'] ?? ''),
|
||||||
'registrationAuthority' => $authority,
|
'registrationAuthority' => $authority,
|
||||||
'engineNumber' => $vehicle['engine_number'] ?? '',
|
'engineNumber' => $input['engine_number']
|
||||||
|
?? ($vehicle['engine_number'] ?? ''),
|
||||||
'manufactureDate' => $vehicle['manufacture_date'] ?? null,
|
'manufactureDate' => $vehicle['manufacture_date'] ?? null,
|
||||||
'registrationDate' => $vehicle['registration_date'] ?? null,
|
'registrationDate' => $vehicle['registration_date'] ?? null,
|
||||||
'vehicleIDV' => [
|
'vehicleIDV' => [
|
||||||
@ -722,17 +800,48 @@ class DigitExecutorService
|
|||||||
protected function pickNumber(array $response, array $keys): ?float
|
protected function pickNumber(array $response, array $keys): ?float
|
||||||
{
|
{
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
if (isset($response[$key]) && is_numeric($response[$key])) {
|
if (!array_key_exists($key, $response)) {
|
||||||
return (float) $response[$key];
|
continue;
|
||||||
|
}
|
||||||
|
$parsed = $this->parseMoneyAmount($response[$key]);
|
||||||
|
if ($parsed !== null) {
|
||||||
|
return $parsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isset($response['premiumBreakUp']) && is_array($response['premiumBreakUp'])) {
|
if (isset($response['premiumBreakUp']) && is_array($response['premiumBreakUp'])) {
|
||||||
foreach (['grossPremium', 'totalPremium', 'netPremium'] as $k) {
|
foreach (['grossPremium', 'totalPremium', 'netPremium'] as $k) {
|
||||||
if (isset($response['premiumBreakUp'][$k]) && is_numeric($response['premiumBreakUp'][$k])) {
|
if (!array_key_exists($k, $response['premiumBreakUp'])) {
|
||||||
return (float) $response['premiumBreakUp'][$k];
|
continue;
|
||||||
|
}
|
||||||
|
$parsed = $this->parseMoneyAmount($response['premiumBreakUp'][$k]);
|
||||||
|
if ($parsed !== null) {
|
||||||
|
return $parsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Digit returns money as numbers or strings like "INR 4030.88".
|
||||||
|
*/
|
||||||
|
protected function parseMoneyAmount($value): ?float
|
||||||
|
{
|
||||||
|
if ($value === null || $value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (is_int($value) || is_float($value)) {
|
||||||
|
return (float) $value;
|
||||||
|
}
|
||||||
|
if (!is_string($value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (is_numeric($value)) {
|
||||||
|
return (float) $value;
|
||||||
|
}
|
||||||
|
if (preg_match('/([0-9]+(?:\.[0-9]+)?)/', $value, $m)) {
|
||||||
|
return (float) $m[1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
633
app/Libraries/DigitMotor/DigitMasterImportService.php
Normal file
633
app/Libraries/DigitMotor/DigitMasterImportService.php
Normal file
@ -0,0 +1,633 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Libraries\DigitMotor;
|
||||||
|
|
||||||
|
use App\Models\MotorMasterAddonAgeLimitModel;
|
||||||
|
use App\Models\MotorMasterDocTypeModel;
|
||||||
|
use App\Models\MotorMasterImportLogModel;
|
||||||
|
use App\Models\MotorMasterNcbModel;
|
||||||
|
use App\Models\MotorMasterNomineeRelationModel;
|
||||||
|
use App\Models\MotorMasterPincodeModel;
|
||||||
|
use App\Models\MotorMasterPreviousInsurerModel;
|
||||||
|
use App\Models\MotorMasterPreviousPolicyTypeModel;
|
||||||
|
use App\Models\MotorMasterProductModel;
|
||||||
|
use App\Models\MotorMasterRtoModel;
|
||||||
|
use App\Models\MotorMasterStateModel;
|
||||||
|
use App\Models\MotorMasterSubProductModel;
|
||||||
|
use App\Models\MotorMasterVehicleModel;
|
||||||
|
use App\Models\MotorMasterVoluntaryDeductibleModel;
|
||||||
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Reader\IReadFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports Digit kit Masters Excel files into motor_master_* tables.
|
||||||
|
*/
|
||||||
|
class DigitMasterImportService
|
||||||
|
{
|
||||||
|
protected string $mastersPath;
|
||||||
|
protected $db;
|
||||||
|
protected MotorMasterImportLogModel $logModel;
|
||||||
|
|
||||||
|
/** @var callable|null */
|
||||||
|
protected $progressCallback;
|
||||||
|
|
||||||
|
public function __construct(?string $mastersPath = null)
|
||||||
|
{
|
||||||
|
$this->mastersPath = rtrim($mastersPath ?: '', '/');
|
||||||
|
$this->db = db_connect();
|
||||||
|
$this->logModel = new MotorMasterImportLogModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setProgressCallback(?callable $cb): self
|
||||||
|
{
|
||||||
|
$this->progressCallback = $cb;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createTables(string $sqlFile): void
|
||||||
|
{
|
||||||
|
if (!is_file($sqlFile)) {
|
||||||
|
throw new \RuntimeException('SQL file not found: ' . $sqlFile);
|
||||||
|
}
|
||||||
|
$sql = file_get_contents($sqlFile);
|
||||||
|
foreach (array_filter(array_map('trim', explode(';', $sql))) as $stmt) {
|
||||||
|
if ($stmt === '' || str_starts_with($stmt, '--')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// skip pure comment blocks
|
||||||
|
$clean = preg_replace('/^--.*$/m', '', $stmt);
|
||||||
|
$clean = trim($clean ?? '');
|
||||||
|
if ($clean === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->db->query($clean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, array{rows:int,status:string,message?:string}>
|
||||||
|
*/
|
||||||
|
public function importAll(array $only = []): array
|
||||||
|
{
|
||||||
|
$map = [
|
||||||
|
'products' => fn () => $this->importProducts(),
|
||||||
|
'previous_insurers' => fn () => $this->importPreviousInsurers(),
|
||||||
|
'ncb' => fn () => $this->importNcb(),
|
||||||
|
'voluntary_deductible'=> fn () => $this->importVoluntaryDeductible(),
|
||||||
|
'previous_policy_type'=> fn () => $this->importPreviousPolicyType(),
|
||||||
|
'doc_types' => fn () => $this->importDocTypes(),
|
||||||
|
'nominee_relations' => fn () => $this->importNomineeRelations(),
|
||||||
|
'states' => fn () => $this->importStates(),
|
||||||
|
'sub_products' => fn () => $this->importSubProducts(),
|
||||||
|
'addon_age_limits' => fn () => $this->importAddonAgeLimits(),
|
||||||
|
'pincodes' => fn () => $this->importPincodes(),
|
||||||
|
'rtos' => fn () => $this->importRtos(),
|
||||||
|
'vehicles' => fn () => $this->importVehicles(),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($only) {
|
||||||
|
$map = array_intersect_key($map, array_flip($only));
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = [];
|
||||||
|
foreach ($map as $key => $fn) {
|
||||||
|
$this->progress('Importing ' . $key . '...');
|
||||||
|
try {
|
||||||
|
$rows = $fn();
|
||||||
|
$results[$key] = ['rows' => $rows, 'status' => 'OK'];
|
||||||
|
$this->logImport($key, $rows, 'OK');
|
||||||
|
$this->progress($key . ': ' . $rows . ' rows');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$results[$key] = ['rows' => 0, 'status' => 'FAILED', 'message' => $e->getMessage()];
|
||||||
|
$this->logImport($key, 0, 'FAILED', $e->getMessage());
|
||||||
|
$this->progress($key . ' FAILED: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function progress(string $message): void
|
||||||
|
{
|
||||||
|
if ($this->progressCallback) {
|
||||||
|
($this->progressCallback)($message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function logImport(string $key, int $rows, string $status, ?string $message = null): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->logModel->insert([
|
||||||
|
'master_key' => $key,
|
||||||
|
'source_file' => $this->mastersPath,
|
||||||
|
'rows_upserted' => $rows,
|
||||||
|
'status' => $status,
|
||||||
|
'message' => $message,
|
||||||
|
'imported_at' => date('Y-m-d H:i:s'),
|
||||||
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// ignore log failures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function findFile(array $candidates): string
|
||||||
|
{
|
||||||
|
foreach ($candidates as $name) {
|
||||||
|
$path = $this->mastersPath . '/' . $name;
|
||||||
|
if (is_file($path)) {
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new \RuntimeException('Master file not found. Tried: ' . implode(', ', $candidates));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadSheet(string $path, string $sheetName = null, int $maxRows = 0): array
|
||||||
|
{
|
||||||
|
$reader = IOFactory::createReaderForFile($path);
|
||||||
|
$reader->setReadDataOnly(true);
|
||||||
|
if (method_exists($reader, 'setReadEmptyCells')) {
|
||||||
|
$reader->setReadEmptyCells(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sheetName) {
|
||||||
|
$reader->setLoadSheetsOnly([$sheetName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$spreadsheet = $reader->load($path);
|
||||||
|
$sheet = $sheetName
|
||||||
|
? $spreadsheet->getSheetByName($sheetName)
|
||||||
|
: $spreadsheet->getActiveSheet();
|
||||||
|
|
||||||
|
if (!$sheet) {
|
||||||
|
$sheet = $spreadsheet->getSheet(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $sheet->toArray(null, true, true, false);
|
||||||
|
$spreadsheet->disconnectWorksheets();
|
||||||
|
unset($spreadsheet);
|
||||||
|
|
||||||
|
if ($maxRows > 0 && count($rows) > $maxRows) {
|
||||||
|
$rows = array_slice($rows, 0, $maxRows);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function upsertBatch(string $table, array $rows, array $updateCols): int
|
||||||
|
{
|
||||||
|
if (!$rows) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
foreach (array_chunk($rows, 500) as $chunk) {
|
||||||
|
$this->db->table($table)->upsertBatch($chunk);
|
||||||
|
$count += count($chunk);
|
||||||
|
}
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function cell($row, int $idx): string
|
||||||
|
{
|
||||||
|
$v = $row[$idx] ?? '';
|
||||||
|
if ($v === null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return trim((string) $v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importPreviousInsurers(): int
|
||||||
|
{
|
||||||
|
$path = $this->findFile(['Motor Prevoius insurer List.xlsx', 'Motor Previous insurer List.xlsx']);
|
||||||
|
$rows = $this->loadSheet($path);
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$out = [];
|
||||||
|
|
||||||
|
foreach ($rows as $i => $row) {
|
||||||
|
if ($i === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$code = $this->cell($row, 0);
|
||||||
|
$name = $this->cell($row, 1);
|
||||||
|
if ($code === '' || !preg_match('/^\d+$/', $code) || $name === '' || strcasecmp($name, 'Name') === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$out[] = [
|
||||||
|
'insurer_code' => str_pad($code, 3, '0', STR_PAD_LEFT),
|
||||||
|
'insurer_name' => $name,
|
||||||
|
'is_active' => 1,
|
||||||
|
'imported_at' => $now,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->upsertBatch('motor_master_previous_insurer', $out, ['insurer_name', 'is_active', 'imported_at']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importProducts(): int
|
||||||
|
{
|
||||||
|
// Seed from known Digit product matrix (also present in Product Code Description workbook headers)
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$products = [
|
||||||
|
['20201', '2W Comprehensive', '2W'],
|
||||||
|
['20202', '2W TP only', '2W'],
|
||||||
|
['20203', '2W SAOD', '2W'],
|
||||||
|
['20101', '4W Comprehensive', '4W'],
|
||||||
|
['20102', '4W TP only', '4W'],
|
||||||
|
['20103', '4W SAOD', '4W'],
|
||||||
|
['20301', 'CV Comprehensive', 'CV'],
|
||||||
|
['20302', 'CV TP only', 'CV'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$out = [];
|
||||||
|
foreach ($products as [$code, $name, $class]) {
|
||||||
|
$out[] = [
|
||||||
|
'product_code' => $code,
|
||||||
|
'product_name' => $name,
|
||||||
|
'vehicle_class' => $class,
|
||||||
|
'is_active' => 1,
|
||||||
|
'imported_at' => $now,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->upsertBatch('motor_master_product', $out, ['product_name', 'vehicle_class', 'is_active', 'imported_at']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importNcb(): int
|
||||||
|
{
|
||||||
|
$path = $this->findFile(['NCB Master.xlsx']);
|
||||||
|
$rows = $this->loadSheet($path);
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$out = [];
|
||||||
|
$order = 0;
|
||||||
|
|
||||||
|
foreach ($rows as $i => $row) {
|
||||||
|
$code = strtoupper($this->cell($row, 0));
|
||||||
|
if ($code === '' || $code === 'NCB') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$out[] = [
|
||||||
|
'ncb_code' => $code,
|
||||||
|
'sort_order' => $order++,
|
||||||
|
'is_active' => 1,
|
||||||
|
'imported_at' => $now,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->upsertBatch('motor_master_ncb', $out, ['sort_order', 'is_active', 'imported_at']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importVoluntaryDeductible(): int
|
||||||
|
{
|
||||||
|
$path = $this->findFile(['Volunatry Deductible Master.xlsx', 'Voluntary Deductible Master.xlsx']);
|
||||||
|
$rows = $this->loadSheet($path);
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$out = [];
|
||||||
|
$order = 0;
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$code = strtoupper($this->cell($row, 0));
|
||||||
|
if ($code === '' || str_contains($code, 'VOLUNTARY')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$out[] = [
|
||||||
|
'deductible_code' => $code,
|
||||||
|
'sort_order' => $order++,
|
||||||
|
'is_active' => 1,
|
||||||
|
'imported_at' => $now,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->upsertBatch('motor_master_voluntary_deductible', $out, ['sort_order', 'is_active', 'imported_at']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importPreviousPolicyType(): int
|
||||||
|
{
|
||||||
|
$path = $this->findFile(['Previous_Policy_Type.xlsx']);
|
||||||
|
$rows = $this->loadSheet($path, 'Description');
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$out = [];
|
||||||
|
|
||||||
|
foreach ($rows as $i => $row) {
|
||||||
|
$code = strtoupper(trim($this->cell($row, 0)));
|
||||||
|
$desc = $this->cell($row, 1);
|
||||||
|
if ($i === 0 || $code === '' || $code === 'DOMNAME') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$out[] = [
|
||||||
|
'policy_type_code' => $code,
|
||||||
|
'description' => $desc,
|
||||||
|
'is_active' => 1,
|
||||||
|
'imported_at' => $now,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->upsertBatch('motor_master_previous_policy_type', $out, ['description', 'is_active', 'imported_at']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importDocTypes(): int
|
||||||
|
{
|
||||||
|
$path = $this->findFile(['Doc Type Master.xlsx', 'Doc Type Master1.xlsx']);
|
||||||
|
$rows = $this->loadSheet($path, 'KYC doc Type');
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$out = [];
|
||||||
|
|
||||||
|
foreach ($rows as $i => $row) {
|
||||||
|
$code = strtoupper($this->cell($row, 0));
|
||||||
|
$type = strtoupper($this->cell($row, 1));
|
||||||
|
if ($i === 0 || $code === '' || $code === 'DOCUMENT_NUMBER') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$out[] = [
|
||||||
|
'doc_code' => $code,
|
||||||
|
'doc_type' => $type,
|
||||||
|
'is_active' => 1,
|
||||||
|
'imported_at' => $now,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->upsertBatch('motor_master_doc_type', $out, ['doc_type', 'is_active', 'imported_at']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importNomineeRelations(): int
|
||||||
|
{
|
||||||
|
$path = $this->findFile(['Nominee Master.xlsx']);
|
||||||
|
$rows = $this->loadSheet($path);
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$out = [];
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$code = strtoupper(trim($this->cell($row, 0)));
|
||||||
|
if ($code === '' || str_contains($code, 'NOMINEE')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$out[] = [
|
||||||
|
'relation_code' => $code,
|
||||||
|
'is_active' => 1,
|
||||||
|
'imported_at' => $now,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->upsertBatch('motor_master_nominee_relation', $out, ['is_active', 'imported_at']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importStates(): int
|
||||||
|
{
|
||||||
|
$path = $this->findFile(['State Code Master (1).xlsx', 'State Code Master.xlsx']);
|
||||||
|
$rows = $this->loadSheet($path);
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$out = [];
|
||||||
|
|
||||||
|
foreach ($rows as $i => $row) {
|
||||||
|
$code = preg_replace('/\s+/', '', $this->cell($row, 0));
|
||||||
|
$name = trim($this->cell($row, 1));
|
||||||
|
if ($i === 0 || $code === '' || stripos($code, 'State') !== false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$out[] = [
|
||||||
|
'state_code' => $code,
|
||||||
|
'state_name' => $name,
|
||||||
|
'is_active' => 1,
|
||||||
|
'imported_at' => $now,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->upsertBatch('motor_master_state', $out, ['state_name', 'is_active', 'imported_at']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importSubProducts(): int
|
||||||
|
{
|
||||||
|
$path = $this->findFile(['Motor Product Code Description (1).xlsx', 'Motor Product Code Description.xlsx']);
|
||||||
|
$rows = $this->loadSheet($path, 'SubinsuranceProductcode');
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$out = [];
|
||||||
|
$businessType = 'NEW';
|
||||||
|
|
||||||
|
foreach ($rows as $i => $row) {
|
||||||
|
$col0 = $this->cell($row, 0);
|
||||||
|
$label = $this->cell($row, 1);
|
||||||
|
$code = $this->cell($row, 2);
|
||||||
|
|
||||||
|
if ($col0 !== '') {
|
||||||
|
if (stripos($col0, 'rollover') !== false || stripos($col0, 'renewal') !== false) {
|
||||||
|
$businessType = 'ROLLOVER';
|
||||||
|
} elseif (stripos($col0, 'new') !== false) {
|
||||||
|
$businessType = 'NEW';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($label === '' || $code === '' || stripos($label, 'Product') === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// code cell may contain notes like "51 (51- 5 yr...)"
|
||||||
|
if (preg_match('/^([A-Za-z0-9]+)/', $code, $m)) {
|
||||||
|
$code = $m[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
$out[] = [
|
||||||
|
'business_type' => $businessType,
|
||||||
|
'product_label' => $label,
|
||||||
|
'sub_product_code' => $code,
|
||||||
|
'is_active' => 1,
|
||||||
|
'imported_at' => $now,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// unique by business+label+code
|
||||||
|
$uniq = [];
|
||||||
|
foreach ($out as $row) {
|
||||||
|
$k = $row['business_type'] . '|' . $row['product_label'] . '|' . $row['sub_product_code'];
|
||||||
|
$uniq[$k] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$uniq) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->table('motor_master_sub_product')->truncate();
|
||||||
|
$this->db->table('motor_master_sub_product')->insertBatch(array_values($uniq));
|
||||||
|
return count($uniq);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importAddonAgeLimits(): int
|
||||||
|
{
|
||||||
|
$path = $this->findFile(['Motor Product Code Description (1).xlsx', 'Motor Product Code Description.xlsx']);
|
||||||
|
$rows = $this->loadSheet($path, 'Addons age limit');
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$out = [];
|
||||||
|
|
||||||
|
foreach ($rows as $i => $row) {
|
||||||
|
$name = $this->cell($row, 0);
|
||||||
|
if ($i === 0 || $name === '' || stripos($name, 'Add On') === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$out[] = [
|
||||||
|
'addon_name' => $name,
|
||||||
|
'age_limit_4w' => $this->cell($row, 1) ?: null,
|
||||||
|
'age_limit_2w' => $this->cell($row, 2) ?: null,
|
||||||
|
'is_active' => 1,
|
||||||
|
'imported_at' => $now,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$out) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->table('motor_master_addon_age_limit')->truncate();
|
||||||
|
$this->db->table('motor_master_addon_age_limit')->insertBatch($out);
|
||||||
|
return count($out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importPincodes(): int
|
||||||
|
{
|
||||||
|
$path = $this->findFile(['Pin Code and RTO Master_2023.xlsx']);
|
||||||
|
$rows = $this->loadSheet($path, 'PIN CODE');
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$out = [];
|
||||||
|
$seen = [];
|
||||||
|
|
||||||
|
foreach ($rows as $i => $row) {
|
||||||
|
$pin = preg_replace('/\D+/', '', $this->cell($row, 0));
|
||||||
|
if ($i === 0 || strlen($pin) !== 6 || isset($seen[$pin])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$seen[$pin] = true;
|
||||||
|
$out[] = [
|
||||||
|
'pincode' => $pin,
|
||||||
|
'city' => $this->cell($row, 1) ?: null,
|
||||||
|
'district' => $this->cell($row, 2) ?: null,
|
||||||
|
'street' => $this->cell($row, 3) ?: null,
|
||||||
|
'taluk' => $this->cell($row, 4) ?: null,
|
||||||
|
'segment' => $this->cell($row, 5) ?: null,
|
||||||
|
'state_code' => null,
|
||||||
|
'is_active' => 1,
|
||||||
|
'imported_at' => $now,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->upsertBatch('motor_master_pincode', $out, [
|
||||||
|
'city', 'district', 'street', 'taluk', 'segment', 'is_active', 'imported_at',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importRtos(): int
|
||||||
|
{
|
||||||
|
$path = $this->findFile(['Pin Code and RTO Master_2023.xlsx']);
|
||||||
|
$rows = $this->loadSheet($path, 'RTO MASTER');
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$out = [];
|
||||||
|
$seen = [];
|
||||||
|
|
||||||
|
foreach ($rows as $i => $row) {
|
||||||
|
$city = $this->cell($row, 0);
|
||||||
|
$rto = strtoupper(preg_replace('/\s+/', '', $this->cell($row, 1)));
|
||||||
|
if ($i === 0 || $rto === '' || $rto === 'RTO' || isset($seen[$rto])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$seen[$rto] = true;
|
||||||
|
$out[] = [
|
||||||
|
'rto_code' => $rto,
|
||||||
|
'city_state' => $city ?: null,
|
||||||
|
'is_active' => 1,
|
||||||
|
'imported_at' => $now,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->upsertBatch('motor_master_rto', $out, ['city_state', 'is_active', 'imported_at']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function importVehicles(): int
|
||||||
|
{
|
||||||
|
$path = $this->findFile(['Vehicle Master_New (2).xlsx', 'Vehicle Master_New.xlsx', 'Vehicle Master.xlsx']);
|
||||||
|
$this->progress('Loading vehicle master workbook (large file)...');
|
||||||
|
|
||||||
|
$reader = IOFactory::createReaderForFile($path);
|
||||||
|
$reader->setReadDataOnly(true);
|
||||||
|
if (method_exists($reader, 'setReadEmptyCells')) {
|
||||||
|
$reader->setReadEmptyCells(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
$spreadsheet = $reader->load($path);
|
||||||
|
$sheet = $spreadsheet->getSheet(0);
|
||||||
|
$highestRow = (int) $sheet->getHighestDataRow();
|
||||||
|
$this->progress('Vehicle master rows to scan: ' . $highestRow);
|
||||||
|
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
$batch = [];
|
||||||
|
$count = 0;
|
||||||
|
$chunkSize = 500;
|
||||||
|
|
||||||
|
for ($r = 2; $r <= $highestRow; $r++) {
|
||||||
|
$code = trim((string) $sheet->getCell('A' . $r)->getValue());
|
||||||
|
if ($code === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$batch[] = [
|
||||||
|
'vehicle_code' => $code,
|
||||||
|
'make' => trim((string) $sheet->getCell('B' . $r)->getValue()),
|
||||||
|
'model' => trim((string) $sheet->getCell('C' . $r)->getValue()),
|
||||||
|
'variant' => trim((string) $sheet->getCell('D' . $r)->getValue()) ?: null,
|
||||||
|
'body_type' => trim((string) $sheet->getCell('E' . $r)->getValue()) ?: null,
|
||||||
|
'seating_capacity' => $this->toInt($sheet->getCell('F' . $r)->getValue()),
|
||||||
|
'power' => $this->toFloat($sheet->getCell('G' . $r)->getValue()),
|
||||||
|
'cubic_capacity' => $this->toFloat($sheet->getCell('H' . $r)->getValue()),
|
||||||
|
'gross_vehicle_weight' => $this->toFloat($sheet->getCell('I' . $r)->getValue()),
|
||||||
|
'fuel_type' => trim((string) $sheet->getCell('J' . $r)->getValue()) ?: null,
|
||||||
|
'no_of_wheels' => $this->toInt($sheet->getCell('K' . $r)->getValue()),
|
||||||
|
'abs' => substr(trim((string) $sheet->getCell('L' . $r)->getValue()), 0, 1) ?: null,
|
||||||
|
'air_bags' => $this->toInt($sheet->getCell('M' . $r)->getValue()),
|
||||||
|
'length_m' => $this->toFloat($sheet->getCell('N' . $r)->getValue()),
|
||||||
|
'ex_showroom_price' => $this->toFloat($sheet->getCell('O' . $r)->getValue()),
|
||||||
|
'price_year' => $this->toInt($sheet->getCell('P' . $r)->getValue()),
|
||||||
|
'production_status' => trim((string) $sheet->getCell('Q' . $r)->getValue()) ?: null,
|
||||||
|
'manufacturing' => trim((string) $sheet->getCell('R' . $r)->getValue()) ?: null,
|
||||||
|
'vehicle_type' => trim((string) $sheet->getCell('S' . $r)->getValue()) ?: null,
|
||||||
|
'is_active' => 1,
|
||||||
|
'imported_at' => $now,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (count($batch) >= $chunkSize) {
|
||||||
|
$this->db->table('motor_master_vehicle')->upsertBatch($batch);
|
||||||
|
$count += count($batch);
|
||||||
|
$batch = [];
|
||||||
|
if ($count % 5000 === 0) {
|
||||||
|
$this->progress('Vehicles upserted: ' . $count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($batch) {
|
||||||
|
$this->db->table('motor_master_vehicle')->upsertBatch($batch);
|
||||||
|
$count += count($batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
$spreadsheet->disconnectWorksheets();
|
||||||
|
unset($spreadsheet);
|
||||||
|
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function toInt($v): ?int
|
||||||
|
{
|
||||||
|
if ($v === null || $v === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!is_numeric($v)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (int) $v;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function toFloat($v): ?float
|
||||||
|
{
|
||||||
|
if ($v === null || $v === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!is_numeric($v)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (float) $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
app/Models/MotorMasterAddonAgeLimitModel.php
Normal file
17
app/Models/MotorMasterAddonAgeLimitModel.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterAddonAgeLimitModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_addon_age_limit';
|
||||||
|
protected $primaryKey = 'id';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = [
|
||||||
|
'addon_name', 'age_limit_4w', 'age_limit_2w', 'is_active', 'imported_at',
|
||||||
|
];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
}
|
||||||
16
app/Models/MotorMasterDocTypeModel.php
Normal file
16
app/Models/MotorMasterDocTypeModel.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterDocTypeModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_doc_type';
|
||||||
|
protected $primaryKey = 'doc_code';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $useAutoIncrement = false;
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = ['doc_code', 'doc_type', 'is_active', 'imported_at'];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
}
|
||||||
17
app/Models/MotorMasterImportLogModel.php
Normal file
17
app/Models/MotorMasterImportLogModel.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterImportLogModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_import_log';
|
||||||
|
protected $primaryKey = 'id';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = [
|
||||||
|
'master_key', 'source_file', 'rows_upserted', 'status', 'message', 'imported_at',
|
||||||
|
];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
}
|
||||||
21
app/Models/MotorMasterNcbModel.php
Normal file
21
app/Models/MotorMasterNcbModel.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterNcbModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_ncb';
|
||||||
|
protected $primaryKey = 'ncb_code';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $useAutoIncrement = false;
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = ['ncb_code', 'sort_order', 'is_active', 'imported_at'];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
|
public function activeList(): array
|
||||||
|
{
|
||||||
|
return $this->where('is_active', 1)->orderBy('sort_order')->orderBy('ncb_code')->findAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Models/MotorMasterNomineeRelationModel.php
Normal file
16
app/Models/MotorMasterNomineeRelationModel.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterNomineeRelationModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_nominee_relation';
|
||||||
|
protected $primaryKey = 'relation_code';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $useAutoIncrement = false;
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = ['relation_code', 'is_active', 'imported_at'];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
}
|
||||||
23
app/Models/MotorMasterPincodeModel.php
Normal file
23
app/Models/MotorMasterPincodeModel.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterPincodeModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_pincode';
|
||||||
|
protected $primaryKey = 'pincode';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $useAutoIncrement = false;
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = [
|
||||||
|
'pincode', 'city', 'district', 'street', 'taluk', 'state_code', 'segment', 'is_active', 'imported_at',
|
||||||
|
];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
|
public function findActive(string $pincode): ?array
|
||||||
|
{
|
||||||
|
return $this->where('pincode', $pincode)->where('is_active', 1)->first();
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/Models/MotorMasterPreviousInsurerModel.php
Normal file
21
app/Models/MotorMasterPreviousInsurerModel.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterPreviousInsurerModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_previous_insurer';
|
||||||
|
protected $primaryKey = 'insurer_code';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $useAutoIncrement = false;
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = ['insurer_code', 'insurer_name', 'is_active', 'imported_at'];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
|
public function activeList(): array
|
||||||
|
{
|
||||||
|
return $this->where('is_active', 1)->orderBy('insurer_name')->findAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/Models/MotorMasterPreviousPolicyTypeModel.php
Normal file
21
app/Models/MotorMasterPreviousPolicyTypeModel.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterPreviousPolicyTypeModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_previous_policy_type';
|
||||||
|
protected $primaryKey = 'policy_type_code';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $useAutoIncrement = false;
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = ['policy_type_code', 'description', 'is_active', 'imported_at'];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
|
public function activeList(): array
|
||||||
|
{
|
||||||
|
return $this->where('is_active', 1)->orderBy('policy_type_code')->findAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
25
app/Models/MotorMasterProductModel.php
Normal file
25
app/Models/MotorMasterProductModel.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterProductModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_product';
|
||||||
|
protected $primaryKey = 'product_code';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $useAutoIncrement = false;
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = ['product_code', 'product_name', 'vehicle_class', 'is_active', 'imported_at'];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
|
public function activeList(?string $vehicleClass = null): array
|
||||||
|
{
|
||||||
|
$builder = $this->where('is_active', 1);
|
||||||
|
if ($vehicleClass) {
|
||||||
|
$builder->where('vehicle_class', $vehicleClass);
|
||||||
|
}
|
||||||
|
return $builder->orderBy('product_code')->findAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Models/MotorMasterRtoModel.php
Normal file
16
app/Models/MotorMasterRtoModel.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterRtoModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_rto';
|
||||||
|
protected $primaryKey = 'rto_code';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $useAutoIncrement = false;
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = ['rto_code', 'city_state', 'is_active', 'imported_at'];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
}
|
||||||
16
app/Models/MotorMasterStateModel.php
Normal file
16
app/Models/MotorMasterStateModel.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterStateModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_state';
|
||||||
|
protected $primaryKey = 'state_code';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $useAutoIncrement = false;
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = ['state_code', 'state_name', 'is_active', 'imported_at'];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
}
|
||||||
17
app/Models/MotorMasterSubProductModel.php
Normal file
17
app/Models/MotorMasterSubProductModel.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterSubProductModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_sub_product';
|
||||||
|
protected $primaryKey = 'id';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = [
|
||||||
|
'business_type', 'product_label', 'sub_product_code', 'is_active', 'imported_at',
|
||||||
|
];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
}
|
||||||
101
app/Models/MotorMasterVehicleModel.php
Normal file
101
app/Models/MotorMasterVehicleModel.php
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterVehicleModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_vehicle';
|
||||||
|
protected $primaryKey = 'vehicle_code';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $useAutoIncrement = false;
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = [
|
||||||
|
'vehicle_code', 'make', 'model', 'variant', 'body_type', 'seating_capacity',
|
||||||
|
'power', 'cubic_capacity', 'gross_vehicle_weight', 'fuel_type', 'no_of_wheels',
|
||||||
|
'abs', 'air_bags', 'length_m', 'ex_showroom_price', 'price_year',
|
||||||
|
'production_status', 'manufacturing', 'vehicle_type', 'is_active', 'imported_at',
|
||||||
|
];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
|
public function findActive(string $vehicleCode): ?array
|
||||||
|
{
|
||||||
|
return $this->where('vehicle_code', $vehicleCode)->where('is_active', 1)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function search(string $q, int $limit = 30): array
|
||||||
|
{
|
||||||
|
$q = trim($q);
|
||||||
|
if ($q === '') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$builder = $this->builder()->where('is_active', 1);
|
||||||
|
|
||||||
|
if (preg_match('/^\d{6,}$/', $q)) {
|
||||||
|
$builder->like('vehicle_code', $q, 'after');
|
||||||
|
} else {
|
||||||
|
$builder->groupStart()
|
||||||
|
->like('make', $q)
|
||||||
|
->orLike('model', $q)
|
||||||
|
->orLike('variant', $q)
|
||||||
|
->orLike('vehicle_code', $q)
|
||||||
|
->groupEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $builder
|
||||||
|
->orderBy('make')
|
||||||
|
->orderBy('model')
|
||||||
|
->orderBy('variant')
|
||||||
|
->limit($limit)
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function distinctMakes(?string $q = null, int $limit = 100): array
|
||||||
|
{
|
||||||
|
$builder = $this->builder()
|
||||||
|
->select('make')
|
||||||
|
->where('is_active', 1)
|
||||||
|
->groupBy('make')
|
||||||
|
->orderBy('make')
|
||||||
|
->limit($limit);
|
||||||
|
|
||||||
|
if ($q) {
|
||||||
|
$builder->like('make', $q);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_column($builder->get()->getResultArray(), 'make');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function distinctModels(string $make, ?string $q = null, int $limit = 200): array
|
||||||
|
{
|
||||||
|
$builder = $this->builder()
|
||||||
|
->select('model')
|
||||||
|
->where('is_active', 1)
|
||||||
|
->where('make', $make)
|
||||||
|
->groupBy('model')
|
||||||
|
->orderBy('model')
|
||||||
|
->limit($limit);
|
||||||
|
|
||||||
|
if ($q) {
|
||||||
|
$builder->like('model', $q);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_column($builder->get()->getResultArray(), 'model');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function variants(string $make, string $model, int $limit = 200): array
|
||||||
|
{
|
||||||
|
return $this->builder()
|
||||||
|
->select('vehicle_code, variant, body_type, fuel_type, seating_capacity, cubic_capacity, production_status')
|
||||||
|
->where('is_active', 1)
|
||||||
|
->where('make', $make)
|
||||||
|
->where('model', $model)
|
||||||
|
->orderBy('variant')
|
||||||
|
->limit($limit)
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/Models/MotorMasterVoluntaryDeductibleModel.php
Normal file
21
app/Models/MotorMasterVoluntaryDeductibleModel.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use CodeIgniter\Model;
|
||||||
|
|
||||||
|
class MotorMasterVoluntaryDeductibleModel extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'motor_master_voluntary_deductible';
|
||||||
|
protected $primaryKey = 'deductible_code';
|
||||||
|
protected $returnType = 'array';
|
||||||
|
protected $useAutoIncrement = false;
|
||||||
|
protected $protectFields = true;
|
||||||
|
protected $allowedFields = ['deductible_code', 'sort_order', 'is_active', 'imported_at'];
|
||||||
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
|
public function activeList(): array
|
||||||
|
{
|
||||||
|
return $this->where('is_active', 1)->orderBy('sort_order')->orderBy('deductible_code')->findAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -108,10 +108,19 @@ $coverage = is_array($quote['coverage_details'] ?? null) ? $quote['coverage_deta
|
|||||||
.dm-field{display:flex;flex-direction:column;gap:6px;}
|
.dm-field{display:flex;flex-direction:column;gap:6px;}
|
||||||
.dm-field.full{grid-column:1/-1;}
|
.dm-field.full{grid-column:1/-1;}
|
||||||
.dm-field label{font-size:12px;color:var(--dm-muted);font-weight:500;margin:0;}
|
.dm-field label{font-size:12px;color:var(--dm-muted);font-weight:500;margin:0;}
|
||||||
|
.dm-field label .req{color:var(--dm-red);margin-left:2px;font-weight:700;}
|
||||||
.dm-field input,.dm-field select{
|
.dm-field input,.dm-field select{
|
||||||
height:38px;border:1px solid var(--dm-line);border-radius:8px;padding:0 12px;font-size:13.5px;
|
height:38px;border:1px solid var(--dm-line);border-radius:8px;padding:0 12px;font-size:13.5px;
|
||||||
background:var(--dm-paper);outline:none;width:100%;
|
background:var(--dm-paper);outline:none;width:100%;
|
||||||
}
|
}
|
||||||
|
.dm-field input.dm-invalid,.dm-field select.dm-invalid{
|
||||||
|
border-color:var(--dm-red)!important;background:#fff5f5;
|
||||||
|
}
|
||||||
|
.dm-field .dm-err{
|
||||||
|
display:none;font-size:11px;color:var(--dm-red);margin-top:2px;
|
||||||
|
}
|
||||||
|
.dm-field.has-error .dm-err{display:block;}
|
||||||
|
|
||||||
.dm-field input:focus,.dm-field select:focus{border-color:var(--dm-teal);background:#fff;}
|
.dm-field input:focus,.dm-field select:focus{border-color:var(--dm-teal);background:#fff;}
|
||||||
|
|
||||||
.dm-chips{display:flex;gap:8px;flex-wrap:wrap;}
|
.dm-chips{display:flex;gap:8px;flex-wrap:wrap;}
|
||||||
@ -164,6 +173,20 @@ $coverage = is_array($quote['coverage_details'] ?? null) ? $quote['coverage_deta
|
|||||||
.dm-alert.error{display:block;background:var(--dm-red-bg);color:var(--dm-red);}
|
.dm-alert.error{display:block;background:var(--dm-red-bg);color:var(--dm-red);}
|
||||||
.dm-alert.ok{display:block;background:var(--dm-green-bg);color:var(--dm-green);}
|
.dm-alert.ok{display:block;background:var(--dm-green-bg);color:var(--dm-green);}
|
||||||
.dm-loading{opacity:.55;pointer-events:none;}
|
.dm-loading{opacity:.55;pointer-events:none;}
|
||||||
|
.dm-suggest-wrap{position:relative;}
|
||||||
|
.dm-suggest{
|
||||||
|
position:absolute;left:0;right:0;top:100%;z-index:20;margin-top:4px;
|
||||||
|
background:#fff;border:1px solid var(--dm-line);border-radius:8px;
|
||||||
|
max-height:220px;overflow:auto;box-shadow:0 8px 20px rgba(0,0,0,.08);display:none;
|
||||||
|
}
|
||||||
|
.dm-suggest.open{display:block;}
|
||||||
|
.dm-suggest button{
|
||||||
|
display:block;width:100%;text-align:left;border:0;background:#fff;padding:8px 12px;
|
||||||
|
font-size:12.5px;color:var(--dm-ink);cursor:pointer;border-bottom:1px solid var(--dm-line);
|
||||||
|
}
|
||||||
|
.dm-suggest button:hover{background:var(--dm-teal-soft);}
|
||||||
|
.dm-suggest .meta{color:var(--dm-muted);font-size:11px;}
|
||||||
|
.dm-vcode-tag{font-family:monospace;font-size:12px;color:var(--dm-teal-dark);margin-top:4px;}
|
||||||
@media(max-width:768px){
|
@media(max-width:768px){
|
||||||
.dm-grid,.dm-grid.g3,.dm-quote-cards{grid-template-columns:1fr;}
|
.dm-grid,.dm-grid.g3,.dm-quote-cards{grid-template-columns:1fr;}
|
||||||
.dm-stop{width:52px;}
|
.dm-stop{width:52px;}
|
||||||
@ -205,26 +228,57 @@ $coverage = is_array($quote['coverage_details'] ?? null) ? $quote['coverage_deta
|
|||||||
|
|
||||||
<div class="dm-section">Vehicle</div>
|
<div class="dm-section">Vehicle</div>
|
||||||
<div class="dm-grid g3">
|
<div class="dm-grid g3">
|
||||||
<div class="dm-field"><label>Registration number</label><input id="f_reg" value="<?= esc($vehicle['license_plate_number'] ?? '') ?>" placeholder="GJ04DA8726"></div>
|
<div class="dm-field"><label>Registration number <span class="req">*</span></label><input id="f_reg" value="<?= esc($vehicle['license_plate_number'] ?? '') ?>" placeholder="GJ04DA8726" data-required="1" data-label="Registration number"><div class="dm-err">Required</div></div>
|
||||||
<div class="dm-field"><label>Vehicle code</label><input id="f_vcode" value="<?= esc($vehicle['vehicle_maincode'] ?? '') ?>" placeholder="1113811407"></div>
|
<div class="dm-field">
|
||||||
<div class="dm-field"><label>Registration date</label><input type="date" id="f_reg_date" value="<?= esc($vehicle['registration_date'] ?? '') ?>"></div>
|
<label>Make <span class="req">*</span></label>
|
||||||
<div class="dm-field"><label>Manufacture date</label><input type="date" id="f_mfg_date" value="<?= esc($vehicle['manufacture_date'] ?? '') ?>"></div>
|
<div class="dm-suggest-wrap">
|
||||||
<div class="dm-field"><label>Chassis / VIN</label><input id="f_vin" value="<?= esc($vehicle['vehicle_identification_number'] ?? '') ?>"></div>
|
<input id="f_make" placeholder="Search make..." autocomplete="off" data-required="1" data-label="Make">
|
||||||
<div class="dm-field"><label>Engine number</label><input id="f_engine" value="<?= esc($vehicle['engine_number'] ?? '') ?>"></div>
|
<div class="dm-suggest" id="sug_make"></div>
|
||||||
<div class="dm-field"><label>Pincode</label><input id="f_pin" value="<?= esc($quote['pincode'] ?? '') ?>" maxlength="6"></div>
|
</div>
|
||||||
<div class="dm-field"><label>Product code</label>
|
<div class="dm-err">Required</div>
|
||||||
<select id="f_product">
|
</div>
|
||||||
<option value="20101" <?= ($quote['insurance_product_code'] ?? '') == '20101' ? 'selected' : '' ?>>20101 · Private Car Package</option>
|
<div class="dm-field">
|
||||||
<option value="20102" <?= ($quote['insurance_product_code'] ?? '20102') == '20102' ? 'selected' : '' ?>>20102 · Private Car</option>
|
<label>Model <span class="req">*</span></label>
|
||||||
</select>
|
<select id="f_model" disabled data-required="1" data-label="Model"><option value="">Select make first</option></select>
|
||||||
|
<div class="dm-err">Required</div>
|
||||||
|
</div>
|
||||||
|
<div class="dm-field">
|
||||||
|
<label>Variant / vehicle code <span class="req">*</span></label>
|
||||||
|
<select id="f_variant" disabled data-required="1" data-label="Variant / vehicle code"><option value="">Select model first</option></select>
|
||||||
|
<div class="dm-vcode-tag" id="f_vcode_tag"><?= esc($vehicle['vehicle_maincode'] ?? '') ?></div>
|
||||||
|
<input type="hidden" id="f_vcode" value="<?= esc($vehicle['vehicle_maincode'] ?? '') ?>" data-required="1" data-label="Vehicle code">
|
||||||
|
<div class="dm-err">Required</div>
|
||||||
|
</div>
|
||||||
|
<div class="dm-field"><label>Or search vehicle code</label>
|
||||||
|
<div class="dm-suggest-wrap">
|
||||||
|
<input id="f_vsearch" placeholder="Type code / make / model..." autocomplete="off">
|
||||||
|
<div class="dm-suggest" id="sug_vsearch"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dm-field"><label>Registration date <span class="req">*</span></label><input type="date" id="f_reg_date" value="<?= esc($vehicle['registration_date'] ?? '') ?>" data-required="1" data-label="Registration date"><div class="dm-err">Required</div></div>
|
||||||
|
<div class="dm-field"><label>Manufacture date <span class="req">*</span></label><input type="date" id="f_mfg_date" value="<?= esc($vehicle['manufacture_date'] ?? '') ?>" data-required="1" data-label="Manufacture date"><div class="dm-err">Required</div></div>
|
||||||
|
<div class="dm-field"><label>Chassis / VIN <span class="req">*</span></label><input id="f_vin" value="<?= esc($vehicle['vehicle_identification_number'] ?? '') ?>" data-required="1" data-label="Chassis / VIN" minlength="5" placeholder="e.g. MAKGM651CJ4306951"><div class="dm-err">Required</div></div>
|
||||||
|
<div class="dm-field"><label>Engine number <span class="req">*</span></label><input id="f_engine" value="<?= esc($vehicle['engine_number'] ?? '') ?>" data-required="1" data-label="Engine number" minlength="5" placeholder="e.g. L15Z15337915"><div class="dm-err">Required</div></div>
|
||||||
|
<div class="dm-field"><label>Pincode <span class="req">*</span></label><input id="f_pin" value="<?= esc($quote['pincode'] ?? '') ?>" maxlength="6" data-required="1" data-label="Pincode" data-len="6"><div class="dm-vcode-tag" id="f_pin_tag"></div><div class="dm-err">Required (6 digits)</div></div>
|
||||||
|
<div class="dm-field"><label>Product code <span class="req">*</span></label>
|
||||||
|
<select id="f_product" data-required="1" data-label="Product code"><option value="">Loading products...</option></select>
|
||||||
|
<div class="dm-err">Required</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dm-field"><label>Policy start</label><input type="date" id="f_start" value="<?= esc($quote['start_date'] ?? '') ?>"></div>
|
<div class="dm-field"><label>Policy start</label><input type="date" id="f_start" value="<?= esc($quote['start_date'] ?? '') ?>"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dm-section">Previous policy</div>
|
<div class="dm-section">Previous policy</div>
|
||||||
<div class="dm-grid g3">
|
<div class="dm-grid g3">
|
||||||
<div class="dm-field"><label>Previous insurer code</label><input id="f_prev_ins" value="<?= esc($quote['previous_insurer_code'] ?? '') ?>" placeholder="190"></div>
|
<div class="dm-field"><label>Previous insurer</label>
|
||||||
|
<select id="f_prev_ins"><option value="">— none / unknown —</option></select>
|
||||||
|
</div>
|
||||||
<div class="dm-field"><label>Prior policy expiry</label><input type="date" id="f_prev_exp" value="<?= esc($quote['previous_policy_expiry_date'] ?? '') ?>"></div>
|
<div class="dm-field"><label>Prior policy expiry</label><input type="date" id="f_prev_exp" value="<?= esc($quote['previous_policy_expiry_date'] ?? '') ?>"></div>
|
||||||
|
<div class="dm-field"><label>Previous NCB</label>
|
||||||
|
<select id="f_prev_ncb"><option value="ZERO">ZERO</option></select>
|
||||||
|
</div>
|
||||||
|
<div class="dm-field"><label>Previous policy type</label>
|
||||||
|
<select id="f_prev_ptype"><option value="">— optional —</option></select>
|
||||||
|
</div>
|
||||||
<div class="dm-field"><label>Claim in last year</label>
|
<div class="dm-field"><label>Claim in last year</label>
|
||||||
<div class="dm-chips" style="height:38px;align-items:center;">
|
<div class="dm-chips" style="height:38px;align-items:center;">
|
||||||
<div class="dm-chip" data-group="claim" data-val="1" onclick="dmToggleExclusive(this)">Yes</div>
|
<div class="dm-chip" data-group="claim" data-val="1" onclick="dmToggleExclusive(this)">Yes</div>
|
||||||
@ -269,15 +323,21 @@ $coverage = is_array($quote['coverage_details'] ?? null) ? $quote['coverage_deta
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="dm-section">Vehicle identifiers</div>
|
||||||
|
<div class="dm-grid">
|
||||||
|
<div class="dm-field"><label>Chassis / VIN <span class="req">*</span></label><input id="f_cq_vin" value="<?= esc($vehicle['vehicle_identification_number'] ?? '') ?>" data-required="1" data-label="Chassis / VIN" minlength="5" placeholder="e.g. MAKGM651CJ4306951"><div class="dm-err">Required for create quote</div></div>
|
||||||
|
<div class="dm-field"><label>Engine number <span class="req">*</span></label><input id="f_cq_engine" value="<?= esc($vehicle['engine_number'] ?? '') ?>" data-required="1" data-label="Engine number" minlength="5" placeholder="e.g. L15Z15337915"><div class="dm-err">Required for create quote</div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="dm-section">Policyholder</div>
|
<div class="dm-section">Policyholder</div>
|
||||||
<div class="dm-grid">
|
<div class="dm-grid">
|
||||||
<div class="dm-field"><label>First name</label><input id="f_fname" value=""></div>
|
<div class="dm-field"><label>First name <span class="req">*</span></label><input id="f_fname" value="" data-required="1" data-label="First name"><div class="dm-err">Required</div></div>
|
||||||
<div class="dm-field"><label>Last name</label><input id="f_lname" value=""></div>
|
<div class="dm-field"><label>Last name</label><input id="f_lname" value=""></div>
|
||||||
<div class="dm-field"><label>Mobile</label><input id="f_mobile" value=""></div>
|
<div class="dm-field"><label>Mobile <span class="req">*</span></label><input id="f_mobile" value="" data-required="1" data-label="Mobile" data-len="10"><div class="dm-err">Required (10 digits)</div></div>
|
||||||
<div class="dm-field"><label>Email</label><input id="f_email" value=""></div>
|
<div class="dm-field"><label>Email <span class="req">*</span></label><input id="f_email" type="email" value="" data-required="1" data-label="Email"><div class="dm-err">Required</div></div>
|
||||||
<div class="dm-field"><label>PAN</label><input id="f_pan" value=""></div>
|
<div class="dm-field"><label>PAN <span class="req">*</span></label><input id="f_pan" value="" data-required="1" data-label="PAN"><div class="dm-err">Required</div></div>
|
||||||
<div class="dm-field"><label>Date of birth</label><input type="date" id="f_dob" value=""></div>
|
<div class="dm-field"><label>Date of birth <span class="req">*</span></label><input type="date" id="f_dob" value="" data-required="1" data-label="Date of birth"><div class="dm-err">Required</div></div>
|
||||||
<div class="dm-field full"><label>Address</label><input id="f_address" value=""></div>
|
<div class="dm-field full"><label>Address <span class="req">*</span></label><input id="f_address" value="" data-required="1" data-label="Address"><div class="dm-err">Required</div></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dm-actions">
|
<div class="dm-actions">
|
||||||
@ -411,11 +471,16 @@ $coverage = is_array($quote['coverage_details'] ?? null) ? $quote['coverage_deta
|
|||||||
window.DM = {
|
window.DM = {
|
||||||
quoteId: <?= $quoteId ? (int)$quoteId : 'null' ?>,
|
quoteId: <?= $quoteId ? (int)$quoteId : 'null' ?>,
|
||||||
base: '<?= rtrim(base_url('digit-motor'), '/') ?>',
|
base: '<?= rtrim(base_url('digit-motor'), '/') ?>',
|
||||||
step: <?= (int)$initialStep ?>
|
step: <?= (int)$initialStep ?>,
|
||||||
|
selectedProduct: '<?= esc($quote['insurance_product_code'] ?? '20102') ?>',
|
||||||
|
selectedInsurer: '<?= esc($quote['previous_insurer_code'] ?? '') ?>',
|
||||||
|
selectedVehicleCode: '<?= esc($vehicle['vehicle_maincode'] ?? '') ?>'
|
||||||
};
|
};
|
||||||
|
|
||||||
const dmSteps = ["Quote","Create","KYC","Pay","Policy"];
|
const dmSteps = ["Quote","Create","KYC","Pay","Policy"];
|
||||||
const dmIcons = ["mdi-car","mdi-file-plus","mdi-shield-check","mdi-credit-card","mdi-file-check"];
|
const dmIcons = ["mdi-car","mdi-file-plus","mdi-shield-check","mdi-credit-card","mdi-file-check"];
|
||||||
|
let dmMakeTimer = null;
|
||||||
|
let dmSearchTimer = null;
|
||||||
|
|
||||||
(function initRoute(){
|
(function initRoute(){
|
||||||
const stops = document.getElementById('dmStops');
|
const stops = document.getElementById('dmStops');
|
||||||
@ -428,8 +493,163 @@ const dmIcons = ["mdi-car","mdi-file-plus","mdi-shield-check","mdi-credit-card",
|
|||||||
stops.appendChild(el);
|
stops.appendChild(el);
|
||||||
});
|
});
|
||||||
dmGo(window.DM.step, true);
|
dmGo(window.DM.step, true);
|
||||||
|
dmLoadMasters();
|
||||||
|
dmBindMasterUi();
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
function dmFillSelect(sel, items, valueKey, labelFn, selected, emptyLabel){
|
||||||
|
const $s = $(sel);
|
||||||
|
$s.empty();
|
||||||
|
if (emptyLabel != null) {
|
||||||
|
$s.append($('<option>').val('').text(emptyLabel));
|
||||||
|
}
|
||||||
|
(items || []).forEach(function(item){
|
||||||
|
const val = typeof item === 'string' ? item : item[valueKey];
|
||||||
|
const label = typeof labelFn === 'function' ? labelFn(item) : val;
|
||||||
|
const opt = $('<option>').val(val).text(label);
|
||||||
|
if (selected != null && String(selected) === String(val)) opt.prop('selected', true);
|
||||||
|
$s.append(opt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dmLoadMasters(){
|
||||||
|
dmGet(window.DM.base + '/masters/bootstrap')
|
||||||
|
.done(function(res){
|
||||||
|
if (!res.status){
|
||||||
|
dmAlert(res.message || 'Masters not loaded');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const d = res.data || {};
|
||||||
|
dmFillSelect('#f_product', d.products || [], 'product_code', function(p){
|
||||||
|
return p.product_code + ' · ' + p.product_name;
|
||||||
|
}, window.DM.selectedProduct, null);
|
||||||
|
|
||||||
|
dmFillSelect('#f_prev_ins', d.previous_insurers || [], 'insurer_code', function(p){
|
||||||
|
return p.insurer_code + ' · ' + p.insurer_name;
|
||||||
|
}, window.DM.selectedInsurer, '— none / unknown —');
|
||||||
|
|
||||||
|
dmFillSelect('#f_prev_ncb', d.ncb || [], 'ncb_code', function(p){ return p.ncb_code; }, 'ZERO', null);
|
||||||
|
|
||||||
|
dmFillSelect('#f_prev_ptype', d.previous_policy_types || [], 'policy_type_code', function(p){
|
||||||
|
return p.policy_type_code + (p.description ? ' · ' + p.description : '');
|
||||||
|
}, '', '— optional —');
|
||||||
|
})
|
||||||
|
.fail(function(){
|
||||||
|
dmAlert('Could not load Digit masters. Import with: php spark digit-motor:import-masters --create-tables');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dmBindMasterUi(){
|
||||||
|
$('#f_make').on('input', function(){
|
||||||
|
const q = $(this).val().trim();
|
||||||
|
clearTimeout(dmMakeTimer);
|
||||||
|
if (q.length < 1){ $('#sug_make').removeClass('open').empty(); return; }
|
||||||
|
dmMakeTimer = setTimeout(function(){
|
||||||
|
dmGet(window.DM.base + '/masters/vehicles/makes?q=' + encodeURIComponent(q))
|
||||||
|
.done(function(res){
|
||||||
|
const box = $('#sug_make').empty();
|
||||||
|
(res.data || []).slice(0, 40).forEach(function(make){
|
||||||
|
$('<button type="button">').text(make).on('click', function(){
|
||||||
|
$('#f_make').val(make);
|
||||||
|
box.removeClass('open').empty();
|
||||||
|
dmLoadModels(make);
|
||||||
|
}).appendTo(box);
|
||||||
|
});
|
||||||
|
box.toggleClass('open', (res.data || []).length > 0);
|
||||||
|
});
|
||||||
|
}, 250);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#f_model').on('change', function(){
|
||||||
|
const make = $('#f_make').val().trim();
|
||||||
|
const model = $(this).val();
|
||||||
|
if (make && model) dmLoadVariants(make, model);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#f_variant').on('change', function(){
|
||||||
|
const code = $(this).val();
|
||||||
|
dmSetVehicleCode(code);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#f_vsearch').on('input', function(){
|
||||||
|
const q = $(this).val().trim();
|
||||||
|
clearTimeout(dmSearchTimer);
|
||||||
|
if (q.length < 2){ $('#sug_vsearch').removeClass('open').empty(); return; }
|
||||||
|
dmSearchTimer = setTimeout(function(){
|
||||||
|
dmGet(window.DM.base + '/masters/vehicles/search?q=' + encodeURIComponent(q))
|
||||||
|
.done(function(res){
|
||||||
|
const box = $('#sug_vsearch').empty();
|
||||||
|
(res.data || []).forEach(function(v){
|
||||||
|
const label = (v.make || '') + ' ' + (v.model || '') + ' ' + (v.variant || '');
|
||||||
|
$('<button type="button">').html(
|
||||||
|
'<div>'+label.trim()+'</div><div class="meta">'+v.vehicle_code+(v.fuel_type ? ' · '+v.fuel_type : '')+'</div>'
|
||||||
|
).on('click', function(){
|
||||||
|
$('#f_make').val(v.make || '');
|
||||||
|
dmSetVehicleCode(v.vehicle_code);
|
||||||
|
$('#f_vsearch').val(label.trim());
|
||||||
|
box.removeClass('open').empty();
|
||||||
|
dmLoadModels(v.make || '', v.model || '', v.vehicle_code);
|
||||||
|
}).appendTo(box);
|
||||||
|
});
|
||||||
|
box.toggleClass('open', (res.data || []).length > 0);
|
||||||
|
});
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#f_pin').on('blur', function(){
|
||||||
|
const pin = $(this).val().trim();
|
||||||
|
if (pin.length !== 6){ $('#f_pin_tag').text(''); return; }
|
||||||
|
dmGet(window.DM.base + '/masters/pincode/' + pin)
|
||||||
|
.done(function(res){
|
||||||
|
if (res.status && res.data){
|
||||||
|
$('#f_pin_tag').text((res.data.city || '') + (res.data.district ? ', ' + res.data.district : ''));
|
||||||
|
} else {
|
||||||
|
$('#f_pin_tag').text('Pincode not in Digit master');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail(function(){ $('#f_pin_tag').text('Pincode not in Digit master'); });
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', function(e){
|
||||||
|
if (!$(e.target).closest('.dm-suggest-wrap').length){
|
||||||
|
$('.dm-suggest').removeClass('open');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dmSetVehicleCode(code){
|
||||||
|
$('#f_vcode').val(code || '');
|
||||||
|
$('#f_vcode_tag').text(code || '');
|
||||||
|
window.DM.selectedVehicleCode = code || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function dmLoadModels(make, selectedModel, selectedCode){
|
||||||
|
const $model = $('#f_model').prop('disabled', true).empty().append($('<option>').val('').text('Loading...'));
|
||||||
|
$('#f_variant').prop('disabled', true).empty().append($('<option>').val('').text('Select model first'));
|
||||||
|
dmGet(window.DM.base + '/masters/vehicles/models?make=' + encodeURIComponent(make))
|
||||||
|
.done(function(res){
|
||||||
|
dmFillSelect('#f_model', res.data || [], null, null, selectedModel || '', 'Select model');
|
||||||
|
$model.prop('disabled', false);
|
||||||
|
if (selectedModel) dmLoadVariants(make, selectedModel, selectedCode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dmLoadVariants(make, model, selectedCode){
|
||||||
|
const $v = $('#f_variant').prop('disabled', true).empty().append($('<option>').val('').text('Loading...'));
|
||||||
|
dmGet(window.DM.base + '/masters/vehicles/variants?make=' + encodeURIComponent(make) + '&model=' + encodeURIComponent(model))
|
||||||
|
.done(function(res){
|
||||||
|
$v.empty().append($('<option>').val('').text('Select variant'));
|
||||||
|
(res.data || []).forEach(function(v){
|
||||||
|
const label = (v.variant || 'STD') + ' · ' + v.vehicle_code + (v.fuel_type ? ' · ' + v.fuel_type : '');
|
||||||
|
const opt = $('<option>').val(v.vehicle_code).text(label);
|
||||||
|
if (selectedCode && String(selectedCode) === String(v.vehicle_code)) opt.prop('selected', true);
|
||||||
|
$v.append(opt);
|
||||||
|
});
|
||||||
|
$v.prop('disabled', false);
|
||||||
|
if (selectedCode) dmSetVehicleCode(selectedCode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function dmGo(i, silent){
|
function dmGo(i, silent){
|
||||||
window.DM.step = i;
|
window.DM.step = i;
|
||||||
document.querySelectorAll('.dm-step').forEach(v=> v.classList.toggle('active', Number(v.dataset.step)===i));
|
document.querySelectorAll('.dm-step').forEach(v=> v.classList.toggle('active', Number(v.dataset.step)===i));
|
||||||
@ -499,8 +719,76 @@ function dmGet(url){
|
|||||||
return $.ajax({ url: url, type: 'GET', dataType: 'json' });
|
return $.ajax({ url: url, type: 'GET', dataType: 'json' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dmClearFieldErrors($scope){
|
||||||
|
$scope = $scope || $(document);
|
||||||
|
$scope.find('.dm-field').removeClass('has-error');
|
||||||
|
$scope.find('.dm-invalid').removeClass('dm-invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
function dmMarkError($el, msg){
|
||||||
|
const $field = $el.closest('.dm-field');
|
||||||
|
$field.addClass('has-error');
|
||||||
|
$el.addClass('dm-invalid');
|
||||||
|
if (msg) $field.find('.dm-err').text(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dmValidateRequired(fieldIds){
|
||||||
|
dmClearFieldErrors();
|
||||||
|
const missing = [];
|
||||||
|
fieldIds.forEach(function(id){
|
||||||
|
const $el = $('#' + id);
|
||||||
|
if (!$el.length) return;
|
||||||
|
let val = ($el.val() || '').toString().trim();
|
||||||
|
const label = $el.data('label') || id;
|
||||||
|
const needLen = parseInt($el.data('len'), 10) || 0;
|
||||||
|
|
||||||
|
// Variant may be empty if vehicle was picked via search — accept hidden f_vcode
|
||||||
|
if (id === 'f_variant' && $('#f_vcode').val()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (id === 'f_make' && $('#f_vcode').val()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (id === 'f_model' && $('#f_vcode').val()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!val) {
|
||||||
|
dmMarkError($el, label + ' is required');
|
||||||
|
missing.push(label);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (needLen && val.replace(/\D/g,'').length < needLen && id === 'f_mobile') {
|
||||||
|
dmMarkError($el, 'Enter a valid 10-digit mobile');
|
||||||
|
missing.push(label);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (needLen && id === 'f_pin' && val.length !== needLen) {
|
||||||
|
dmMarkError($el, 'Pincode must be 6 digits');
|
||||||
|
missing.push(label);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (id === 'f_reg' && val.length < 4) {
|
||||||
|
dmMarkError($el, 'Registration number is too short');
|
||||||
|
missing.push(label);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return missing;
|
||||||
|
}
|
||||||
|
|
||||||
function dmQuickQuote(){
|
function dmQuickQuote(){
|
||||||
const claimChip = document.querySelector('.dm-chip[data-group="claim"].on');
|
const claimChip = document.querySelector('.dm-chip[data-group="claim"].on');
|
||||||
|
const missing = dmValidateRequired([
|
||||||
|
'f_reg', 'f_make', 'f_model', 'f_variant', 'f_vcode',
|
||||||
|
'f_reg_date', 'f_mfg_date', 'f_vin', 'f_engine', 'f_pin', 'f_product'
|
||||||
|
]);
|
||||||
|
if (missing.length){
|
||||||
|
dmAlert('Please fill required fields: ' + missing.join(', '));
|
||||||
|
const first = document.querySelector('.dm-field.has-error');
|
||||||
|
if (first) first.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
license_plate_number: $('#f_reg').val().trim(),
|
license_plate_number: $('#f_reg').val().trim(),
|
||||||
vehicle_maincode: $('#f_vcode').val().trim(),
|
vehicle_maincode: $('#f_vcode').val().trim(),
|
||||||
@ -514,6 +802,8 @@ function dmQuickQuote(){
|
|||||||
end_date: null,
|
end_date: null,
|
||||||
previous_insurer_code: $('#f_prev_ins').val() || null,
|
previous_insurer_code: $('#f_prev_ins').val() || null,
|
||||||
previous_policy_expiry_date: $('#f_prev_exp').val() || null,
|
previous_policy_expiry_date: $('#f_prev_exp').val() || null,
|
||||||
|
previous_ncb: $('#f_prev_ncb').val() || 'ZERO',
|
||||||
|
previous_policy_type: $('#f_prev_ptype').val() || '',
|
||||||
is_claim_in_last_year: claimChip ? claimChip.dataset.val === '1' : false,
|
is_claim_in_last_year: claimChip ? claimChip.dataset.val === '1' : false,
|
||||||
coverages: dmAddonFlags(),
|
coverages: dmAddonFlags(),
|
||||||
enquiry_id: window.DM.quoteId ? ($('#enquiryIdTag').text().trim() !== '— new —' ? $('#enquiryIdTag').text().trim() : null) : null
|
enquiry_id: window.DM.quoteId ? ($('#enquiryIdTag').text().trim() !== '— new —' ? $('#enquiryIdTag').text().trim() : null) : null
|
||||||
@ -539,10 +829,17 @@ function dmQuickQuote(){
|
|||||||
$('#payPremium').val(prem != null ? 'INR ' + prem.toFixed(2) : '');
|
$('#payPremium').val(prem != null ? 'INR ' + prem.toFixed(2) : '');
|
||||||
dmAlert('Quick quote generated.', true);
|
dmAlert('Quick quote generated.', true);
|
||||||
history.replaceState(null, '', window.DM.base + '/journey/' + window.DM.quoteId);
|
history.replaceState(null, '', window.DM.base + '/journey/' + window.DM.quoteId);
|
||||||
|
$('#f_cq_vin').val(body.vehicle_identification_number || '');
|
||||||
|
$('#f_cq_engine').val(body.engine_number || '');
|
||||||
dmGo(1);
|
dmGo(1);
|
||||||
})
|
})
|
||||||
.fail(function(xhr){
|
.fail(function(xhr){
|
||||||
const msg = (xhr.responseJSON && xhr.responseJSON.message) || 'Quick quote failed.';
|
let msg = (xhr.responseJSON && xhr.responseJSON.message) || 'Quick quote failed.';
|
||||||
|
const errs = xhr.responseJSON && xhr.responseJSON.errors;
|
||||||
|
if (errs && typeof errs === 'object') {
|
||||||
|
const parts = Object.keys(errs).map(function(k){ return errs[k]; });
|
||||||
|
if (parts.length) msg = parts.join(' | ');
|
||||||
|
}
|
||||||
dmAlert(msg);
|
dmAlert(msg);
|
||||||
})
|
})
|
||||||
.always(function(){ dmBusy(false); });
|
.always(function(){ dmBusy(false); });
|
||||||
@ -550,6 +847,19 @@ function dmQuickQuote(){
|
|||||||
|
|
||||||
function dmCreateQuote(){
|
function dmCreateQuote(){
|
||||||
if (!window.DM.quoteId){ dmAlert('Run quick quote first.'); return; }
|
if (!window.DM.quoteId){ dmAlert('Run quick quote first.'); return; }
|
||||||
|
|
||||||
|
// Keep QQ + CQ chassis/engine in sync when editing on step 2
|
||||||
|
if ($('#f_cq_vin').val()) $('#f_vin').val($('#f_cq_vin').val().trim().toUpperCase());
|
||||||
|
if ($('#f_cq_engine').val()) $('#f_engine').val($('#f_cq_engine').val().trim().toUpperCase());
|
||||||
|
|
||||||
|
const missing = dmValidateRequired(['f_cq_vin', 'f_cq_engine', 'f_fname', 'f_mobile', 'f_email', 'f_pan', 'f_dob', 'f_address']);
|
||||||
|
if (missing.length){
|
||||||
|
dmAlert('Please fill required fields: ' + missing.join(', '));
|
||||||
|
const first = document.querySelector('.dm-field.has-error');
|
||||||
|
if (first) first.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
first_name: $('#f_fname').val().trim(),
|
first_name: $('#f_fname').val().trim(),
|
||||||
last_name: $('#f_lname').val().trim(),
|
last_name: $('#f_lname').val().trim(),
|
||||||
@ -558,6 +868,8 @@ function dmCreateQuote(){
|
|||||||
pan: $('#f_pan').val().trim(),
|
pan: $('#f_pan').val().trim(),
|
||||||
dob: $('#f_dob').val(),
|
dob: $('#f_dob').val(),
|
||||||
address: $('#f_address').val().trim(),
|
address: $('#f_address').val().trim(),
|
||||||
|
vehicle_identification_number: $('#f_cq_vin').val().trim().toUpperCase(),
|
||||||
|
engine_number: $('#f_cq_engine').val().trim().toUpperCase(),
|
||||||
start_date: $('#f_start').val() || null,
|
start_date: $('#f_start').val() || null,
|
||||||
coverages: dmAddonFlags()
|
coverages: dmAddonFlags()
|
||||||
};
|
};
|
||||||
@ -610,8 +922,12 @@ function dmKycStatus(){
|
|||||||
|
|
||||||
function dmPaymentLink(){
|
function dmPaymentLink(){
|
||||||
if (!window.DM.quoteId){ dmAlert('No quote loaded.'); return; }
|
if (!window.DM.quoteId){ dmAlert('No quote loaded.'); return; }
|
||||||
|
const body = {
|
||||||
|
payment_mode: $('#payMode').val() || 'EB',
|
||||||
|
premium_amount: ($('#payPremium').val() || '').trim() || null
|
||||||
|
};
|
||||||
dmBusy(true);
|
dmBusy(true);
|
||||||
dmPost(window.DM.base + '/payments/' + window.DM.quoteId + '/link', {})
|
dmPost(window.DM.base + '/payments/' + window.DM.quoteId + '/link', body)
|
||||||
.done(function(res){
|
.done(function(res){
|
||||||
if (!res.status){ dmAlert(res.message || 'Payment link failed'); return; }
|
if (!res.status){ dmAlert(res.message || 'Payment link failed'); return; }
|
||||||
const link = res.data.dispatcher_response;
|
const link = res.data.dispatcher_response;
|
||||||
@ -669,4 +985,12 @@ function dmPolicyPdf(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', function(){ dmGo(window.DM.step, true); });
|
window.addEventListener('resize', function(){ dmGo(window.DM.step, true); });
|
||||||
|
|
||||||
|
$(document).on('input change', '.dm-field input, .dm-field select', function(){
|
||||||
|
const $field = $(this).closest('.dm-field');
|
||||||
|
if (($(this).val() || '').toString().trim()) {
|
||||||
|
$field.removeClass('has-error');
|
||||||
|
$(this).removeClass('dm-invalid');
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user