Compare commits
No commits in common. "31359b11951a3824cf769471610e8056cfbaa032" and "cff90ed42efeeced9b90e49d066a7d10c910d8d9" have entirely different histories.
31359b1195
...
cff90ed42e
8
.claude/settings.local.json
Normal file
8
.claude/settings.local.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(find /c/xampp/htdocs/PHP828APPS/ruc/nhance -type f -name *.php -path */migration*)",
|
||||
"Bash(find /c/xampp/htdocs/PHP828APPS/ruc/nhance -type f \\\\\\(-name *.sql -o -name schema* \\\\\\))"
|
||||
]
|
||||
}
|
||||
}
|
||||
5
.cpanel.yml
Executable file
5
.cpanel.yml
Executable file
@ -0,0 +1,5 @@
|
||||
---
|
||||
deployment:
|
||||
tasks:
|
||||
- export DEPLOYPATH=home/venbaehn/public_html/nhance/devauto
|
||||
- /bin/cp /home/venbaehn/repositories/nhance_dev/* $DEPLOYPATH -r
|
||||
251
.env.sample
Executable file
251
.env.sample
Executable file
@ -0,0 +1,251 @@
|
||||
#--------------------------------------------------------------------
|
||||
# ENVIRONMENT
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
CI_ENVIRONMENT =
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# APP
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
app.baseURL =
|
||||
app.appTimezone =
|
||||
# If you have trouble with `.`, you could also use `_`.
|
||||
# app_baseURL = ''
|
||||
# app.forceGlobalSecureRequests = false
|
||||
# app.CSPEnabled = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# DATABASE
|
||||
#--------------------------------------------------------------------
|
||||
DB_SSL_ENABLE =
|
||||
database.default.hostname =
|
||||
database.default.database =
|
||||
database.default.username =
|
||||
database.default.password =
|
||||
database.default.DBDriver =
|
||||
#database.default.DBPrefix =
|
||||
#database.default.port =
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# SESSION
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
# session.driver = 'CodeIgniter\Session\Handlers\FileHandler'
|
||||
# session.cookieName = 'ci_session'
|
||||
session.expiration =
|
||||
# session.savePath = null
|
||||
# session.matchIP = false
|
||||
session.timeToUpdate = 300
|
||||
session.regenerateDestroy = false
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# LOGGER
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
logger.threshold = 4
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# CURLRequest
|
||||
#--------------------------------------------------------------------
|
||||
# curlrequest.shareOptions = true
|
||||
|
||||
GOOGLE_OAUTH_APP_NAME =
|
||||
#GOOGLE_OAUTH_CLIENT_ID =
|
||||
#GOOGLE_OAUTH_CLIENT_SECRET =
|
||||
|
||||
GOOGLE_OAUTH_CLIENT_ID =
|
||||
GOOGLE_OAUTH_CLIENT_SECRET =
|
||||
GOOGLE_OAUTH_REDIRECT_URI =
|
||||
GOOGLE_OAUTH_SCOPES =
|
||||
App_Url =
|
||||
NHANCE_LOGO =
|
||||
helpdeskURL =
|
||||
TOKENTIMEOUT =
|
||||
# Grace seconds after token_time_out epoch before reset-token-timeout cron clears it (default 180 = 3 min)
|
||||
TOKEN_TIMEOUT_RESET_BUFFER_SECONDS = 180
|
||||
POST_ENROLLMENT_APP_LINK =
|
||||
GDRIVE_ROOT_FOLDER_ID =
|
||||
RFQ_PARENT_FOLDER_ID =
|
||||
|
||||
|
||||
email.fromEmail =
|
||||
email.fromName =
|
||||
ZEPTO_API_KEY =
|
||||
email.SMTPPass =
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# SET COOKIE DATA
|
||||
#--------------------------------------------------------------------
|
||||
cookie.Path = '/';
|
||||
cookie.Domain = '.nhanceindia.in'; //Enter Domain Name with preceding .(dot) -> .nhanceindia.in
|
||||
cookie.secure = 'true';// if https set as true or if http set as false
|
||||
|
||||
unlayer.projectID =
|
||||
email.enquiryMail =
|
||||
|
||||
#SMS
|
||||
SMS_API_KEY =
|
||||
SMS_SENDER_ID =
|
||||
SMS_TEMPLATE_ID =
|
||||
SMS_SERVICE_NAME =
|
||||
SMS_APP_NAME =
|
||||
|
||||
# CORS Configuration
|
||||
CORS_ALLOWED_ORIGINS=
|
||||
CORS_ALLOW_CREDENTIALS=true
|
||||
CORS_ALLOWED_METHODS=GET,POST,PUT,PATCH,DELETE,OPTIONS
|
||||
CORS_ALLOWED_HEADERS=Content-Type,Authorization,X-Requested-With,Accept,Origin,App-Signature
|
||||
CORS_EXPOSE_HEADERS=X-Total-Count,X-Page-Count,X-RateLimit-Remaining
|
||||
CORS_MAX_AGE=7200
|
||||
CORS_DEBUG=true
|
||||
|
||||
APP_SIGNATURE =
|
||||
TOKENTIMEOUT =
|
||||
JWT_SECRET =
|
||||
|
||||
ICICI_PRIMARY_KEY_CONSTANT =
|
||||
|
||||
ABHI_PRIMARY_KEY_CONSTANT =
|
||||
|
||||
R_CARE_PRIMARY_KEY_CONSTANT =
|
||||
|
||||
FHPL_PRIMARY_KEY_CONSTANT =
|
||||
|
||||
VIDAL_PRIMARY_KEY_CONSTANT =
|
||||
|
||||
MEDI_ASSIST_PRIMARY_KEY_CONSTANT =
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# TPA PRIMARY KEY CONSTANTS LIVE
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
MEDI_ASSIST_PRIMARY_KEY_CONSTANT_LIVE =
|
||||
ICICI_PRIMARY_KEY_CONSTANT_LIVE =
|
||||
ABHI_PRIMARY_KEY_CONSTANT_LIVE =
|
||||
R_CARE_PRIMARY_KEY_CONSTANT_LIVE =
|
||||
FHPL_PRIMARY_KEY_CONSTANT_LIVE =
|
||||
VIDAL_PRIMARY_KEY_CONSTANT_LIVE =
|
||||
VOLO_PRIMARY_KEY_CONSTANT_LIVE =
|
||||
|
||||
# When true, claims-collection-report falls back to ticket_master only if the
|
||||
# policy TPA has no mapped dump table (or dump table missing). Dump TPAs always use claim_report.
|
||||
CLAIM_REPORT_FALLBACK_TO_TICKET_MASTER = false
|
||||
|
||||
FHPL_TOKEN_URL =
|
||||
FHPL_BASE_URL =
|
||||
FHPL_USER_NAME =
|
||||
FHPL_PASSWORD =
|
||||
FHPL_GRANT_TYPE =
|
||||
|
||||
METABASE_SECRET_KEY=
|
||||
|
||||
|
||||
# Health care api details
|
||||
HEALTH_INDIA_BASE_URL =
|
||||
HEALTH_INDIA_TOKEN_URL =
|
||||
HEALTH_INDIA_USERNAME =
|
||||
HEALTH_INDIA_PASSWORD =
|
||||
|
||||
HEALTH_INDIA_PRIMARY_KEY_CONSTANT =
|
||||
|
||||
# Volo / TrueCover (EWA) — ewatpa.com APIs
|
||||
VOLO_PRIMARY_KEY_CONSTANT =
|
||||
VOLO_API_ADMIN_BASE_URL = https://uatapiadmin.ewatpa.com
|
||||
VOLO_API_CONSUMER_BASE_URL = https://uatapiconsumer.ewatpa.com
|
||||
VOLO_API_EMAIL =
|
||||
VOLO_API_PASSWORD =
|
||||
VOLO_LOGGED_IN_PORTAL = POLICY_CONFIGURATION_PORTAL
|
||||
# Optional: if get-entity-from-policy cannot be used
|
||||
VOLO_DEFAULT_ENTITY_ID =
|
||||
# Required for claim intimation — hospital id from Volo network master
|
||||
VOLO_DEFAULT_HOSPITAL_ID =
|
||||
|
||||
#For sending mail for leads RFQ/QCR
|
||||
LEAD_INSURER_FROM_MAIL_ID =
|
||||
LEAD_CLIENT_FROM_MAIL_ID =
|
||||
|
||||
# BDS Daily Report Emails Configuration
|
||||
bds.dailyReportEmails =
|
||||
|
||||
# BDS Installment Reminder (pending UTR / overdue) — used by getLeadInstallmentDetails / sendInstallmentRemainderMail cron
|
||||
# Master switch: when false, no installment reminder records are fetched
|
||||
bds.installmentReminder.enabled = true
|
||||
# Business days ahead of today to match upcoming payment_date (was hardcoded 5)
|
||||
bds.installmentReminder.businessDays = 5
|
||||
# When true, also fetch records with null UTR whose payment_date is already past (overdue)
|
||||
bds.installmentReminder.fetchOverduePendingUtr = false
|
||||
# When true, fetch every day; when false, only on days listed in bds.installmentReminder.days
|
||||
bds.installmentReminder.daily = true
|
||||
# Comma-separated weekdays (mon,tue,wed,thu,fri,sat,sun) — used only when daily = false
|
||||
bds.installmentReminder.days = mon,tue,wed,thu,fri
|
||||
|
||||
# Policy Transaction Renewal Reminder — used by sendRenewalReminderMail cron
|
||||
pt.renewalReminder.enabled = true
|
||||
pt.renewalReminder.daily = true
|
||||
pt.renewalReminder.days = mon,tue,wed,thu,fri
|
||||
# Client types: 1=Group, 2=Individual
|
||||
pt.renewalReminder.clientTypes = 1,2
|
||||
# renewal_date | policy_end_date
|
||||
pt.renewalReminder.dateField = renewal_date
|
||||
pt.renewalReminder.daysBefore = 30, 15, 7, 1, -2, -5, -10
|
||||
pt.renewalReminder.includeClientEmail = true
|
||||
# When true, also send for policies whose dateField is already past (overdue)
|
||||
pt.renewalReminder.includeOverdue = false
|
||||
pt.renewalReminder.toEmails =
|
||||
pt.renewalReminder.ccEmails =
|
||||
pt.renewalReminder.bccEmails =
|
||||
pt.renewalReminder.fromMail =
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# MEDI ASSIST WELLNESS SSO Configuration
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
MEDIASSIST_WELLNESS_KEY =
|
||||
MEDIASSIST_WELLNESS_IV =
|
||||
MEDIASSIST_WELLNESS_LOGIN_URL =
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Icici api details LIVE
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
ICICI_TOKEN_URL =
|
||||
ICICI_BASE_URL =
|
||||
ICICI_PASSWORD =
|
||||
ICICI_CLIENT_ID =
|
||||
ICICI_CLIENT_SECRET =
|
||||
ICICI_API_SCOPE =
|
||||
ICICI_GRANT_TYPE =
|
||||
|
||||
|
||||
ICICI_PRIMARY_KEY_CONSTANT =
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Digit Motor (OneAPI)
|
||||
#--------------------------------------------------------------------
|
||||
DIGIT_MOTOR_BASE_URL = https://preprod-oneapi.godigit.com
|
||||
DIGIT_MOTOR_AUTH_PATH = /OneAPI/v1/auth
|
||||
DIGIT_MOTOR_EXECUTOR_PATH = /OneAPI/v1/executor
|
||||
DIGIT_MOTOR_USERNAME =
|
||||
DIGIT_MOTOR_PASSWORD =
|
||||
DIGIT_MOTOR_ENVIRONMENT = staging
|
||||
DIGIT_MOTOR_TIMEOUT = 30
|
||||
DIGIT_MOTOR_TOKEN_LEEWAY_SEC = 60
|
||||
DIGIT_MOTOR_PDF_AUTH_KEY =
|
||||
DIGIT_MOTOR_MASTERS_PATH =
|
||||
DIGIT_MOTOR_IID_QUICK_QUOTE = 29266-0100
|
||||
DIGIT_MOTOR_IID_CREATE_QUOTE = 29268-0100
|
||||
DIGIT_MOTOR_IID_KYC = 29269-0100
|
||||
DIGIT_MOTOR_IID_PAYMENT = 29270-0100
|
||||
DIGIT_MOTOR_IID_POLICY_STATUS = 29271-0100
|
||||
DIGIT_MOTOR_IID_POLICY_PDF = 29272-0100
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# File storage (S3 uploads)
|
||||
#--------------------------------------------------------------------
|
||||
# FILE_STORAGE_DRIVER = s3
|
||||
# AWS_FILE_UPLOAD_BUCKET =
|
||||
# Keep permanent local copy after S3 upload in storage_file_Upload (default false)
|
||||
RETAIN_LOCAL = false
|
||||
45
.gitignore
vendored
Executable file
45
.gitignore
vendored
Executable file
@ -0,0 +1,45 @@
|
||||
#-------------------------
|
||||
# Temporary Files
|
||||
#-------------------------
|
||||
writable/cache/*
|
||||
!writable/cache/.gitkeep
|
||||
!writable/cache/storage_runtime/
|
||||
!writable/cache/storage_runtime/.gitkeep
|
||||
!writable/cache/claim_files_runtime/
|
||||
!writable/cache/claim_files_runtime/.gitkeep
|
||||
|
||||
writable/logs/*
|
||||
!writable/logs/.gitkeep
|
||||
|
||||
writable/session/*
|
||||
!writable/session/.gitkeep
|
||||
|
||||
writable/uploads/*
|
||||
!writable/uploads/.gitkeep
|
||||
|
||||
public/uploads/*
|
||||
!public/uploads/.gitkeep
|
||||
|
||||
writable/debugbar/*
|
||||
!writable/debugbar/.gitkeep
|
||||
|
||||
writable/**/*.db
|
||||
writable/**/*.sqlite
|
||||
|
||||
# writable/e_card_template/*
|
||||
# !writable/e_card_template/.gitkeep
|
||||
|
||||
writable/tmp/*
|
||||
!writable/tmp/.gitkeep
|
||||
|
||||
writable/insurer_claim_form/*
|
||||
!writable/insurer_claim_form/Claim_Form_IRDAI.pdf
|
||||
|
||||
vendor/
|
||||
build/
|
||||
composer.lock
|
||||
.env
|
||||
.phpunit*
|
||||
phpqueue.sh
|
||||
.claude
|
||||
|
||||
67
.htaccess
Executable file
67
.htaccess
Executable file
@ -0,0 +1,67 @@
|
||||
# Disable directory browsing
|
||||
Options -Indexes
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Rewrite engine
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Turning on the rewrite engine is necessary for the following rules and features.
|
||||
# FollowSymLinks must be enabled for this to work.
|
||||
<IfModule mod_rewrite.c>
|
||||
Options +FollowSymlinks
|
||||
RewriteEngine On
|
||||
|
||||
# If you installed CodeIgniter in a subfolder, you will need to
|
||||
# change the following line to match the subfolder you need.
|
||||
# http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase
|
||||
# RewriteBase /
|
||||
|
||||
# Redirect Trailing Slashes...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} (.+)/$
|
||||
RewriteRule ^ %1 [L,R=301]
|
||||
|
||||
# Rewrite "www.example.com -> example.com"
|
||||
RewriteCond %{HTTPS} !=on
|
||||
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
|
||||
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
|
||||
|
||||
# Checks to see if the user is attempting to access a valid file,
|
||||
# such as an image or css document, if this isn't true it sends the
|
||||
# request to the front controller, index.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^([\s\S]*)$ public/$1 [L,NC,QSA]
|
||||
|
||||
# Ensure Authorization header is passed along
|
||||
RewriteCond %{HTTP:Authorization} .
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||
|
||||
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresByType image/jpg "access 1 year"
|
||||
ExpiresByType image/jpeg "access 1 year"
|
||||
ExpiresByType image/gif "access 1 year"
|
||||
ExpiresByType image/png "access 1 year"
|
||||
ExpiresByType text/css "access 1 month"
|
||||
ExpiresByType text/js "access 1 month"
|
||||
ExpiresByType application/pdf "access 1 month"
|
||||
ExpiresByType application/javascript "access 1 month"
|
||||
ExpiresByType application/x-javascript "access 1 month"
|
||||
ExpiresByType application/x-shockwave-flash "access 1 month"
|
||||
ExpiresByType image/x-icon "access 1 year"
|
||||
ExpiresDefault "access 2 days"
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_rewrite.c>
|
||||
# If we don't have mod_rewrite installed, all 404's
|
||||
# can be sent to index.php, and everything works as normal.
|
||||
ErrorDocument 404 index.php
|
||||
</IfModule>
|
||||
|
||||
# Disable server signature start
|
||||
ServerSignature Off
|
||||
# Disable server signature end
|
||||
BIN
GPA_Endorsement_Batch_v2.0 APIGEE.pdf
Normal file
BIN
GPA_Endorsement_Batch_v2.0 APIGEE.pdf
Normal file
Binary file not shown.
22
LICENSE
Executable file
22
LICENSE
Executable file
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2019 British Columbia Institute of Technology
|
||||
Copyright (c) 2019-2023 CodeIgniter Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
BIN
Papirus-Team-Papirus-Apps-Fusion-icon.512.png
Executable file
BIN
Papirus-Team-Papirus-Apps-Fusion-icon.512.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
37
README.md
Normal file → Executable file
37
README.md
Normal file → Executable file
@ -1,2 +1,37 @@
|
||||
# Nhance
|
||||
# CodeIgniter 4 Framework
|
||||
|
||||
## Unit Testing
|
||||
|
||||
From command propmt run the following cmds
|
||||
|
||||
`php vendor/bin/phpunit tests\unit\GridType11PremiumCalculationTest.php`
|
||||
|
||||
Run speeific method
|
||||
|
||||
`php vendor/bin/phpunit tests\unit\PremiumCalculationTest.php --filter testPremiumCalculationWithPrimaryRackRateAndAdditionalRackRate`
|
||||
|
||||
Test Comments
|
||||
|
||||
## Non-EB Claims API Tests
|
||||
|
||||
Run all 4 Non-EB API tests at once:
|
||||
|
||||
`php vendor/bin/phpunit tests\unit\Api`
|
||||
|
||||
Run a single test file:
|
||||
|
||||
`php vendor/bin/phpunit tests\unit\Api\CreateClaimTest.php`
|
||||
|
||||
`php vendor/bin/phpunit tests\unit\Api\ListClaimsTest.php`
|
||||
|
||||
`php vendor/bin/phpunit tests\unit\Api\ClaimHistoryTest.php`
|
||||
|
||||
`php vendor/bin/phpunit tests\unit\Api\UploadRequiredDocTest.php`
|
||||
|
||||
Run a specific test method:
|
||||
|
||||
`php vendor/bin/phpunit tests\unit\Api\CreateClaimTest.php --filter testReturns401WhenNoAuth`
|
||||
|
||||
Run with verbose output (shows each test name):
|
||||
|
||||
`php vendor/bin/phpunit tests\unit\Api --testdox`
|
||||
6
app/.htaccess
Executable file
6
app/.htaccess
Executable file
@ -0,0 +1,6 @@
|
||||
<IfModule authz_core_module>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !authz_core_module>
|
||||
Deny from all
|
||||
</IfModule>
|
||||
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;
|
||||
}
|
||||
}
|
||||
217
app/Commands/MigrateLocalFilesToS3.php
Normal file
217
app/Commands/MigrateLocalFilesToS3.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
class MigrateLocalFilesToS3 extends BaseCommand
|
||||
{
|
||||
protected $group = 'Storage';
|
||||
protected $name = 'storage:migrate-local-to-s3';
|
||||
protected $description = 'One-time phased migration of local uploaded files to S3 using FileStorageService.';
|
||||
protected $usage = 'storage:migrate-local-to-s3 [--module writable_uploads|<folder_name>|insurer_claim_form|all] [--limit 500] [--offset 0] [--execute]';
|
||||
protected $options = [
|
||||
'--module' => 'Target module/folder (default: writable_uploads). Use writable_uploads for all writable/uploads/* folders.',
|
||||
'--limit' => 'Max files per run (default: 500).',
|
||||
'--offset' => 'Start offset for phased batches (default: 0).',
|
||||
'--execute' => 'Actually upload files. Without this flag, command runs in dry-run mode.',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private array $staticPaths = [
|
||||
'insurer_claim_form' => WRITEPATH . 'insurer_claim_form',
|
||||
];
|
||||
|
||||
public function run(array $params)
|
||||
{
|
||||
$moduleOpt = strtolower((string) (CLI::getOption('module') ?? 'writable_uploads'));
|
||||
$execute = CLI::getOption('execute') !== null;
|
||||
$dryRun = !$execute;
|
||||
$limit = max(1, (int) (CLI::getOption('limit') ?? 500));
|
||||
$offset = max(0, (int) (CLI::getOption('offset') ?? 0));
|
||||
$modulePaths = $this->buildModulePaths();
|
||||
|
||||
$selectedModules = $this->resolveModules($moduleOpt, $modulePaths);
|
||||
if ($selectedModules === []) {
|
||||
CLI::error('Invalid --module value. Allowed: writable_uploads, ' . implode(', ', array_keys($modulePaths)) . ', all');
|
||||
return;
|
||||
}
|
||||
|
||||
$storage = \Config\Services::getFileStorageService(false);
|
||||
if (!$storage->usesS3()) {
|
||||
CLI::error('FileStorageService is not in S3 mode. Set FILE_STORAGE_DRIVER=s3 before migration.');
|
||||
return;
|
||||
}
|
||||
|
||||
CLI::write($dryRun ? 'Running in DRY-RUN mode (no uploads).' : 'Running in EXECUTE mode (uploads enabled).', 'yellow');
|
||||
CLI::write('Target bucket: ' . ($storage->getBucket() ?? 'N/A'), 'cyan');
|
||||
CLI::newLine();
|
||||
|
||||
$totals = [
|
||||
'seen' => 0,
|
||||
'uploaded' => 0,
|
||||
'skipped_exists' => 0,
|
||||
'skipped_missing' => 0,
|
||||
'failed' => 0,
|
||||
];
|
||||
|
||||
foreach ($selectedModules as $module) {
|
||||
$path = $modulePaths[$module];
|
||||
CLI::write("Module: {$module}", 'green');
|
||||
CLI::write("Path: {$path}");
|
||||
|
||||
if (!is_dir($path)) {
|
||||
CLI::write(' - Skipped: folder does not exist', 'yellow');
|
||||
$totals['skipped_missing']++;
|
||||
CLI::newLine();
|
||||
continue;
|
||||
}
|
||||
|
||||
$files = $this->listFiles($path);
|
||||
$files = array_slice($files, $offset, $limit);
|
||||
CLI::write(' - Files in batch: ' . count($files));
|
||||
|
||||
foreach ($files as $fullPath) {
|
||||
$totals['seen']++;
|
||||
$fileName = basename($fullPath);
|
||||
|
||||
if (!is_file($fullPath)) {
|
||||
$totals['skipped_missing']++;
|
||||
CLI::write(" - Missing: {$fileName}", 'yellow');
|
||||
continue;
|
||||
}
|
||||
|
||||
$alreadyExists = $storage->exists($path, $fileName);
|
||||
if ($alreadyExists) {
|
||||
$totals['skipped_exists']++;
|
||||
CLI::write(" - Exists in S3, skipped: {$fileName}", 'yellow');
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($dryRun) {
|
||||
CLI::write(" - [dry-run] Would upload: {$fileName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $storage->upload($fullPath, $path, $fileName);
|
||||
if ($result['success'] ?? false) {
|
||||
$totals['uploaded']++;
|
||||
CLI::write(" - Uploaded: {$fileName}", 'green');
|
||||
} else {
|
||||
$totals['failed']++;
|
||||
CLI::write(" - Failed: {$fileName} | " . ($result['message'] ?? 'unknown error'), 'red');
|
||||
}
|
||||
}
|
||||
|
||||
CLI::newLine();
|
||||
}
|
||||
|
||||
CLI::write('Migration summary', 'cyan');
|
||||
CLI::write(' Seen: ' . $totals['seen']);
|
||||
CLI::write(' Uploaded: ' . $totals['uploaded'], $totals['uploaded'] > 0 ? 'green' : 'white');
|
||||
CLI::write(' Skipped (already in S3): ' . $totals['skipped_exists'], 'yellow');
|
||||
CLI::write(' Skipped (missing/invalid): ' . $totals['skipped_missing'], 'yellow');
|
||||
CLI::write(' Failed: ' . $totals['failed'], $totals['failed'] > 0 ? 'red' : 'white');
|
||||
|
||||
if ($dryRun) {
|
||||
CLI::newLine();
|
||||
CLI::write('Dry-run complete. Re-run with --execute to perform uploads.', 'yellow');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function resolveModules(string $moduleOpt, array $modulePaths): array
|
||||
{
|
||||
if ($moduleOpt === 'all') {
|
||||
return array_keys($modulePaths);
|
||||
}
|
||||
|
||||
if ($moduleOpt === 'writable_uploads') {
|
||||
$uploadRoot = rtrim(WRITEPATH, '/\\') . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
|
||||
$modules = [];
|
||||
foreach ($modulePaths as $module => $path) {
|
||||
$normalized = str_replace('\\', '/', $path);
|
||||
$normalizedRoot = str_replace('\\', '/', $uploadRoot);
|
||||
if (strpos($normalized, rtrim($normalizedRoot, '/')) === 0) {
|
||||
$modules[] = $module;
|
||||
}
|
||||
}
|
||||
|
||||
return $modules;
|
||||
}
|
||||
|
||||
$raw = array_filter(array_map('trim', explode(',', $moduleOpt)));
|
||||
$valid = [];
|
||||
foreach ($raw as $module) {
|
||||
if (array_key_exists($module, $modulePaths)) {
|
||||
$valid[] = $module;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique($valid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build migratable paths:
|
||||
* - all direct subfolders in writable/uploads
|
||||
* - additional static paths (outside writable/uploads)
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function buildModulePaths(): array
|
||||
{
|
||||
$paths = [];
|
||||
$uploadRoot = rtrim(WRITEPATH, '/\\') . DIRECTORY_SEPARATOR . 'uploads';
|
||||
$entries = @scandir($uploadRoot);
|
||||
|
||||
if (is_array($entries)) {
|
||||
foreach ($entries as $entry) {
|
||||
if ($entry === '.' || $entry === '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fullPath = $uploadRoot . DIRECTORY_SEPARATOR . $entry;
|
||||
if (is_dir($fullPath)) {
|
||||
$paths[strtolower($entry)] = $fullPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->staticPaths as $key => $path) {
|
||||
$paths[$key] = $path;
|
||||
}
|
||||
|
||||
ksort($paths);
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function listFiles(string $path): array
|
||||
{
|
||||
$entries = @scandir($path);
|
||||
if (!is_array($entries)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$files = [];
|
||||
foreach ($entries as $entry) {
|
||||
if ($entry === '.' || $entry === '..') {
|
||||
continue;
|
||||
}
|
||||
$fullPath = rtrim($path, '/\\') . DIRECTORY_SEPARATOR . $entry;
|
||||
if (is_file($fullPath)) {
|
||||
$files[] = $fullPath;
|
||||
}
|
||||
}
|
||||
|
||||
sort($files);
|
||||
return $files;
|
||||
}
|
||||
}
|
||||
117
app/Commands/RateLimitBlocksReconcile.php
Normal file
117
app/Commands/RateLimitBlocksReconcile.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
use App\Libraries\RateLimiterService;
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Config\Database;
|
||||
use Config\RateLimiter as RateLimiterConfig;
|
||||
|
||||
/**
|
||||
* Align rate_limit_blocks with timed cache TTL: purge stale cache keys and delete DB rows.
|
||||
*
|
||||
* Schedule (example every 10 minutes):
|
||||
* *\/10 * * * * cd /path/to/project && php spark rate-limit:reconcile-blocks
|
||||
*/
|
||||
class RateLimitBlocksReconcile extends BaseCommand
|
||||
{
|
||||
protected $group = 'Rate limit';
|
||||
|
||||
protected $name = 'rate-limit:reconcile-blocks';
|
||||
|
||||
protected $description = 'For active rows whose block TTL has passed: purge cache keys, then delete the DB row.';
|
||||
|
||||
protected $usage = 'rate-limit:reconcile-blocks [--dry-run]';
|
||||
|
||||
/** @var array<string, string> */
|
||||
protected $options = [
|
||||
'--dry-run' => 'Show which rows would be purged without deleting cache or DB.',
|
||||
];
|
||||
|
||||
public function run(array $params)
|
||||
{
|
||||
$dryRun = CLI::getOption('dry-run') !== null;
|
||||
|
||||
$db = Database::connect();
|
||||
|
||||
if (! $db->tableExists('rate_limit_blocks')) {
|
||||
CLI::write('Table rate_limit_blocks does not exist. Nothing to do.', 'yellow');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var RateLimiterConfig $rl */
|
||||
$rl = config('RateLimiter');
|
||||
$limiter = new RateLimiterService();
|
||||
|
||||
$rows = $db->table('rate_limit_blocks')
|
||||
->where('status', 'active')
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
$count = 0;
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$duration = $this->blockSecondsForRow($row, $rl);
|
||||
if ($duration <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$blockedAt = strtotime((string) $row['blocked_at']);
|
||||
if ($blockedAt === false) {
|
||||
CLI::write('Skipping id ' . $row['id'] . ': invalid blocked_at.', 'red');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (time() < $blockedAt + $duration) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$id = (int) $row['id'];
|
||||
$cacheId = (string) $row['cache_identifier'];
|
||||
$blockType = (string) $row['block_type'];
|
||||
|
||||
CLI::write(
|
||||
($dryRun ? '[dry-run] Would reconcile ' : 'Reconciling ')
|
||||
. "{$blockType} id={$id} level={$row['block_level']} display=" . $row['display_identifier'],
|
||||
'cyan'
|
||||
);
|
||||
|
||||
if (! $dryRun) {
|
||||
if ($blockType === 'ip') {
|
||||
$limiter->purgeIpBlockCaches($cacheId);
|
||||
} elseif ($blockType === 'user') {
|
||||
$limiter->purgeUserBlockCaches($cacheId);
|
||||
}
|
||||
|
||||
$db->table('rate_limit_blocks')->delete(['id' => $id], 1);
|
||||
}
|
||||
|
||||
$count++;
|
||||
}
|
||||
|
||||
CLI::write(
|
||||
$dryRun
|
||||
? "Dry run complete. {$count} row(s) would be purged and deleted."
|
||||
: "Done. Reconciled {$count} expired row(s).",
|
||||
'yellow'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
*/
|
||||
protected function blockSecondsForRow(array $row, RateLimiterConfig $cfg): int
|
||||
{
|
||||
$blockCfg = ($row['block_type'] ?? '') === 'ip' ? $cfg->ipBlock : $cfg->userBlock;
|
||||
$level = (string) ($row['block_level'] ?? '');
|
||||
|
||||
return match ($level) {
|
||||
'soft' => (int) $blockCfg['soft_duration'],
|
||||
'medium' => (int) $blockCfg['medium_duration'],
|
||||
'hard' => (int) $blockCfg['hard_duration'],
|
||||
default => 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
319
app/Commands/RecreateOrphanDumpTickets.php
Normal file
319
app/Commands/RecreateOrphanDumpTickets.php
Normal file
@ -0,0 +1,319 @@
|
||||
<?php
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
use App\Libraries\TpaClaimsImportFactory;
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* Find TPA dump rows whose ticket_id is missing/inactive in ticket_master,
|
||||
* clear the orphan link, then run Job 2 to insert tickets again.
|
||||
*
|
||||
* Usage:
|
||||
* php spark tpa:recreate-orphan-tickets
|
||||
* php spark tpa:recreate-orphan-tickets --tpa=icici
|
||||
* php spark tpa:recreate-orphan-tickets --tpa-id=6 --apply
|
||||
* php spark tpa:recreate-orphan-tickets --file-id=84 --apply
|
||||
* php spark tpa:recreate-orphan-tickets --tpa=all --apply
|
||||
*/
|
||||
class RecreateOrphanDumpTickets extends BaseCommand
|
||||
{
|
||||
protected $group = 'TPA';
|
||||
protected $name = 'tpa:recreate-orphan-tickets';
|
||||
protected $description = 'Recreate ticket_master rows for dump ticket_id orphans';
|
||||
protected $usage = 'tpa:recreate-orphan-tickets [--tpa=NAME|all] [--tpa-id=ID] [--file-id=ID] [--include-pending] [--apply]';
|
||||
protected $options = [
|
||||
'--tpa' => 'TPA key: icici|abhi|mediassist|fhpl|rcare|vidal|all (default: all)',
|
||||
'--tpa-id' => 'Numeric TPA primary key (overrides --tpa)',
|
||||
'--file-id' => 'Limit to one claim_dump_files.id',
|
||||
'--include-pending' => 'Also process dump rows with ticket_id IS NULL (never linked)',
|
||||
'--apply' => 'Clear orphan links and run Job 2 (default is dry-run list only)',
|
||||
];
|
||||
|
||||
private function tpaConfigs(): array
|
||||
{
|
||||
return [
|
||||
'icici' => ['env' => 'ICICI_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_icici'],
|
||||
'abhi' => ['env' => 'ABHI_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_abhi'],
|
||||
'mediassist' => ['env' => 'MEDI_ASSIST_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_medi_assist'],
|
||||
'fhpl' => ['env' => 'FHPL_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_fhpl'],
|
||||
'rcare' => ['env' => 'R_CARE_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_reliance'],
|
||||
'vidal' => ['env' => 'VIDAL_PRIMARY_KEY_CONSTANT', 'table' => 'claims_dump_vidal'],
|
||||
];
|
||||
}
|
||||
|
||||
public function run(array $params)
|
||||
{
|
||||
helper('utility_helper');
|
||||
|
||||
$db = db_connect();
|
||||
$apply = $this->hasFlag('apply');
|
||||
$includePending = $this->hasFlag('include-pending');
|
||||
$fileIdFilter = (int) $this->resolveOptionValue('file-id', '0');
|
||||
$configs = $this->tpaConfigs();
|
||||
$selected = $this->resolveSelectedTpas($configs);
|
||||
|
||||
if ($selected === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$summary = [];
|
||||
|
||||
foreach ($selected as $tpaKey) {
|
||||
$cfg = $configs[$tpaKey];
|
||||
$tpaId = (int) env($cfg['env']);
|
||||
$table = $cfg['table'];
|
||||
|
||||
CLI::newLine();
|
||||
CLI::write(str_repeat('=', 64), 'yellow');
|
||||
CLI::write(strtoupper($tpaKey) . " (tpa_id={$tpaId}, table={$table})", 'yellow');
|
||||
CLI::write(str_repeat('=', 64), 'yellow');
|
||||
|
||||
if (!$this->tableExists($db, $table)) {
|
||||
CLI::error("Table {$table} missing — skipped");
|
||||
$summary[$tpaKey] = ['ok' => false, 'error' => 'table missing'];
|
||||
continue;
|
||||
}
|
||||
|
||||
$orphans = $this->findOrphans($db, $table, $fileIdFilter);
|
||||
CLI::write('Orphan dump rows (ticket_id missing in ticket_master): ' . count($orphans));
|
||||
|
||||
$pending = [];
|
||||
if ($includePending) {
|
||||
$pending = $this->findPending($db, $table, $fileIdFilter);
|
||||
CLI::write('Pending dump rows (ticket_id IS NULL): ' . count($pending));
|
||||
}
|
||||
|
||||
if ($orphans === [] && $pending === []) {
|
||||
$summary[$tpaKey] = ['ok' => true, 'orphans' => 0, 'pending' => 0, 'files' => 0];
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (array_slice($orphans, 0, 20) as $row) {
|
||||
CLI::write(sprintf(
|
||||
' [orphan] dump_id=%s file_id=%s ticket_id=%s',
|
||||
$row['id'],
|
||||
$row['file_id'],
|
||||
$row['ticket_id']
|
||||
));
|
||||
}
|
||||
if (count($orphans) > 20) {
|
||||
CLI::write(' ... and ' . (count($orphans) - 20) . ' more orphans');
|
||||
}
|
||||
|
||||
foreach (array_slice($pending, 0, 10) as $row) {
|
||||
CLI::write(sprintf(
|
||||
' [pending] dump_id=%s file_id=%s',
|
||||
$row['id'],
|
||||
$row['file_id']
|
||||
));
|
||||
}
|
||||
|
||||
$fileIds = array_values(array_unique(array_map(
|
||||
'intval',
|
||||
array_merge(array_column($orphans, 'file_id'), array_column($pending, 'file_id'))
|
||||
)));
|
||||
CLI::write('Affected file_ids: ' . implode(', ', $fileIds), 'cyan');
|
||||
|
||||
if (!$apply) {
|
||||
CLI::write("Dry-run only. Apply with: php spark tpa:recreate-orphan-tickets --tpa={$tpaKey} --apply", 'yellow');
|
||||
$summary[$tpaKey] = [
|
||||
'ok' => true,
|
||||
'orphans' => count($orphans),
|
||||
'pending' => count($pending),
|
||||
'files' => count($fileIds),
|
||||
'dry_run' => true,
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
$fileResults = [];
|
||||
foreach ($fileIds as $fileId) {
|
||||
CLI::write("Processing file_id={$fileId} ...", 'light_red');
|
||||
try {
|
||||
$service = TpaClaimsImportFactory::make($tpaId);
|
||||
$result = $service->recreateOrphanDumpTickets($fileId);
|
||||
CLI::write(' result: ' . json_encode($result));
|
||||
|
||||
$stillOrphan = $this->countOrphansForFile($db, $table, $fileId);
|
||||
$linked = $db->table($table)
|
||||
->where('file_id', $fileId)
|
||||
->where('is_active', 1)
|
||||
->where('ticket_id IS NOT NULL', null, false)
|
||||
->countAllResults();
|
||||
|
||||
$ok = !empty($result['status']) && $stillOrphan === 0;
|
||||
$fileResults[] = [
|
||||
'file_id' => $fileId,
|
||||
'ok' => $ok,
|
||||
'still_orphan' => $stillOrphan,
|
||||
'linked' => $linked,
|
||||
'message' => $result['message'] ?? null,
|
||||
];
|
||||
|
||||
if (!$ok) {
|
||||
CLI::error(" file_id={$fileId} still has {$stillOrphan} orphan(s) or Job2 failed");
|
||||
} else {
|
||||
CLI::write(" file_id={$fileId} OK (linked={$linked})", 'green');
|
||||
}
|
||||
|
||||
if (!empty($result['status'])) {
|
||||
$db->table('claim_dump_files')->where('id', $fileId)->update([
|
||||
'status' => 'success',
|
||||
'reason' => null,
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
CLI::error(" file_id={$fileId} ERROR: " . $e->getMessage());
|
||||
$fileResults[] = [
|
||||
'file_id' => $fileId,
|
||||
'ok' => false,
|
||||
'error' => $e->getMessage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$allOk = !in_array(false, array_column($fileResults, 'ok'), true);
|
||||
$summary[$tpaKey] = [
|
||||
'ok' => $allOk,
|
||||
'orphans' => count($orphans),
|
||||
'pending' => count($pending),
|
||||
'files' => count($fileIds),
|
||||
'results' => $fileResults,
|
||||
];
|
||||
}
|
||||
|
||||
CLI::newLine();
|
||||
CLI::write(str_repeat('=', 64), 'cyan');
|
||||
CLI::write('SUMMARY', 'cyan');
|
||||
CLI::write(str_repeat('=', 64), 'cyan');
|
||||
foreach ($summary as $tpaKey => $row) {
|
||||
$line = sprintf(
|
||||
'%-12s %s orphans=%s pending=%s files=%s%s',
|
||||
strtoupper($tpaKey),
|
||||
!empty($row['ok']) ? 'OK' : 'FAIL',
|
||||
$row['orphans'] ?? 0,
|
||||
$row['pending'] ?? 0,
|
||||
$row['files'] ?? 0,
|
||||
!empty($row['dry_run']) ? ' (dry-run)' : (!empty($row['error']) ? ' | ' . $row['error'] : '')
|
||||
);
|
||||
CLI::write($line, !empty($row['ok']) ? 'green' : 'red');
|
||||
}
|
||||
|
||||
if (!$apply) {
|
||||
CLI::newLine();
|
||||
CLI::write('No DB changes made. Re-run with --apply to clear orphans and insert tickets.', 'yellow');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump rows with ticket_id set but no active ticket_master row.
|
||||
*/
|
||||
private function findOrphans($db, string $table, int $fileIdFilter = 0): array
|
||||
{
|
||||
$sql = "SELECT d.id, d.file_id, d.ticket_id
|
||||
FROM `{$table}` d
|
||||
LEFT JOIN ticket_master tm ON tm.id = d.ticket_id AND tm.is_active = 1
|
||||
WHERE d.is_active = 1
|
||||
AND d.ticket_id IS NOT NULL
|
||||
AND tm.id IS NULL";
|
||||
$binds = [];
|
||||
if ($fileIdFilter > 0) {
|
||||
$sql .= ' AND d.file_id = ?';
|
||||
$binds[] = $fileIdFilter;
|
||||
}
|
||||
$sql .= ' ORDER BY d.file_id ASC, d.id ASC';
|
||||
|
||||
return $db->query($sql, $binds)->getResultArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump rows never linked / rejected (eligible for Job 2 insert).
|
||||
*/
|
||||
private function findPending($db, string $table, int $fileIdFilter = 0): array
|
||||
{
|
||||
$builder = $db->table($table)
|
||||
->select('id, file_id, ticket_id')
|
||||
->where('is_active', 1)
|
||||
->where('ticket_id IS NULL', null, false)
|
||||
->where('master_reject_reason IS NULL', null, false);
|
||||
if ($fileIdFilter > 0) {
|
||||
$builder->where('file_id', $fileIdFilter);
|
||||
}
|
||||
|
||||
return $builder->orderBy('file_id', 'ASC')->orderBy('id', 'ASC')->get()->getResultArray();
|
||||
}
|
||||
|
||||
private function countOrphansForFile($db, string $table, int $fileId): int
|
||||
{
|
||||
$sql = "SELECT COUNT(*) AS cnt
|
||||
FROM `{$table}` d
|
||||
LEFT JOIN ticket_master tm ON tm.id = d.ticket_id AND tm.is_active = 1
|
||||
WHERE d.is_active = 1
|
||||
AND d.file_id = ?
|
||||
AND d.ticket_id IS NOT NULL
|
||||
AND tm.id IS NULL";
|
||||
$row = $db->query($sql, [$fileId])->getRowArray();
|
||||
|
||||
return (int) ($row['cnt'] ?? 0);
|
||||
}
|
||||
|
||||
private function resolveSelectedTpas(array $configs): array
|
||||
{
|
||||
$tpaIdOpt = (int) $this->resolveOptionValue('tpa-id', '0');
|
||||
if ($tpaIdOpt > 0) {
|
||||
foreach ($configs as $key => $cfg) {
|
||||
if ((int) env($cfg['env']) === $tpaIdOpt) {
|
||||
return [$key];
|
||||
}
|
||||
}
|
||||
CLI::error("No TPA config matches --tpa-id={$tpaIdOpt}");
|
||||
return [];
|
||||
}
|
||||
|
||||
$tpaOpt = strtolower((string) ($this->resolveOptionValue('tpa', '') ?: 'all'));
|
||||
if ($tpaOpt === '' || $tpaOpt === 'all') {
|
||||
return array_keys($configs);
|
||||
}
|
||||
if (!isset($configs[$tpaOpt])) {
|
||||
CLI::error("Unknown --tpa={$tpaOpt}. Use: " . implode('|', array_keys($configs)) . '|all');
|
||||
return [];
|
||||
}
|
||||
|
||||
return [$tpaOpt];
|
||||
}
|
||||
|
||||
private function tableExists($db, string $table): bool
|
||||
{
|
||||
return !empty($db->query('SHOW TABLES LIKE ' . $db->escape($table))->getResultArray());
|
||||
}
|
||||
|
||||
private function hasFlag(string $name): bool
|
||||
{
|
||||
if (CLI::getOption($name) !== null) {
|
||||
return true;
|
||||
}
|
||||
return in_array('--' . $name, $_SERVER['argv'] ?? [], true);
|
||||
}
|
||||
|
||||
private function resolveOptionValue(string $name, string $default): string
|
||||
{
|
||||
$opt = CLI::getOption($name);
|
||||
if (is_string($opt) && $opt !== '') {
|
||||
return $opt;
|
||||
}
|
||||
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
foreach ($argv as $i => $arg) {
|
||||
if (preg_match('/^--' . preg_quote($name, '/') . '=(.+)$/', (string) $arg, $m)) {
|
||||
return trim($m[1]);
|
||||
}
|
||||
if ($arg === '--' . $name && isset($argv[$i + 1]) && strpos((string) $argv[$i + 1], '--') !== 0) {
|
||||
return (string) $argv[$i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
361
app/Commands/StorageMergePdfSmokeTest.php
Normal file
361
app/Commands/StorageMergePdfSmokeTest.php
Normal file
@ -0,0 +1,361 @@
|
||||
<?php
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
use App\Models\ClaimFilesModel;
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use Mpdf\Mpdf;
|
||||
use Mpdf\Output\Destination;
|
||||
|
||||
/**
|
||||
* Dedicated smoke test for claim PDF merge with S3-backed claim_files.
|
||||
*
|
||||
* Usage:
|
||||
* php spark storage:smoke-test-merge-pdf
|
||||
* php spark storage:smoke-test-merge-pdf --list
|
||||
* php spark storage:smoke-test-merge-pdf --keep # leave S3/DB artifacts (debug)
|
||||
*/
|
||||
class StorageMergePdfSmokeTest extends BaseCommand
|
||||
{
|
||||
protected $group = 'Storage';
|
||||
protected $name = 'storage:smoke-test-merge-pdf';
|
||||
protected $description = 'Run dedicated merge_ticket_pdfs S3 scenarios (claim_files).';
|
||||
protected $usage = 'storage:smoke-test-merge-pdf [--list] [--keep]';
|
||||
protected $options = [
|
||||
'--list' => 'List merge scenarios without executing.',
|
||||
'--keep' => 'Skip cleanup of S3 objects and claim_files rows.',
|
||||
];
|
||||
|
||||
private int $pass = 0;
|
||||
private int $fail = 0;
|
||||
private int $skip = 0;
|
||||
|
||||
/** @var list<array{id:string,title:string}> */
|
||||
private array $catalog = [
|
||||
['id' => 'MERGE-01', 'title' => 'Create & upload 2 source PDFs to S3 (claim_files, no permanent local)'],
|
||||
['id' => 'MERGE-02', 'title' => 'Insert claim_files DB rows for synthetic ticket'],
|
||||
['id' => 'MERGE-03', 'title' => 'merge_ticket_pdfs resolves sources from S3 (temp download)'],
|
||||
['id' => 'MERGE-04', 'title' => 'Merge succeeds with pages >= 2'],
|
||||
['id' => 'MERGE-05', 'title' => 'Merged PDF exists on S3'],
|
||||
['id' => 'MERGE-06', 'title' => 'Merged permanent local path gone when S3 enabled'],
|
||||
['id' => 'MERGE-07', 'title' => 'Merged claim_files row registered (file_type=4)'],
|
||||
['id' => 'MERGE-08', 'title' => 'Claim runtime temps cleaned after merge'],
|
||||
['id' => 'MERGE-09', 'title' => 'Cleanup removes S3 objects + soft-deletes DB rows'],
|
||||
];
|
||||
|
||||
public function run(array $params)
|
||||
{
|
||||
helper(['utility', 'merge_pdf']);
|
||||
|
||||
if (CLI::getOption('list') !== null) {
|
||||
CLI::write('========== MERGE PDF SCENARIOS ==========', 'cyan');
|
||||
foreach ($this->catalog as $row) {
|
||||
CLI::write(" {$row['id']} {$row['title']}");
|
||||
}
|
||||
CLI::newLine();
|
||||
CLI::write('Run: php spark storage:smoke-test-merge-pdf');
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
$storage = \Config\Services::getFileStorageService(false);
|
||||
$keep = CLI::getOption('keep') !== null;
|
||||
|
||||
CLI::write('========== CLAIM PDF MERGE SMOKE ==========', 'cyan');
|
||||
CLI::write('usesS3: ' . ($storage->usesS3() ? 'YES' : 'NO'));
|
||||
CLI::write('Bucket: ' . ($storage->getBucket() ?? 'N/A'));
|
||||
CLI::write('RETAIN_LOCAL: ' . (storage_env_retain_local() ? 'true' : 'false'));
|
||||
CLI::newLine();
|
||||
|
||||
if (! $storage->usesS3()) {
|
||||
foreach ($this->catalog as $row) {
|
||||
$this->skip($row['id'], 'FILE_STORAGE_DRIVER must be s3');
|
||||
}
|
||||
$this->printResult();
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
$uploadDir = rtrim(WRITEPATH, '/\\') . DIRECTORY_SEPARATOR
|
||||
. 'uploads' . DIRECTORY_SEPARATOR
|
||||
. 'claim_files' . DIRECTORY_SEPARATOR;
|
||||
if (! is_dir($uploadDir)) {
|
||||
@mkdir($uploadDir, 0775, true);
|
||||
}
|
||||
|
||||
$ticketId = 990000000 + random_int(1000, 999999);
|
||||
$stamp = date('Ymd_His') . '_' . bin2hex(random_bytes(3));
|
||||
$srcNames = [
|
||||
'smoke_merge_a_' . $stamp . '.pdf',
|
||||
'smoke_merge_b_' . $stamp . '.pdf',
|
||||
];
|
||||
$sourceIds = [];
|
||||
$mergedName = null;
|
||||
$mergedId = null;
|
||||
$claimFiles = new ClaimFilesModel();
|
||||
|
||||
try {
|
||||
// MERGE-01
|
||||
$uploaded = 0;
|
||||
foreach ($srcNames as $i => $name) {
|
||||
$local = $this->makeOnePagePdf($uploadDir, $name, 'Smoke source ' . ($i + 1));
|
||||
if ($local === null || ! is_file($local)) {
|
||||
continue;
|
||||
}
|
||||
$up = $storage->upload($local, rtrim($uploadDir, '/\\'), $name);
|
||||
@unlink($local);
|
||||
if (($up['success'] ?? false) && $storage->exists(rtrim($uploadDir, '/\\'), $name) && ! is_file($uploadDir . $name)) {
|
||||
$uploaded++;
|
||||
}
|
||||
}
|
||||
$this->step('MERGE-01', $uploaded === 2, "uploaded={$uploaded}/2 ticket_id={$ticketId}");
|
||||
|
||||
if ($uploaded !== 2) {
|
||||
foreach (array_slice($this->catalog, 1) as $row) {
|
||||
$this->skip($row['id'], 'source upload failed');
|
||||
}
|
||||
$this->printResult();
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
// MERGE-02
|
||||
foreach ($srcNames as $idx => $name) {
|
||||
$id = $claimFiles->insert([
|
||||
'ticket_id' => $ticketId,
|
||||
'file_type' => 2,
|
||||
'doc_name' => 'SMOKE_MERGE_SRC_' . ($idx + 1),
|
||||
'file_name' => $name,
|
||||
'url' => $name,
|
||||
'mime_type' => 'application/pdf',
|
||||
'is_active' => 1,
|
||||
'created_by' => 1,
|
||||
]);
|
||||
if ($id) {
|
||||
$sourceIds[] = (int) $id;
|
||||
}
|
||||
}
|
||||
$this->step('MERGE-02', count($sourceIds) === 2, 'ids=' . implode(',', $sourceIds));
|
||||
|
||||
if (count($sourceIds) !== 2) {
|
||||
foreach (array_slice($this->catalog, 2) as $row) {
|
||||
$this->skip($row['id'], 'DB insert failed');
|
||||
}
|
||||
$this->cleanup($storage, $uploadDir, $srcNames, null, $ticketId, $sourceIds, $keep);
|
||||
$this->printResult();
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
// MERGE-03..07 via merge_ticket_pdfs
|
||||
$result = merge_ticket_pdfs($ticketId, [
|
||||
'replace' => true,
|
||||
'created_by' => 1,
|
||||
'include_file_types' => [1, 2],
|
||||
'include_mime_types' => ['application/pdf', 'image/jpeg', 'image/png'],
|
||||
]);
|
||||
|
||||
$this->step(
|
||||
'MERGE-03',
|
||||
(bool) ($result['status'] ?? false) && (int) ($result['source_count'] ?? 0) >= 2,
|
||||
(string) ($result['message'] ?? '') . ' sources=' . (int) ($result['source_count'] ?? 0)
|
||||
);
|
||||
|
||||
$pages = (int) ($result['pages'] ?? 0);
|
||||
$this->step(
|
||||
'MERGE-04',
|
||||
(bool) ($result['status'] ?? false) && $pages >= 2,
|
||||
'pages=' . $pages
|
||||
);
|
||||
|
||||
$mergedName = (string) ($result['file_name'] ?? '');
|
||||
$mergedId = $result['merged_file_id'] ?? null;
|
||||
$onS3 = $mergedName !== '' && $storage->exists(rtrim($uploadDir, '/\\'), $mergedName);
|
||||
$this->step('MERGE-05', $onS3, $mergedName !== '' ? $mergedName : 'no merged name');
|
||||
|
||||
$localMergedGone = $mergedName === '' || ! is_file($uploadDir . $mergedName);
|
||||
$this->step('MERGE-06', $localMergedGone, $mergedName !== '' ? basename($mergedName) : 'n/a');
|
||||
|
||||
$rowOk = false;
|
||||
if ($mergedId) {
|
||||
$row = $claimFiles->find((int) $mergedId);
|
||||
$rowOk = is_array($row)
|
||||
&& (int) ($row['file_type'] ?? 0) === MERGED_CLAIM_FILE_TYPE
|
||||
&& (int) ($row['is_active'] ?? 0) === 1
|
||||
&& (string) ($row['file_name'] ?? '') === $mergedName;
|
||||
}
|
||||
$this->step('MERGE-07', $rowOk, 'merged_file_id=' . ($mergedId ?? 'null'));
|
||||
|
||||
// MERGE-08: no leftover smoke temps for our source names in claim_files_runtime
|
||||
$afterRuntime = $this->listClaimRuntimeFiles();
|
||||
$leaked = [];
|
||||
foreach ($afterRuntime as $f) {
|
||||
foreach ($srcNames as $src) {
|
||||
if (strpos($f, $src) !== false) {
|
||||
$leaked[] = $f;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->step('MERGE-08', $leaked === [], $leaked === [] ? 'no leaks' : implode(',', $leaked));
|
||||
|
||||
// MERGE-09 cleanup
|
||||
$cleanOk = $this->cleanup(
|
||||
$storage,
|
||||
$uploadDir,
|
||||
$srcNames,
|
||||
$mergedName !== '' ? $mergedName : null,
|
||||
$ticketId,
|
||||
$sourceIds,
|
||||
$keep,
|
||||
$mergedId ? [(int) $mergedId] : []
|
||||
);
|
||||
if ($keep) {
|
||||
$this->skip('MERGE-09', '--keep set; left ticket_id=' . $ticketId);
|
||||
} else {
|
||||
$srcGone = true;
|
||||
foreach ($srcNames as $name) {
|
||||
if ($storage->exists(rtrim($uploadDir, '/\\'), $name)) {
|
||||
$srcGone = false;
|
||||
}
|
||||
}
|
||||
$mergedGone = $mergedName === '' || ! $storage->exists(rtrim($uploadDir, '/\\'), $mergedName);
|
||||
$this->step('MERGE-09', $cleanOk && $srcGone && $mergedGone, 'ticket_id=' . $ticketId);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
CLI::error('Unhandled: ' . $e->getMessage());
|
||||
$this->fail++;
|
||||
$this->cleanup($storage, $uploadDir, $srcNames, $mergedName, $ticketId, $sourceIds, $keep, $mergedId ? [(int) $mergedId] : []);
|
||||
}
|
||||
|
||||
$this->printResult();
|
||||
return $this->fail > 0 ? EXIT_ERROR : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
private function makeOnePagePdf(string $uploadDir, string $fileName, string $text): ?string
|
||||
{
|
||||
$tempDir = rtrim(WRITEPATH, '/\\') . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'mpdf';
|
||||
if (! is_dir($tempDir)) {
|
||||
@mkdir($tempDir, 0775, true);
|
||||
}
|
||||
$path = $uploadDir . $fileName;
|
||||
try {
|
||||
$mpdf = new Mpdf(['tempDir' => $tempDir, 'mode' => 'utf-8']);
|
||||
$mpdf->WriteHTML('<h1>' . htmlspecialchars($text, ENT_QUOTES, 'UTF-8') . '</h1><p>storage merge smoke</p>');
|
||||
$mpdf->Output($path, Destination::FILE);
|
||||
return is_file($path) ? $path : null;
|
||||
} catch (\Throwable $e) {
|
||||
CLI::error('PDF create failed: ' . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
private function listClaimRuntimeFiles(): array
|
||||
{
|
||||
$dir = rtrim(WRITEPATH, '/\\') . DIRECTORY_SEPARATOR
|
||||
. 'cache' . DIRECTORY_SEPARATOR
|
||||
. 'claim_files_runtime' . DIRECTORY_SEPARATOR;
|
||||
if (! is_dir($dir)) {
|
||||
return [];
|
||||
}
|
||||
$out = [];
|
||||
foreach (scandir($dir) ?: [] as $f) {
|
||||
if ($f === '.' || $f === '..') {
|
||||
continue;
|
||||
}
|
||||
if (is_file($dir . $f)) {
|
||||
$out[] = $f;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $srcNames
|
||||
* @param list<int> $sourceIds
|
||||
* @param list<int> $extraIds
|
||||
*/
|
||||
private function cleanup(
|
||||
$storage,
|
||||
string $uploadDir,
|
||||
array $srcNames,
|
||||
?string $mergedName,
|
||||
int $ticketId,
|
||||
array $sourceIds,
|
||||
bool $keep,
|
||||
array $extraIds = []
|
||||
): bool {
|
||||
if ($keep) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$ok = true;
|
||||
$dir = rtrim($uploadDir, '/\\');
|
||||
foreach ($srcNames as $name) {
|
||||
if ($storage->exists($dir, $name)) {
|
||||
$del = $storage->delete($dir, $name);
|
||||
if (! ($del['success'] ?? false)) {
|
||||
$ok = false;
|
||||
}
|
||||
}
|
||||
@unlink($uploadDir . $name);
|
||||
}
|
||||
if ($mergedName) {
|
||||
if ($storage->exists($dir, $mergedName)) {
|
||||
$del = $storage->delete($dir, $mergedName);
|
||||
if (! ($del['success'] ?? false)) {
|
||||
$ok = false;
|
||||
}
|
||||
}
|
||||
@unlink($uploadDir . $mergedName);
|
||||
}
|
||||
|
||||
try {
|
||||
$claimFiles = new ClaimFilesModel();
|
||||
$ids = array_values(array_unique(array_merge($sourceIds, $extraIds)));
|
||||
if ($ids !== []) {
|
||||
$claimFiles->whereIn('id', $ids)->set(['is_active' => 0])->update();
|
||||
}
|
||||
$claimFiles->where('ticket_id', $ticketId)->set(['is_active' => 0])->update();
|
||||
} catch (\Throwable $e) {
|
||||
CLI::write('DB cleanup warning: ' . $e->getMessage(), 'yellow');
|
||||
$ok = false;
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
private function step(string $id, bool $ok, string $detail = ''): void
|
||||
{
|
||||
$title = $this->titleFor($id);
|
||||
if ($ok) {
|
||||
$this->pass++;
|
||||
CLI::write("PASS {$id} {$title}" . ($detail !== '' ? " — {$detail}" : ''), 'green');
|
||||
} else {
|
||||
$this->fail++;
|
||||
CLI::write("FAIL {$id} {$title}" . ($detail !== '' ? " — {$detail}" : ''), 'red');
|
||||
}
|
||||
}
|
||||
|
||||
private function skip(string $id, string $reason): void
|
||||
{
|
||||
$this->skip++;
|
||||
CLI::write("SKIP {$id} {$this->titleFor($id)} — {$reason}", 'yellow');
|
||||
}
|
||||
|
||||
private function titleFor(string $id): string
|
||||
{
|
||||
foreach ($this->catalog as $row) {
|
||||
if ($row['id'] === $id) {
|
||||
return $row['title'];
|
||||
}
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
private function printResult(): void
|
||||
{
|
||||
CLI::newLine();
|
||||
CLI::write(
|
||||
sprintf('MERGE RESULT: %d passed | %d failed | %d skipped', $this->pass, $this->fail, $this->skip),
|
||||
$this->fail === 0 ? 'green' : 'red'
|
||||
);
|
||||
}
|
||||
}
|
||||
189
app/Commands/StoragePurgeS3.php
Normal file
189
app/Commands/StoragePurgeS3.php
Normal file
@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* Purge objects under an S3 prefix that matches a storage module folder
|
||||
* (e.g. claim_files → keys claim_files/* in AWS_FILE_UPLOAD_BUCKET).
|
||||
*
|
||||
* Usage:
|
||||
* php spark storage:purge-s3 --module claim_files
|
||||
* php spark storage:purge-s3 --module claim_files --execute
|
||||
* php spark storage:purge-s3 --module claim_files,excel --limit 100 --execute
|
||||
*/
|
||||
class StoragePurgeS3 extends BaseCommand
|
||||
{
|
||||
protected $group = 'Storage';
|
||||
protected $name = 'storage:purge-s3';
|
||||
protected $description = 'List/delete S3 objects under a module prefix (dry-run by default).';
|
||||
protected $usage = 'storage:purge-s3 --module <folder>[,folder...] [--limit 0] [--offset 0] [--execute] [--list-modules]';
|
||||
protected $options = [
|
||||
'--module' => 'Required. Module folder name(s), comma-separated (e.g. claim_files).',
|
||||
'--limit' => 'Max objects to delete per module (0 = all, default: 0).',
|
||||
'--offset' => 'Skip first N keys per module (default: 0).',
|
||||
'--execute' => 'Actually delete. Without this flag, dry-run only.',
|
||||
'--list-modules' => 'List known local upload module folder names and exit.',
|
||||
];
|
||||
|
||||
public function run(array $params)
|
||||
{
|
||||
if (CLI::getOption('list-modules') !== null) {
|
||||
$this->printKnownModules();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
$moduleOpt = trim((string) (CLI::getOption('module') ?? ($params[0] ?? '')));
|
||||
if ($moduleOpt === '') {
|
||||
CLI::error('Missing --module. Example: php spark storage:purge-s3 --module claim_files');
|
||||
CLI::write('Tip: php spark storage:purge-s3 --list-modules');
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
$execute = CLI::getOption('execute') !== null;
|
||||
$dryRun = ! $execute;
|
||||
$limit = max(0, (int) (CLI::getOption('limit') ?? 0));
|
||||
$offset = max(0, (int) (CLI::getOption('offset') ?? 0));
|
||||
|
||||
$modules = $this->parseModules($moduleOpt);
|
||||
if ($modules === []) {
|
||||
CLI::error('No valid module names after parsing. Use basename-only folder names (no path).');
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
$storage = \Config\Services::getFileStorageService(false);
|
||||
if (! $storage->usesS3()) {
|
||||
CLI::error('FileStorageService is not in S3 mode. Set FILE_STORAGE_DRIVER=s3 and AWS_FILE_UPLOAD_BUCKET.');
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
$s3 = \Config\Services::getS3Service(false);
|
||||
$bucket = $storage->getBucket();
|
||||
|
||||
CLI::write($dryRun ? 'Running in DRY-RUN mode (no deletes).' : 'Running in EXECUTE mode (deletes enabled).', 'yellow');
|
||||
CLI::write('Bucket: ' . ($bucket ?? 'N/A'), 'cyan');
|
||||
CLI::write('Modules: ' . implode(', ', $modules));
|
||||
CLI::newLine();
|
||||
|
||||
$totals = [
|
||||
'listed' => 0,
|
||||
'deleted' => 0,
|
||||
'failed' => 0,
|
||||
];
|
||||
|
||||
foreach ($modules as $module) {
|
||||
$prefix = $module . '/';
|
||||
CLI::write("Module: {$module}", 'green');
|
||||
CLI::write(" Prefix: {$prefix}");
|
||||
|
||||
$listed = $s3->listFiles($prefix, $bucket);
|
||||
if (! ($listed['success'] ?? false)) {
|
||||
CLI::error(' List failed: ' . ($listed['message'] ?? 'unknown'));
|
||||
$totals['failed']++;
|
||||
CLI::newLine();
|
||||
continue;
|
||||
}
|
||||
|
||||
$keys = [];
|
||||
foreach ($listed['files'] ?? [] as $file) {
|
||||
$key = (string) ($file['key'] ?? '');
|
||||
if ($key === '' || substr($key, -1) === '/') {
|
||||
continue; // skip empty / folder markers
|
||||
}
|
||||
$keys[] = $key;
|
||||
}
|
||||
sort($keys);
|
||||
|
||||
$batch = array_slice($keys, $offset, $limit > 0 ? $limit : null);
|
||||
CLI::write(' Objects under prefix: ' . count($keys));
|
||||
CLI::write(' In this batch: ' . count($batch));
|
||||
|
||||
if ($batch === []) {
|
||||
CLI::write(' Nothing to purge.', 'yellow');
|
||||
CLI::newLine();
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($batch as $key) {
|
||||
$totals['listed']++;
|
||||
if ($dryRun) {
|
||||
CLI::write(" - [dry-run] Would delete: {$key}");
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $s3->delete($key, $bucket);
|
||||
if ($result['success'] ?? false) {
|
||||
$totals['deleted']++;
|
||||
CLI::write(" - Deleted: {$key}", 'green');
|
||||
} else {
|
||||
$totals['failed']++;
|
||||
CLI::write(' - Failed: ' . $key . ' | ' . ($result['message'] ?? ''), 'red');
|
||||
}
|
||||
}
|
||||
|
||||
CLI::newLine();
|
||||
}
|
||||
|
||||
CLI::write('Purge summary', 'cyan');
|
||||
CLI::write(' Listed (batch): ' . $totals['listed']);
|
||||
CLI::write(' Deleted: ' . $totals['deleted'], $totals['deleted'] > 0 ? 'green' : 'white');
|
||||
CLI::write(' Failed: ' . $totals['failed'], $totals['failed'] > 0 ? 'red' : 'white');
|
||||
|
||||
if ($dryRun) {
|
||||
CLI::newLine();
|
||||
CLI::write('Dry-run complete. Re-run with --execute to delete these objects.', 'yellow');
|
||||
}
|
||||
|
||||
return $totals['failed'] > 0 ? EXIT_ERROR : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
private function parseModules(string $moduleOpt): array
|
||||
{
|
||||
$raw = array_filter(array_map('trim', explode(',', strtolower($moduleOpt))));
|
||||
$out = [];
|
||||
foreach ($raw as $module) {
|
||||
$module = basename(str_replace('\\', '/', $module));
|
||||
if ($module === '' || $module === '.' || $module === '..') {
|
||||
continue;
|
||||
}
|
||||
if (strpos($module, '/') !== false || preg_match('/[^a-z0-9_\-]/i', $module)) {
|
||||
CLI::write(" Skipping invalid module name: {$module}", 'yellow');
|
||||
continue;
|
||||
}
|
||||
$out[] = $module;
|
||||
}
|
||||
|
||||
return array_values(array_unique($out));
|
||||
}
|
||||
|
||||
private function printKnownModules(): void
|
||||
{
|
||||
$uploadRoot = rtrim(WRITEPATH, '/\\') . DIRECTORY_SEPARATOR . 'uploads';
|
||||
CLI::write('Known local upload folders (S3 prefix = folder name):', 'cyan');
|
||||
$entries = @scandir($uploadRoot);
|
||||
if (! is_array($entries)) {
|
||||
CLI::write(' (uploads root missing)');
|
||||
return;
|
||||
}
|
||||
$names = [];
|
||||
foreach ($entries as $entry) {
|
||||
if ($entry === '.' || $entry === '..') {
|
||||
continue;
|
||||
}
|
||||
if (is_dir($uploadRoot . DIRECTORY_SEPARATOR . $entry)) {
|
||||
$names[] = $entry;
|
||||
}
|
||||
}
|
||||
sort($names);
|
||||
foreach ($names as $name) {
|
||||
CLI::write(' ' . $name);
|
||||
}
|
||||
CLI::newLine();
|
||||
CLI::write('Example: php spark storage:purge-s3 --module claim_files');
|
||||
}
|
||||
}
|
||||
514
app/Commands/StorageSmokeTest.php
Normal file
514
app/Commands/StorageSmokeTest.php
Normal file
@ -0,0 +1,514 @@
|
||||
<?php
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* Runs ALL automatable S3/storage scenarios in one command.
|
||||
*
|
||||
* Usage:
|
||||
* php spark storage:smoke-test
|
||||
* php spark storage:smoke-test --module excel
|
||||
* php spark storage:smoke-test --list # list all scenarios without running
|
||||
*/
|
||||
class StorageSmokeTest extends BaseCommand
|
||||
{
|
||||
protected $group = 'Storage';
|
||||
protected $name = 'storage:smoke-test';
|
||||
protected $description = 'Run every automatable storage scenario (all modules) and list remaining manual ones.';
|
||||
protected $usage = 'storage:smoke-test [--module all|excel|claim_files|...] [--list]';
|
||||
protected $options = [
|
||||
'--module' => 'One folder name, or all (default: all wired folders).',
|
||||
'--list' => 'List every scenario ID without executing.',
|
||||
];
|
||||
|
||||
private int $pass = 0;
|
||||
private int $fail = 0;
|
||||
private int $skip = 0;
|
||||
|
||||
/** @var list<array{id:string,title:string,group:string}> */
|
||||
private array $catalog = [];
|
||||
|
||||
/** @var list<string> */
|
||||
private array $wiredModules = [
|
||||
'claim_files',
|
||||
'client_kyc_documents',
|
||||
'excel',
|
||||
'import_excel',
|
||||
'lead_files',
|
||||
'attachments',
|
||||
'claim_dump_excel',
|
||||
'claims_mis',
|
||||
'claims_dump',
|
||||
'statements',
|
||||
'bds_dump_excel',
|
||||
'hr_files',
|
||||
'non_eb_asset_files',
|
||||
'non_eb_rack_rate',
|
||||
];
|
||||
|
||||
public function run(array $params)
|
||||
{
|
||||
helper('utility');
|
||||
$this->buildCatalog();
|
||||
|
||||
if (CLI::getOption('list') !== null) {
|
||||
$this->printCatalog();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
$storage = \Config\Services::getFileStorageService(false);
|
||||
$moduleOpt = strtolower((string) (CLI::getOption('module') ?? 'all'));
|
||||
$modules = $moduleOpt === 'all' || $moduleOpt === ''
|
||||
? $this->wiredModules
|
||||
: [basename($moduleOpt)];
|
||||
|
||||
CLI::write('========== STORAGE FULL SCENARIO RUN ==========', 'cyan');
|
||||
CLI::write('usesS3: ' . ($storage->usesS3() ? 'YES' : 'NO'));
|
||||
CLI::write('Bucket: ' . ($storage->getBucket() ?? 'N/A'));
|
||||
CLI::write('RETAIN_LOCAL: ' . (storage_env_retain_local() ? 'true' : 'false'));
|
||||
CLI::write('Modules: ' . implode(', ', $modules));
|
||||
CLI::write('Total catalog scenarios: ' . count($this->catalog));
|
||||
CLI::newLine();
|
||||
|
||||
// ENV / helper checks
|
||||
$this->runEnvScenarios($storage);
|
||||
|
||||
// Per-module full lifecycle for every wired folder
|
||||
foreach ($modules as $mod) {
|
||||
$this->runModuleLifecycle($storage, $mod);
|
||||
}
|
||||
|
||||
// Cross-cutting claim / mail / mirror / soft-delete
|
||||
$this->runClaimScenarios($storage);
|
||||
$this->runMailScenarios($storage);
|
||||
$this->runMirrorScenario($storage);
|
||||
$this->runRetainLocalSimulation($storage);
|
||||
$this->runClaimsDumpResolve($storage);
|
||||
|
||||
CLI::newLine();
|
||||
CLI::write(
|
||||
sprintf('RESULT: %d passed | %d failed | %d skipped', $this->pass, $this->fail, $this->skip),
|
||||
$this->fail === 0 ? 'green' : 'red'
|
||||
);
|
||||
|
||||
CLI::newLine();
|
||||
$this->printManualOnlyRemaining();
|
||||
|
||||
return $this->fail > 0 ? EXIT_ERROR : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
private function buildCatalog(): void
|
||||
{
|
||||
$this->catalog = [
|
||||
['id' => 'ENV-01', 'group' => 'ENV', 'title' => 'FILE_STORAGE / bucket / RETAIN_LOCAL readable'],
|
||||
['id' => 'ENV-02', 'group' => 'ENV', 'title' => 'Runtime temp dirs writable'],
|
||||
['id' => 'ENV-03', 'group' => 'ENV', 'title' => 'usesS3 matches FILE_STORAGE_DRIVER'],
|
||||
];
|
||||
|
||||
foreach ($this->wiredModules as $mod) {
|
||||
$this->catalog[] = ['id' => "MOD-{$mod}-01", 'group' => "MODULE:{$mod}", 'title' => 'Upload to S3/local'];
|
||||
$this->catalog[] = ['id' => "MOD-{$mod}-02", 'group' => "MODULE:{$mod}", 'title' => 'Exists after upload'];
|
||||
$this->catalog[] = ['id' => "MOD-{$mod}-03", 'group' => "MODULE:{$mod}", 'title' => 'Download content'];
|
||||
$this->catalog[] = ['id' => "MOD-{$mod}-04", 'group' => "MODULE:{$mod}", 'title' => 'ensure_local from storage'];
|
||||
$this->catalog[] = ['id' => "MOD-{$mod}-05", 'group' => "MODULE:{$mod}", 'title' => 'No permanent local when RETAIN_LOCAL=false (S3)'];
|
||||
$this->catalog[] = ['id' => "MOD-{$mod}-06", 'group' => "MODULE:{$mod}", 'title' => 'Delete removes object'];
|
||||
$this->catalog[] = ['id' => "MOD-{$mod}-07", 'group' => "MODULE:{$mod}", 'title' => 'Presigned URL (S3 only)'];
|
||||
}
|
||||
|
||||
$extra = [
|
||||
['id' => 'CLAIM-01', 'group' => 'CLAIM', 'title' => 'resolve_claim_file_path'],
|
||||
['id' => 'CLAIM-02', 'group' => 'CLAIM', 'title' => 'claim_file_download_url (TPA)'],
|
||||
['id' => 'CLAIM-03', 'group' => 'CLAIM', 'title' => 'soft_delete cleans S3 when unused (DB)'],
|
||||
['id' => 'MERGE-PDF', 'group' => 'CLAIM', 'title' => 'Dedicated PDF merge suite — php spark storage:smoke-test-merge-pdf'],
|
||||
['id' => 'MAIL-01', 'group' => 'MAIL', 'title' => 'resolve_mail_attachment_path'],
|
||||
['id' => 'GEN-01', 'group' => 'GENERATED', 'title' => 'mirror_generated_file then local gone'],
|
||||
['id' => 'RETAIN-01', 'group' => 'RETAIN_LOCAL', 'title' => 'Simulate retain=false (S3-only)'],
|
||||
['id' => 'RETAIN-02', 'group' => 'RETAIN_LOCAL', 'title' => 'Simulate retain=true (dual-write keep local)'],
|
||||
['id' => 'DUMP-01', 'group' => 'CLAIMS_DUMP', 'title' => 'resolveDumpLocalPath / ensure_local'],
|
||||
];
|
||||
foreach ($extra as $row) {
|
||||
$this->catalog[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
private function printCatalog(): void
|
||||
{
|
||||
CLI::write('========== ALL SCENARIO TEST CASES ==========', 'cyan');
|
||||
$current = '';
|
||||
foreach ($this->catalog as $row) {
|
||||
if ($row['group'] !== $current) {
|
||||
$current = $row['group'];
|
||||
CLI::newLine();
|
||||
CLI::write("--- {$current} ---", 'yellow');
|
||||
}
|
||||
CLI::write(" {$row['id']} {$row['title']}");
|
||||
}
|
||||
CLI::newLine();
|
||||
CLI::write('Manual-only (UI/TPA jobs — run by human after automated):', 'yellow');
|
||||
foreach ($this->manualScenarioLines() as $line) {
|
||||
CLI::write(' ' . $line);
|
||||
}
|
||||
CLI::newLine();
|
||||
CLI::write('Run all automated: php spark storage:smoke-test');
|
||||
}
|
||||
|
||||
private function step(string $id, bool $ok, string $detail = ''): void
|
||||
{
|
||||
$title = $this->titleFor($id);
|
||||
if ($ok) {
|
||||
$this->pass++;
|
||||
CLI::write("[PASS] {$id} {$title}" . ($detail !== '' ? " — {$detail}" : ''), 'green');
|
||||
} else {
|
||||
$this->fail++;
|
||||
CLI::write("[FAIL] {$id} {$title}" . ($detail !== '' ? " — {$detail}" : ''), 'red');
|
||||
}
|
||||
}
|
||||
|
||||
private function skip(string $id, string $reason): void
|
||||
{
|
||||
$this->skip++;
|
||||
CLI::write("[SKIP] {$id} {$this->titleFor($id)} — {$reason}", 'yellow');
|
||||
}
|
||||
|
||||
private function titleFor(string $id): string
|
||||
{
|
||||
foreach ($this->catalog as $row) {
|
||||
if ($row['id'] === $id) {
|
||||
return $row['title'];
|
||||
}
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
private function uploadPath(string $mod): string
|
||||
{
|
||||
return rtrim(WRITEPATH, '/\\') . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . $mod;
|
||||
}
|
||||
|
||||
private function makeTmp(string $name, string $body): string
|
||||
{
|
||||
$dir = storage_runtime_temp_dir();
|
||||
if (! is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
}
|
||||
$path = $dir . DIRECTORY_SEPARATOR . $name;
|
||||
file_put_contents($path, $body);
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
private function runEnvScenarios($storage): void
|
||||
{
|
||||
CLI::write('--- ENV ---', 'white');
|
||||
$driver = strtolower((string) (getenv('FILE_STORAGE_DRIVER') ?: env('FILE_STORAGE_DRIVER') ?: ''));
|
||||
$bucket = (string) (getenv('AWS_FILE_UPLOAD_BUCKET') ?: getenv('AWS_BUCKET') ?: '');
|
||||
$this->step('ENV-01', true, "driver=" . ($driver !== '' ? $driver : 'default') . ", retain=" . (storage_env_retain_local() ? '1' : '0') . ", bucket=" . ($bucket !== '' ? 'set' : 'empty'));
|
||||
|
||||
$runtime = storage_runtime_temp_dir();
|
||||
$claimRt = rtrim(WRITEPATH, '/\\') . '/cache/claim_files_runtime';
|
||||
foreach ([$runtime, $claimRt] as $d) {
|
||||
if (! is_dir($d)) {
|
||||
@mkdir($d, 0755, true);
|
||||
}
|
||||
}
|
||||
$probe = $runtime . DIRECTORY_SEPARATOR . 'probe_' . uniqid('', true) . '.txt';
|
||||
$wrote = @file_put_contents($probe, 'ok') !== false;
|
||||
if ($wrote) {
|
||||
@unlink($probe);
|
||||
}
|
||||
$this->step('ENV-02', $wrote && is_dir($runtime));
|
||||
|
||||
if ($driver === 'local') {
|
||||
$this->step('ENV-03', ! $storage->usesS3(), 'local driver => usesS3 false');
|
||||
} elseif ($driver === 's3' || $driver === '') {
|
||||
$this->step('ENV-03', $storage->usesS3(), 's3 driver => usesS3 true (bucket required)');
|
||||
} else {
|
||||
$this->skip('ENV-03', 'unknown FILE_STORAGE_DRIVER=' . $driver);
|
||||
}
|
||||
}
|
||||
|
||||
private function runModuleLifecycle($storage, string $mod): void
|
||||
{
|
||||
CLI::write("--- MODULE: {$mod} ---", 'white');
|
||||
$path = $this->uploadPath($mod);
|
||||
$fileName = 'smoke_' . $mod . '_' . date('Ymd_His') . '_' . bin2hex(random_bytes(2)) . '.txt';
|
||||
$tmp = $this->makeTmp($fileName, "smoke-{$mod}\n" . date('c') . "\n");
|
||||
|
||||
$up = $storage->upload($tmp, $path, $fileName);
|
||||
@unlink($tmp);
|
||||
$this->step("MOD-{$mod}-01", (bool) ($up['success'] ?? false), (string) ($up['message'] ?? ''));
|
||||
if (! ($up['success'] ?? false)) {
|
||||
// Skip rest of module if upload failed
|
||||
foreach (['02', '03', '04', '05', '06', '07'] as $n) {
|
||||
$this->skip("MOD-{$mod}-{$n}", 'upload failed');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$this->step("MOD-{$mod}-02", $storage->exists($path, $fileName));
|
||||
|
||||
$dl = $storage->download($path, $fileName);
|
||||
$contentOk = ($dl['success'] ?? false) && (
|
||||
((! empty($dl['content'])) && str_contains((string) $dl['content'], "smoke-{$mod}"))
|
||||
|| ((! empty($dl['path'])) && is_file((string) $dl['path'])
|
||||
&& str_contains((string) file_get_contents((string) $dl['path']), "smoke-{$mod}"))
|
||||
);
|
||||
$this->step("MOD-{$mod}-03", $contentOk, (string) ($dl['message'] ?? ''));
|
||||
|
||||
// Force missing permanent local then ensure_local
|
||||
$permanent = $storage->resolveLocalPath($path, $fileName);
|
||||
if (is_file($permanent)) {
|
||||
@unlink($permanent);
|
||||
}
|
||||
$ensured = storage_ensure_local_file($path, $fileName);
|
||||
$this->step(
|
||||
"MOD-{$mod}-04",
|
||||
is_string($ensured) && is_file($ensured) && str_contains((string) file_get_contents($ensured), "smoke-{$mod}"),
|
||||
$ensured ? basename($ensured) : 'null'
|
||||
);
|
||||
if ($ensured) {
|
||||
storage_cleanup_temp_file($ensured);
|
||||
}
|
||||
|
||||
if ($storage->usesS3() && ! storage_env_retain_local()) {
|
||||
$this->step("MOD-{$mod}-05", ! is_file($permanent), 'permanent path absent');
|
||||
} else {
|
||||
$this->skip("MOD-{$mod}-05", $storage->usesS3() ? 'RETAIN_LOCAL=true' : 'local driver');
|
||||
}
|
||||
|
||||
$del = $storage->delete($path, $fileName);
|
||||
$this->step("MOD-{$mod}-06", (bool) ($del['success'] ?? false) && ! $storage->exists($path, $fileName));
|
||||
|
||||
if ($storage->usesS3()) {
|
||||
$tmp = $this->makeTmp($fileName, "presign-{$mod}\n");
|
||||
$storage->upload($tmp, $path, $fileName);
|
||||
@unlink($tmp);
|
||||
$pre = $storage->getPresignedUrl($path, $fileName, 5);
|
||||
$this->step("MOD-{$mod}-07", (bool) ($pre['success'] ?? false) && ! empty($pre['url']));
|
||||
$storage->delete($path, $fileName);
|
||||
} else {
|
||||
$this->skip("MOD-{$mod}-07", 'local driver');
|
||||
}
|
||||
}
|
||||
|
||||
private function runClaimScenarios($storage): void
|
||||
{
|
||||
CLI::write('--- CLAIM ---', 'white');
|
||||
$claimDir = $this->uploadPath('claim_files');
|
||||
$fileName = 'smoke_claim_' . date('Ymd_His') . '_' . bin2hex(random_bytes(2)) . '.pdf';
|
||||
$tmp = $this->makeTmp($fileName, "%PDF-1.4 smoke-claim\n");
|
||||
$up = $storage->upload($tmp, $claimDir, $fileName);
|
||||
@unlink($tmp);
|
||||
if (! ($up['success'] ?? false)) {
|
||||
$this->skip('CLAIM-01', 'upload claim sample failed');
|
||||
$this->skip('CLAIM-02', 'upload claim sample failed');
|
||||
$this->skip('CLAIM-03', 'upload claim sample failed');
|
||||
return;
|
||||
}
|
||||
// Ensure no permanent leftover interferes
|
||||
$permanent = $storage->resolveLocalPath($claimDir, $fileName);
|
||||
if (is_file($permanent)) {
|
||||
@unlink($permanent);
|
||||
}
|
||||
|
||||
$resolved = storage_resolve_claim_file_path($claimDir, $fileName);
|
||||
$this->step(
|
||||
'CLAIM-01',
|
||||
(bool) ($resolved['success'] ?? false) && ! empty($resolved['path']) && is_file((string) $resolved['path']),
|
||||
(string) ($resolved['message'] ?? '')
|
||||
);
|
||||
storage_cleanup_temp_claim_file($resolved);
|
||||
|
||||
if ($storage->usesS3()) {
|
||||
$url = storage_claim_file_download_url($fileName, null, 10);
|
||||
$this->step('CLAIM-02', is_string($url) && str_starts_with($url, 'http'));
|
||||
} else {
|
||||
$this->skip('CLAIM-02', 'local driver');
|
||||
}
|
||||
|
||||
// Soft-delete: insert temp claim_files row, soft delete, expect S3 gone
|
||||
try {
|
||||
$db = \Config\Database::connect();
|
||||
$insert = [
|
||||
'ticket_master_id' => 0,
|
||||
'file_name' => $fileName,
|
||||
'url' => $fileName,
|
||||
'doc_name' => 'smoke-test',
|
||||
'file_type' => 2,
|
||||
'is_active' => 1,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
// Only use columns that exist
|
||||
$fields = $db->getFieldNames('claim_files');
|
||||
$row = [];
|
||||
foreach ($insert as $k => $v) {
|
||||
if (in_array($k, $fields, true)) {
|
||||
$row[$k] = $v;
|
||||
}
|
||||
}
|
||||
$db->table('claim_files')->insert($row);
|
||||
$id = (int) $db->insertID();
|
||||
$ok = $id > 0 && storage_soft_delete_claim_file($id);
|
||||
$gone = ! $storage->exists($claimDir, $fileName);
|
||||
$this->step('CLAIM-03', $ok && $gone, 'id=' . $id);
|
||||
// Hard cleanup leftover row
|
||||
if ($id > 0) {
|
||||
$db->table('claim_files')->where('id', $id)->delete();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->skip('CLAIM-03', 'DB soft-delete unavailable: ' . $e->getMessage());
|
||||
$storage->delete($claimDir, $fileName);
|
||||
}
|
||||
}
|
||||
|
||||
private function runMailScenarios($storage): void
|
||||
{
|
||||
CLI::write('--- MAIL ---', 'white');
|
||||
$dir = $this->uploadPath('attachments');
|
||||
$fileName = 'smoke_mail_' . date('Ymd_His') . '_' . bin2hex(random_bytes(2)) . '.txt';
|
||||
$tmp = $this->makeTmp($fileName, "mail\n");
|
||||
$up = $storage->upload($tmp, $dir, $fileName);
|
||||
@unlink($tmp);
|
||||
if (! ($up['success'] ?? false)) {
|
||||
$this->step('MAIL-01', false, (string) ($up['message'] ?? ''));
|
||||
return;
|
||||
}
|
||||
$permanent = $storage->resolveLocalPath($dir, $fileName);
|
||||
if (is_file($permanent)) {
|
||||
@unlink($permanent);
|
||||
}
|
||||
$resolved = storage_resolve_mail_attachment_path('uploads/attachments/' . $fileName, $fileName);
|
||||
$this->step(
|
||||
'MAIL-01',
|
||||
(bool) ($resolved['success'] ?? false) && ! empty($resolved['path']) && is_file((string) $resolved['path'])
|
||||
);
|
||||
if (! empty($resolved['path'])) {
|
||||
storage_cleanup_temp_file((string) $resolved['path']);
|
||||
}
|
||||
$storage->delete($dir, $fileName);
|
||||
}
|
||||
|
||||
private function runMirrorScenario($storage): void
|
||||
{
|
||||
CLI::write('--- GENERATED ---', 'white');
|
||||
$dir = $this->uploadPath('excel');
|
||||
$fileName = 'smoke_gen_' . date('Ymd_His') . '_' . bin2hex(random_bytes(2)) . '.txt';
|
||||
$local = $this->makeTmp($fileName, "generated\n");
|
||||
$ok = storage_mirror_generated_file($local, $dir, $fileName);
|
||||
if ($storage->usesS3()) {
|
||||
$this->step('GEN-01', $ok && ! is_file($local) && $storage->exists($dir, $fileName));
|
||||
$storage->delete($dir, $fileName);
|
||||
} else {
|
||||
$this->step('GEN-01', $ok && is_file($local));
|
||||
@unlink($local);
|
||||
}
|
||||
}
|
||||
|
||||
private function runRetainLocalSimulation($storage): void
|
||||
{
|
||||
CLI::write('--- RETAIN_LOCAL simulation ---', 'white');
|
||||
if (! $storage->usesS3()) {
|
||||
$this->skip('RETAIN-01', 'local driver');
|
||||
$this->skip('RETAIN-02', 'local driver');
|
||||
return;
|
||||
}
|
||||
|
||||
$dir = $this->uploadPath('excel');
|
||||
|
||||
// retain=false behaviour: upload via temp then unlink (current default path)
|
||||
$f1 = 'smoke_retain_off_' . bin2hex(random_bytes(2)) . '.txt';
|
||||
$t1 = $this->makeTmp($f1, "off\n");
|
||||
$storage->upload($t1, $dir, $f1);
|
||||
@unlink($t1);
|
||||
$p1 = $storage->resolveLocalPath($dir, $f1);
|
||||
$this->step('RETAIN-01', ! is_file($p1) && $storage->exists($dir, $f1));
|
||||
$storage->delete($dir, $f1);
|
||||
|
||||
// retain=true behaviour: keep local file after S3 upload
|
||||
$f2 = 'smoke_retain_on_' . bin2hex(random_bytes(2)) . '.txt';
|
||||
// Use runtime path as the "kept local" copy (CLI may lack write perms under uploads/)
|
||||
$p2 = $this->makeTmp($f2, "on\n");
|
||||
$up2 = $storage->upload($p2, $dir, $f2);
|
||||
$this->step(
|
||||
'RETAIN-02',
|
||||
(bool) ($up2['success'] ?? false) && is_file($p2) && $storage->exists($dir, $f2),
|
||||
'local kept + S3'
|
||||
);
|
||||
@unlink($p2);
|
||||
$storage->delete($dir, $f2);
|
||||
}
|
||||
|
||||
private function runClaimsDumpResolve($storage): void
|
||||
{
|
||||
CLI::write('--- CLAIMS_DUMP ---', 'white');
|
||||
$dir = $this->uploadPath('claims_dump');
|
||||
$fileName = 'smoke_cdump_' . date('Ymd_His') . '_' . bin2hex(random_bytes(2)) . '.xlsx';
|
||||
$tmp = $this->makeTmp($fileName, "claims-dump\n");
|
||||
$up = $storage->upload($tmp, $dir, $fileName);
|
||||
@unlink($tmp);
|
||||
if (! ($up['success'] ?? false)) {
|
||||
$this->step('DUMP-01', false, (string) ($up['message'] ?? ''));
|
||||
return;
|
||||
}
|
||||
$permanent = $storage->resolveLocalPath($dir, $fileName);
|
||||
if (is_file($permanent)) {
|
||||
@unlink($permanent);
|
||||
}
|
||||
$resolved = storage_ensure_local_file($dir, $fileName);
|
||||
$this->step('DUMP-01', is_string($resolved) && is_file($resolved));
|
||||
if ($resolved) {
|
||||
storage_cleanup_temp_file($resolved);
|
||||
}
|
||||
$storage->delete($dir, $fileName);
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
private function manualScenarioLines(): array
|
||||
{
|
||||
return [
|
||||
'UI-KYC-01 Upload KYC in browser',
|
||||
'UI-KYC-02 Download download-kyc-docs/{id}',
|
||||
'UI-KYC-03 Re-upload / delete KYC',
|
||||
'UI-PT-01 Upload PT doc + download-pt-docs/{id}',
|
||||
'UI-PT-02 REST downloadPolicyFiles',
|
||||
'UI-CLAIM-01 Upload claim file in ticket UI',
|
||||
'UI-CLAIM-02 downloadClaimFile / viewClaimFile',
|
||||
'UI-CLAIM-03 Remove claim file in UI',
|
||||
'UI-CLAIM-04 Merge PDFs in UI (core automated: php spark storage:smoke-test-merge-pdf)',
|
||||
'UI-TPA-01 FHPL claim submit (base64)',
|
||||
'UI-TPA-02 Vidal claim submit (base64)',
|
||||
'UI-TPA-03 HealthIndia claim submit',
|
||||
'UI-TPA-04 MediAssist uses presigned URL',
|
||||
'UI-TPA-05 Volo uses presigned URL',
|
||||
'UI-EMP-01 Employee excel upload + background job',
|
||||
'UI-EMP-02 download-file-list / download_import_file',
|
||||
'UI-EMP-03 HR upload + hrFileDownload',
|
||||
'UI-LEAD-01 Lead member upload + demography job',
|
||||
'UI-LEAD-02 downloadMemberFile / error excel',
|
||||
'UI-DUMP-01 Claim dump / MIS / statements / BDS UI jobs',
|
||||
'UI-MAIL-01 Compose mail with attachment send',
|
||||
'UI-NEB-01 Non-EB asset + rack rate UI',
|
||||
'UI-NEG-01 Invalid extension rejected in UI',
|
||||
'UI-NEG-02 Toggle FILE_STORAGE_DRIVER=local and retest one upload',
|
||||
'UI-NEG-03 Toggle RETAIN_LOCAL=true/false and retest one upload',
|
||||
];
|
||||
}
|
||||
|
||||
private function printManualOnlyRemaining(): void
|
||||
{
|
||||
CLI::write('========== MANUAL UI / TPA / JOB (not auto-runnable) ==========', 'yellow');
|
||||
CLI::write('These need browser login / real TPA — tick after you test:');
|
||||
foreach ($this->manualScenarioLines() as $i => $line) {
|
||||
CLI::write(sprintf(' [ ] %s', $line));
|
||||
}
|
||||
CLI::newLine();
|
||||
CLI::write('Commands:');
|
||||
CLI::write(' php spark storage:smoke-test # run ALL automated scenarios');
|
||||
CLI::write(' php spark storage:smoke-test --list # list every scenario ID');
|
||||
CLI::write(' php spark storage:smoke-test --module claim_files');
|
||||
}
|
||||
}
|
||||
413
app/Commands/TestIciciStatusUpdate.php
Normal file
413
app/Commands/TestIciciStatusUpdate.php
Normal file
@ -0,0 +1,413 @@
|
||||
<?php
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
use App\Libraries\TPAClaimsImportServices\IciciClaimImportService;
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* Temporary dry-run helper for ICICI existing-ticket status update logic.
|
||||
*
|
||||
* Usage:
|
||||
* php spark tpa:test-icici-status --scenario
|
||||
* php spark tpa:test-icici-status --scenario --apply
|
||||
* php spark tpa:test-icici-status 68
|
||||
* php spark tpa:test-icici-status 68 --apply
|
||||
*/
|
||||
class TestIciciStatusUpdate extends BaseCommand
|
||||
{
|
||||
protected $group = 'TPA';
|
||||
protected $name = 'tpa:test-icici-status';
|
||||
protected $description = 'Dry-run / apply ICICI existing ticket status update logic';
|
||||
protected $usage = 'tpa:test-icici-status [file_id] [--scenario] [--apply] [--new-status STATUS]';
|
||||
protected $arguments = [
|
||||
'file_id' => 'Optional claim_dump_files.id to process',
|
||||
];
|
||||
protected $options = [
|
||||
'--scenario' => 'Seed a controlled pending dump row for one existing ICICI ticket and test status update',
|
||||
'--apply' => 'Actually run runTicketMasterInsert (default is map-only dry-run)',
|
||||
'--new-status' => 'Dump status string to use in scenario (default: REJECTED)',
|
||||
];
|
||||
|
||||
public function run(array $params)
|
||||
{
|
||||
helper('utility_helper');
|
||||
|
||||
$db = db_connect();
|
||||
$iciciTpaId = (int) env('ICICI_PRIMARY_KEY_CONSTANT');
|
||||
$fileId = $this->resolveFileId($params);
|
||||
$apply = $this->hasFlag('apply');
|
||||
$scenario = $this->hasFlag('scenario');
|
||||
$newStatus = $this->resolveOptionValue('new-status', 'REJECTED');
|
||||
|
||||
CLI::write("ICICI_PRIMARY_KEY_CONSTANT = {$iciciTpaId}", 'yellow');
|
||||
CLI::write('Parsed args: file_id=' . $fileId . ', apply=' . ($apply ? 'yes' : 'no') . ', scenario=' . ($scenario ? 'yes' : 'no') . ', new_status=' . $newStatus, 'yellow');
|
||||
|
||||
if ($scenario) {
|
||||
$fileId = $this->seedScenario($db, $iciciTpaId, $newStatus);
|
||||
if ($fileId <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileId <= 0) {
|
||||
$recent = $db->table('claim_dump_files')
|
||||
->select('id, tpa_id, client_id, client_policy_id, file_name, status, created_at')
|
||||
->where('tpa_id', $iciciTpaId)
|
||||
->orderBy('id', 'DESC')
|
||||
->limit(10)
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
CLI::write('No --file_id provided. Recent ICICI claim_dump_files:', 'cyan');
|
||||
if (empty($recent)) {
|
||||
CLI::error('No ICICI claim dump files found.');
|
||||
$this->printMatchPreview($db, $iciciTpaId);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($recent as $row) {
|
||||
CLI::write(json_encode($row));
|
||||
}
|
||||
|
||||
CLI::newLine();
|
||||
CLI::write('Controlled scenario test:', 'green');
|
||||
CLI::write(' php spark tpa:test-icici-status --scenario');
|
||||
CLI::write(' php spark tpa:test-icici-status --scenario --apply');
|
||||
CLI::write('Existing file (use positional file_id):');
|
||||
CLI::write(' php spark tpa:test-icici-status 68');
|
||||
CLI::write(' php spark tpa:test-icici-status 68 --apply');
|
||||
$this->printMatchPreview($db, $iciciTpaId);
|
||||
return;
|
||||
}
|
||||
|
||||
$file = $db->table('claim_dump_files')->where('id', $fileId)->get()->getRowArray();
|
||||
if (empty($file)) {
|
||||
CLI::error("claim_dump_files id={$fileId} not found");
|
||||
return;
|
||||
}
|
||||
|
||||
CLI::write('File: ' . json_encode([
|
||||
'id' => $file['id'],
|
||||
'tpa_id' => $file['tpa_id'],
|
||||
'status' => $file['status'],
|
||||
'file_name' => $file['file_name'],
|
||||
'client_policy_id' => $file['client_policy_id'],
|
||||
]), 'cyan');
|
||||
|
||||
if ((int) $file['tpa_id'] !== $iciciTpaId) {
|
||||
CLI::error("File tpa_id={$file['tpa_id']} is not ICICI ({$iciciTpaId}). Aborting.");
|
||||
return;
|
||||
}
|
||||
|
||||
$pendingDump = $db->table('claims_dump_icici')
|
||||
->where('file_id', $fileId)
|
||||
->where('is_active', 1)
|
||||
->where('ticket_id IS NULL', null, false)
|
||||
->where('master_reject_reason IS NULL', null, false)
|
||||
->countAllResults();
|
||||
|
||||
CLI::write("Pending dump rows for mapClaimMasterData: {$pendingDump}", 'yellow');
|
||||
|
||||
$service = new IciciClaimImportService();
|
||||
|
||||
if ($apply) {
|
||||
CLI::write('APPLY mode: running runTicketMasterInsert...', 'light_red');
|
||||
|
||||
$pendingBefore = $db->table('claims_dump_icici')
|
||||
->select('id, ticket_id, master_reject_reason, updated_status, employee_member_id, uhid, claimed_amount, doa')
|
||||
->where('file_id', $fileId)
|
||||
->where('is_active', 1)
|
||||
->get()
|
||||
->getResultArray();
|
||||
CLI::write('Dump rows BEFORE: ' . json_encode($pendingBefore, JSON_PRETTY_PRINT));
|
||||
|
||||
$result = $service->runTicketMasterInsert(['file_id' => $fileId]);
|
||||
CLI::write('Result: ' . json_encode($result, JSON_PRETTY_PRINT));
|
||||
|
||||
$pendingAfter = $db->table('claims_dump_icici')
|
||||
->select('id, ticket_id, master_reject_reason, updated_status, employee_member_id, uhid, claimed_amount, doa')
|
||||
->where('file_id', $fileId)
|
||||
->where('is_active', 1)
|
||||
->get()
|
||||
->getResultArray();
|
||||
CLI::write('Dump rows AFTER: ' . json_encode($pendingAfter, JSON_PRETTY_PRINT));
|
||||
|
||||
$ticketIds = array_values(array_filter(array_column($pendingAfter, 'ticket_id')));
|
||||
if (!empty($ticketIds)) {
|
||||
$ticketsAfter = $db->table('ticket_master')
|
||||
->select('id, claim_status_id, claim_dump_ref_id, emp_code, tpa_no, claim_amount, doa')
|
||||
->whereIn('id', $ticketIds)
|
||||
->get()
|
||||
->getResultArray();
|
||||
CLI::write('Linked tickets AFTER: ' . json_encode($ticketsAfter, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CLI::write('DRY-RUN mode: calling mapClaimMasterData only (no DB writes)...', 'green');
|
||||
|
||||
$ref = new \ReflectionClass($service);
|
||||
$method = $ref->getMethod('mapClaimMasterData');
|
||||
$method->setAccessible(true);
|
||||
$mapped = $method->invoke($service, $fileId);
|
||||
|
||||
if (empty($mapped['status'])) {
|
||||
CLI::error('mapClaimMasterData failed: ' . ($mapped['message'] ?? 'unknown'));
|
||||
if (!empty($mapped['error_data'])) {
|
||||
CLI::write(json_encode($mapped['error_data'], JSON_PRETTY_PRINT));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$inserts = $mapped['mapped_array'] ?? [];
|
||||
$rejects = $mapped['rejected_reason_array'] ?? [];
|
||||
$updates = $mapped['status_update_array'] ?? [];
|
||||
|
||||
CLI::newLine();
|
||||
CLI::write('=== RESULT COUNTS ===', 'yellow');
|
||||
CLI::write('New tickets to insert : ' . count($inserts));
|
||||
CLI::write('Status updates (existing) : ' . count($updates));
|
||||
CLI::write('Rejected / skipped rows : ' . count($rejects));
|
||||
|
||||
CLI::newLine();
|
||||
CLI::write('=== STATUS UPDATE ARRAY ===', 'cyan');
|
||||
CLI::write(empty($updates) ? '(none)' : json_encode($updates, JSON_PRETTY_PRINT));
|
||||
|
||||
CLI::newLine();
|
||||
CLI::write('=== REJECT / SKIP REASONS (first 20) ===', 'cyan');
|
||||
CLI::write(json_encode(array_slice($rejects, 0, 20), JSON_PRETTY_PRINT));
|
||||
|
||||
if (!empty($updates)) {
|
||||
CLI::newLine();
|
||||
CLI::write('Ticket fields BEFORE update:', 'yellow');
|
||||
$ids = array_column($updates, 'id');
|
||||
$before = $db->table('ticket_master')
|
||||
->select('id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, file_id')
|
||||
->whereIn('id', $ids)
|
||||
->get()
|
||||
->getResultArray();
|
||||
CLI::write(json_encode($before, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
CLI::newLine();
|
||||
CLI::write('To apply for real:', 'green');
|
||||
CLI::write(" php spark tpa:test-icici-status {$fileId} --apply");
|
||||
}
|
||||
|
||||
private function resolveFileId(array $params): int
|
||||
{
|
||||
if (!empty($params['file_id'])) {
|
||||
return (int) $params['file_id'];
|
||||
}
|
||||
if (!empty($params[0]) && is_numeric($params[0])) {
|
||||
return (int) $params[0];
|
||||
}
|
||||
|
||||
// Fallback for --file_id=68 / --file_id 68 (CI option parsing can miss these)
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
foreach ($argv as $i => $arg) {
|
||||
if (preg_match('/^--file[_-]id=(.+)$/', (string) $arg, $m)) {
|
||||
return (int) $m[1];
|
||||
}
|
||||
if (in_array($arg, ['--file_id', '--file-id'], true) && isset($argv[$i + 1])) {
|
||||
return (int) $argv[$i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
$opt = CLI::getOption('file_id') ?? CLI::getOption('file-id');
|
||||
return (int) ($opt ?? 0);
|
||||
}
|
||||
|
||||
private function hasFlag(string $name): bool
|
||||
{
|
||||
if (CLI::getOption($name) !== null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
return in_array('--' . $name, $argv, true);
|
||||
}
|
||||
|
||||
private function resolveOptionValue(string $name, string $default): string
|
||||
{
|
||||
$opt = CLI::getOption($name);
|
||||
if (is_string($opt) && $opt !== '') {
|
||||
return $opt;
|
||||
}
|
||||
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
foreach ($argv as $i => $arg) {
|
||||
if (preg_match('/^--' . preg_quote($name, '/') . '=(.+)$/', (string) $arg, $m)) {
|
||||
return trim($m[1]);
|
||||
}
|
||||
if ($arg === '--' . $name && isset($argv[$i + 1]) && strpos((string) $argv[$i + 1], '--') !== 0) {
|
||||
return (string) $argv[$i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temporary claim_dump_files + one pending claims_dump_icici row
|
||||
* matching an existing ticket, with a different dump status.
|
||||
*/
|
||||
private function seedScenario($db, int $iciciTpaId, string $newStatus): int
|
||||
{
|
||||
CLI::write("Seeding controlled ICICI status-update scenario (new_status={$newStatus})...", 'cyan');
|
||||
|
||||
$ticket = $db->table('ticket_master')
|
||||
->select('id, client_id, client_policy_id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, tpa_id')
|
||||
->where('tpa_id', $iciciTpaId)
|
||||
->where('is_active', 1)
|
||||
->where('emp_code IS NOT NULL', null, false)
|
||||
->where('tpa_no IS NOT NULL', null, false)
|
||||
->where('claim_amount IS NOT NULL', null, false)
|
||||
->where('doa IS NOT NULL', null, false)
|
||||
->orderBy('id', 'DESC')
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
if (empty($ticket)) {
|
||||
// Fall back to any ticket, then override tpa matching by using that ticket's keys
|
||||
$ticket = $db->table('ticket_master')
|
||||
->select('id, client_id, client_policy_id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, tpa_id')
|
||||
->where('is_active', 1)
|
||||
->where('claim_created_by', 'DUMP_TPA')
|
||||
->where('emp_code IS NOT NULL', null, false)
|
||||
->where('tpa_no IS NOT NULL', null, false)
|
||||
->where('claim_amount IS NOT NULL', null, false)
|
||||
->where('doa IS NOT NULL', null, false)
|
||||
->orderBy('id', 'DESC')
|
||||
->get()
|
||||
->getRowArray();
|
||||
}
|
||||
|
||||
if (empty($ticket)) {
|
||||
CLI::error('No suitable existing ticket found to seed a status-update scenario.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
CLI::write('Using existing ticket: ' . json_encode([
|
||||
'id' => $ticket['id'],
|
||||
'emp_code' => $ticket['emp_code'],
|
||||
'tpa_no' => $ticket['tpa_no'],
|
||||
'claim_amount' => $ticket['claim_amount'],
|
||||
'doa' => $ticket['doa'],
|
||||
'claim_status_id' => $ticket['claim_status_id'],
|
||||
'claim_dump_ref_id' => $ticket['claim_dump_ref_id'] ?? null,
|
||||
'tpa_id' => $ticket['tpa_id'],
|
||||
]), 'yellow');
|
||||
|
||||
// Clear stale claim_dump_ref_id so the scenario can re-test link backfill.
|
||||
if (!empty($ticket['claim_dump_ref_id'])) {
|
||||
$db->table('ticket_master')->where('id', $ticket['id'])->update([
|
||||
'claim_dump_ref_id' => null,
|
||||
]);
|
||||
$ticket['claim_dump_ref_id'] = null;
|
||||
CLI::write('Cleared stale ticket.claim_dump_ref_id for retest.', 'yellow');
|
||||
}
|
||||
|
||||
// Ensure a physical file exists for resolveTpaClaimDumpFile if --apply uses controller later
|
||||
$uploadDir = WRITEPATH . 'uploads' . DIRECTORY_SEPARATOR . 'claim_dump_excel' . DIRECTORY_SEPARATOR;
|
||||
if (!is_dir($uploadDir)) {
|
||||
mkdir($uploadDir, 0775, true);
|
||||
}
|
||||
$fileName = 'icici_status_test_' . date('Ymd_His') . '.xlsx';
|
||||
$filePath = $uploadDir . $fileName;
|
||||
if (!is_file($filePath)) {
|
||||
file_put_contents($filePath, 'placeholder');
|
||||
}
|
||||
|
||||
$fileInsert = [
|
||||
'tpa_id' => $iciciTpaId,
|
||||
'client_id' => $ticket['client_id'],
|
||||
'client_policy_id' => $ticket['client_policy_id'],
|
||||
'file_name' => $fileName,
|
||||
'status' => 'pending',
|
||||
'created_by' => 1,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'is_active' => 1,
|
||||
];
|
||||
|
||||
// claim_dump_files may have different nullable columns; insert only known-safe ones
|
||||
$fileCols = array_column($db->query('SHOW COLUMNS FROM claim_dump_files')->getResultArray(), 'Field');
|
||||
$fileInsert = array_intersect_key($fileInsert, array_flip($fileCols));
|
||||
|
||||
$db->table('claim_dump_files')->insert($fileInsert);
|
||||
$fileId = (int) $db->insertID();
|
||||
if ($fileId <= 0) {
|
||||
CLI::error('Failed to create claim_dump_files row for scenario.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
$dumpInsert = [
|
||||
'client_id' => $ticket['client_id'],
|
||||
'client_policy_id' => $ticket['client_policy_id'],
|
||||
'file_id' => $fileId,
|
||||
'employee_member_id' => $ticket['emp_code'],
|
||||
'uhid' => $ticket['tpa_no'],
|
||||
'claimed_amount' => $ticket['claim_amount'],
|
||||
'doa' => $ticket['doa'],
|
||||
'updated_status' => $newStatus,
|
||||
'relation' => 'SELF',
|
||||
'is_active' => 1,
|
||||
'created_by' => 1,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'ticket_id' => null,
|
||||
'master_reject_reason' => null,
|
||||
];
|
||||
|
||||
$dumpCols = array_column($db->query('SHOW COLUMNS FROM claims_dump_icici')->getResultArray(), 'Field');
|
||||
$dumpInsert = array_intersect_key($dumpInsert, array_flip($dumpCols));
|
||||
|
||||
$db->table('claims_dump_icici')->insert($dumpInsert);
|
||||
$dumpId = (int) $db->insertID();
|
||||
|
||||
CLI::write("Seeded file_id={$fileId}, dump_id={$dumpId}", 'green');
|
||||
CLI::write("Expected: if ticket claim_status_id ({$ticket['claim_status_id']}) != mapped REJECTED(8)/chosen status, status_update_array should contain ticket {$ticket['id']}", 'green');
|
||||
|
||||
return $fileId;
|
||||
}
|
||||
|
||||
private function printMatchPreview($db, int $iciciTpaId): void
|
||||
{
|
||||
CLI::newLine();
|
||||
CLI::write('Preview: dump rows that match existing tickets (possible status updates)', 'cyan');
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
cd.id AS dump_id,
|
||||
cd.file_id,
|
||||
cd.employee_member_id,
|
||||
cd.uhid,
|
||||
cd.claimed_amount,
|
||||
cd.doa,
|
||||
cd.updated_status AS dump_status,
|
||||
tm.id AS ticket_id,
|
||||
tm.claim_status_id AS current_status_id
|
||||
FROM claims_dump_icici cd
|
||||
INNER JOIN ticket_master tm
|
||||
ON tm.is_active = 1
|
||||
AND tm.emp_code = cd.employee_member_id
|
||||
AND tm.tpa_no = cd.uhid
|
||||
AND tm.claim_amount = cd.claimed_amount
|
||||
AND tm.doa = cd.doa
|
||||
WHERE cd.is_active = 1
|
||||
AND tm.tpa_id = ?
|
||||
ORDER BY cd.id DESC
|
||||
LIMIT 10
|
||||
";
|
||||
|
||||
$rows = $db->query($sql, [$iciciTpaId])->getResultArray();
|
||||
if (empty($rows)) {
|
||||
CLI::write('No overlapping dump/ticket pairs found.');
|
||||
return;
|
||||
}
|
||||
|
||||
CLI::write(json_encode($rows, JSON_PRETTY_PRINT));
|
||||
}
|
||||
}
|
||||
729
app/Commands/TestTpaE2ePipeline.php
Normal file
729
app/Commands/TestTpaE2ePipeline.php
Normal file
@ -0,0 +1,729 @@
|
||||
<?php
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
use App\Libraries\TpaClaimsImportFactory;
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* End-to-end TPA claim dump test: generate Excel → Job 1 → Job 2.
|
||||
*
|
||||
* Usage:
|
||||
* php spark tpa:e2e-pipeline --tpa=all
|
||||
* php spark tpa:e2e-pipeline --tpa=icici --apply
|
||||
* php spark tpa:e2e-pipeline --tpa-id=6 --apply
|
||||
* php spark tpa:e2e-pipeline --tpa=all --apply --truncate
|
||||
* php spark tpa:e2e-pipeline --tpa=icici --status-cases --apply
|
||||
*/
|
||||
class TestTpaE2ePipeline extends BaseCommand
|
||||
{
|
||||
protected $group = 'TPA';
|
||||
protected $name = 'tpa:e2e-pipeline';
|
||||
protected $description = 'Generate TPA Excel and run Job1 + Job2 end-to-end';
|
||||
protected $usage = 'tpa:e2e-pipeline [--tpa=NAME|all] [--tpa-id=ID] [--apply] [--truncate] [--new-status=STATUS] [--status-cases]';
|
||||
protected $options = [
|
||||
'--tpa' => 'TPA key: icici|abhi|mediassist|fhpl|rcare|vidal|all (default: all)',
|
||||
'--tpa-id' => 'Numeric TPA primary key (overrides --tpa)',
|
||||
'--apply' => 'Run Job 1 + Job 2 (default is generate Excel + file row only)',
|
||||
'--truncate' => 'Soft-truncate the test file after a successful apply',
|
||||
'--new-status' => 'Override dump status string written into Excel',
|
||||
'--status-cases' => 'Write 3 rows: empty status, unmapped status, mapped status (expect 61/61/mapped id)',
|
||||
];
|
||||
|
||||
/**
|
||||
* Per-TPA Excel seed config.
|
||||
* excel_fields: excel header => source key from ticket/policy context
|
||||
*/
|
||||
private function tpaConfigs(): array
|
||||
{
|
||||
return [
|
||||
'icici' => [
|
||||
'env' => 'ICICI_PRIMARY_KEY_CONSTANT',
|
||||
'table' => 'claims_dump_icici',
|
||||
'sheet' => null,
|
||||
'default_status' => 'REJECTED',
|
||||
'status_header' => 'Updated_status',
|
||||
'status_db_column' => 'updated_status',
|
||||
'mapped_status' => 'PAID',
|
||||
'mapped_status_id' => 11,
|
||||
'excel_fields' => [
|
||||
'POLICY_NO' => 'policy_no',
|
||||
'UHID' => 'tpa_no',
|
||||
'EMPLOYEE_MEMBER_ID' => 'emp_code',
|
||||
'RELATION' => 'relation',
|
||||
'CLAIMED_AMOUNT' => 'claim_amount',
|
||||
'DOA' => 'doa',
|
||||
'Updated_status' => 'status_value',
|
||||
],
|
||||
],
|
||||
'abhi' => [
|
||||
'env' => 'ABHI_PRIMARY_KEY_CONSTANT',
|
||||
'table' => 'claims_dump_abhi',
|
||||
'sheet' => null,
|
||||
'default_status' => 'Rejected',
|
||||
'status_header' => 'Claim Status',
|
||||
'status_db_column' => 'claim_status',
|
||||
'mapped_status' => 'Paid',
|
||||
'mapped_status_id' => 11,
|
||||
'excel_fields' => [
|
||||
'Policy Number' => 'policy_no',
|
||||
'HEALTHCARD_ID' => 'tpa_no',
|
||||
'Member Code' => 'emp_code',
|
||||
'Relation' => 'relation',
|
||||
'Claimed Amount'=> 'claim_amount',
|
||||
'DOA' => 'doa',
|
||||
'Claim Status' => 'status_value',
|
||||
],
|
||||
],
|
||||
'mediassist' => [
|
||||
'env' => 'MEDI_ASSIST_PRIMARY_KEY_CONSTANT',
|
||||
'table' => 'claims_dump_medi_assist',
|
||||
'sheet' => null,
|
||||
'default_status' => 'Rejected',
|
||||
'status_header' => 'claim_status',
|
||||
'status_db_column' => 'claim_status',
|
||||
'mapped_status' => 'Paid',
|
||||
'mapped_status_id' => 11,
|
||||
'excel_fields' => [
|
||||
'policy_no' => 'policy_no',
|
||||
'event_id' => 'tpa_no',
|
||||
'pribenef_employee_code' => 'emp_code',
|
||||
'benef_relation' => 'relation',
|
||||
'claim_amount' => 'claim_amount',
|
||||
'date_of_admission' => 'doa',
|
||||
'claim_status' => 'status_value',
|
||||
],
|
||||
],
|
||||
'fhpl' => [
|
||||
'env' => 'FHPL_PRIMARY_KEY_CONSTANT',
|
||||
'table' => 'claims_dump_fhpl',
|
||||
'sheet' => 'Claims&Preauth',
|
||||
'default_status' => 'Rejected',
|
||||
'status_header' => 'Current Claim Status',
|
||||
'status_db_column' => 'current_claim_status',
|
||||
'mapped_status' => 'Paid',
|
||||
'mapped_status_id' => 11,
|
||||
'excel_fields' => [
|
||||
'Policy No' => 'policy_no',
|
||||
'UHIDNO' => 'tpa_no',
|
||||
'employeeid' => 'emp_code',
|
||||
'relationship' => 'relation',
|
||||
'claimamount' => 'claim_amount',
|
||||
'Admdate' => 'doa',
|
||||
'Current Claim Status' => 'status_value',
|
||||
],
|
||||
],
|
||||
'rcare' => [
|
||||
'env' => 'R_CARE_PRIMARY_KEY_CONSTANT',
|
||||
'table' => 'claims_dump_reliance',
|
||||
'sheet' => 'CL',
|
||||
'default_status' => 'Rejected',
|
||||
'status_header' => 'Final Status',
|
||||
'status_db_column' => 'final_status',
|
||||
'mapped_status' => 'Paid',
|
||||
'mapped_status_id' => 11,
|
||||
'excel_fields' => [
|
||||
'Policy Number' => 'policy_no',
|
||||
'UHID' => 'tpa_no',
|
||||
'Employee/Member Id' => 'emp_code',
|
||||
'Relation' => 'relation',
|
||||
'Claimed Amount' => 'claim_amount',
|
||||
'DOA/OPD Treatment From' => 'doa',
|
||||
'Final Status' => 'status_value',
|
||||
],
|
||||
],
|
||||
'vidal' => [
|
||||
'env' => 'VIDAL_PRIMARY_KEY_CONSTANT',
|
||||
'table' => 'claims_dump_vidal',
|
||||
'sheet' => null,
|
||||
'default_status' => 'Rejected',
|
||||
'status_header' => 'Claim Status',
|
||||
'status_db_column' => 'claim_status',
|
||||
'mapped_status' => 'Paid',
|
||||
'mapped_status_id' => 11,
|
||||
'excel_fields' => [
|
||||
'Insurer Policy Number' => 'policy_no',
|
||||
'Primary Policy Holder Card ID' => 'tpa_no',
|
||||
'Employee Number' => 'emp_code',
|
||||
'Relation' => 'relation',
|
||||
'Claim Amount' => 'claim_amount',
|
||||
'Date of Admission' => 'doa',
|
||||
'Claim Status' => 'status_value',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function run(array $params)
|
||||
{
|
||||
helper('utility_helper');
|
||||
|
||||
$db = db_connect();
|
||||
$apply = $this->hasFlag('apply');
|
||||
$truncate = $this->hasFlag('truncate');
|
||||
$statusCases = $this->hasFlag('status-cases');
|
||||
$statusOverride = $this->resolveOptionValue('new-status', '');
|
||||
$configs = $this->tpaConfigs();
|
||||
|
||||
$selected = $this->resolveSelectedTpas($configs);
|
||||
if ($selected === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$summary = [];
|
||||
foreach ($selected as $tpaKey) {
|
||||
CLI::newLine();
|
||||
CLI::write(str_repeat('=', 64), 'yellow');
|
||||
CLI::write('E2E TPA: ' . strtoupper($tpaKey) . ($statusCases ? ' [status-cases]' : ''), 'yellow');
|
||||
CLI::write(str_repeat('=', 64), 'yellow');
|
||||
|
||||
try {
|
||||
$summary[$tpaKey] = $this->runForTpa(
|
||||
$db,
|
||||
$tpaKey,
|
||||
$configs[$tpaKey],
|
||||
$apply,
|
||||
$truncate,
|
||||
$statusOverride,
|
||||
$statusCases
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
CLI::error("[{$tpaKey}] " . $e->getMessage());
|
||||
$summary[$tpaKey] = [
|
||||
'ok' => false,
|
||||
'error' => $e->getMessage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
CLI::newLine();
|
||||
CLI::write(str_repeat('=', 64), 'cyan');
|
||||
CLI::write('E2E SUMMARY', 'cyan');
|
||||
CLI::write(str_repeat('=', 64), 'cyan');
|
||||
foreach ($summary as $tpaKey => $row) {
|
||||
$ok = !empty($row['ok']);
|
||||
$line = sprintf(
|
||||
'%-12s %s file_id=%s job1=%s job2=%s%s',
|
||||
strtoupper($tpaKey),
|
||||
$ok ? 'OK' : 'FAIL',
|
||||
$row['file_id'] ?? '-',
|
||||
$row['job1'] ?? '-',
|
||||
$row['job2'] ?? '-',
|
||||
!empty($row['error']) ? ' | ' . $row['error'] : ''
|
||||
);
|
||||
CLI::write($line, $ok ? 'green' : 'red');
|
||||
if (!empty($row['status_cases'])) {
|
||||
foreach ($row['status_cases'] as $case) {
|
||||
$caseOk = !empty($case['ok']);
|
||||
CLI::write(sprintf(
|
||||
' %-10s dump=%s expected=%s got=%s %s',
|
||||
$case['label'],
|
||||
$case['dump_status'] === '' ? '(empty)' : $case['dump_status'],
|
||||
$case['expected_id'],
|
||||
$case['actual_id'] ?? 'null',
|
||||
$caseOk ? 'PASS' : 'FAIL'
|
||||
), $caseOk ? 'green' : 'red');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$apply) {
|
||||
CLI::newLine();
|
||||
CLI::write('Dry-run only (Excel + claim_dump_files created). Re-run with --apply to execute Job1+Job2.', 'yellow');
|
||||
}
|
||||
}
|
||||
|
||||
private function resolveSelectedTpas(array $configs): array
|
||||
{
|
||||
$tpaIdOpt = (int) $this->resolveOptionValue('tpa-id', '0');
|
||||
if ($tpaIdOpt > 0) {
|
||||
foreach ($configs as $key => $cfg) {
|
||||
if ((int) env($cfg['env']) === $tpaIdOpt) {
|
||||
return [$key];
|
||||
}
|
||||
}
|
||||
CLI::error("No TPA config matches --tpa-id={$tpaIdOpt}");
|
||||
return [];
|
||||
}
|
||||
|
||||
$tpaOpt = strtolower((string) ($this->resolveOptionValue('tpa', '') ?: 'all'));
|
||||
if ($tpaOpt === '' || $tpaOpt === 'all') {
|
||||
return array_keys($configs);
|
||||
}
|
||||
if (!isset($configs[$tpaOpt])) {
|
||||
CLI::error("Unknown --tpa={$tpaOpt}. Use: " . implode('|', array_keys($configs)) . '|all');
|
||||
return [];
|
||||
}
|
||||
|
||||
return [$tpaOpt];
|
||||
}
|
||||
|
||||
private function runForTpa(
|
||||
$db,
|
||||
string $tpaKey,
|
||||
array $cfg,
|
||||
bool $apply,
|
||||
bool $truncate,
|
||||
string $statusOverride,
|
||||
bool $statusCases = false
|
||||
): array {
|
||||
$tpaId = (int) env($cfg['env']);
|
||||
CLI::write("{$cfg['env']} = {$tpaId}", 'cyan');
|
||||
CLI::write("table={$cfg['table']}, sheet=" . ($cfg['sheet'] ?? 'active'), 'cyan');
|
||||
|
||||
if (!$this->tableExists($db, $cfg['table'])) {
|
||||
throw new \RuntimeException("Table {$cfg['table']} does not exist");
|
||||
}
|
||||
|
||||
$context = $this->resolveSeedContext($db, $tpaId, $tpaKey, $cfg, $statusOverride);
|
||||
CLI::write('Seed ticket: ' . json_encode([
|
||||
'id' => $context['ticket']['id'] ?? null,
|
||||
'client_id' => $context['client_id'],
|
||||
'client_policy_id' => $context['client_policy_id'],
|
||||
'policy_no' => $context['policy_no'],
|
||||
'emp_code' => $context['emp_code'],
|
||||
'tpa_no' => $context['tpa_no'],
|
||||
'claim_amount' => $context['claim_amount'],
|
||||
'doa' => $context['doa'],
|
||||
'status_value' => $context['status_value'],
|
||||
]));
|
||||
|
||||
$service = TpaClaimsImportFactory::make($tpaId);
|
||||
$headers = $this->extractExcelHeaders($service);
|
||||
if ($headers === []) {
|
||||
throw new \RuntimeException('Could not read Excel headers from service mapping');
|
||||
}
|
||||
|
||||
$scenarios = $statusCases
|
||||
? $this->buildStatusCaseScenarios($cfg)
|
||||
: [['label' => 'default', 'status_value' => $context['status_value'], 'claim_amount' => $context['claim_amount'], 'expected_id' => null]];
|
||||
|
||||
$excelRows = [];
|
||||
foreach ($scenarios as $i => $scenario) {
|
||||
$rowContext = $context;
|
||||
$rowContext['status_value'] = $scenario['status_value'];
|
||||
$rowContext['claim_amount'] = $scenario['claim_amount'];
|
||||
$excelRows[] = $this->buildExcelRow($headers, $cfg, $rowContext);
|
||||
CLI::write(sprintf(
|
||||
'Row %d [%s]: status=%s amount=%s expected_id=%s',
|
||||
$i + 1,
|
||||
$scenario['label'],
|
||||
$scenario['status_value'] === '' ? '(empty)' : $scenario['status_value'],
|
||||
$scenario['claim_amount'],
|
||||
$scenario['expected_id'] ?? 'n/a'
|
||||
), 'cyan');
|
||||
}
|
||||
|
||||
$fileName = $statusCases
|
||||
? "{$tpaKey}_status_cases_" . date('Ymd_His') . '.xlsx'
|
||||
: "{$tpaKey}_e2e_" . date('Ymd_His') . '.xlsx';
|
||||
$uploadDir = WRITEPATH . 'uploads' . DIRECTORY_SEPARATOR . 'claim_dump_excel' . DIRECTORY_SEPARATOR;
|
||||
if (!is_dir($uploadDir)) {
|
||||
mkdir($uploadDir, 0775, true);
|
||||
}
|
||||
$filePath = $uploadDir . $fileName;
|
||||
$this->writeExcel($filePath, $headers, $excelRows, $cfg['sheet'] ?? null);
|
||||
CLI::write("Excel written: {$filePath} (" . count($excelRows) . ' data rows)', 'green');
|
||||
|
||||
$fileId = $this->createClaimDumpFile($db, [
|
||||
'tpa_id' => $tpaId,
|
||||
'client_id' => $context['client_id'],
|
||||
'client_policy_id' => $context['client_policy_id'],
|
||||
'file_name' => $fileName,
|
||||
'status' => 'inprogress',
|
||||
'created_by' => 1,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'is_active' => 1,
|
||||
]);
|
||||
CLI::write("claim_dump_files.id = {$fileId}", 'green');
|
||||
|
||||
$result = [
|
||||
'ok' => true,
|
||||
'file_id' => $fileId,
|
||||
'file_name' => $fileName,
|
||||
'job1' => 'skipped',
|
||||
'job2' => 'skipped',
|
||||
];
|
||||
|
||||
if (!$apply) {
|
||||
$applyHint = $statusCases
|
||||
? "php spark tpa:e2e-pipeline --tpa={$tpaKey} --status-cases --apply"
|
||||
: "php spark tpa:e2e-pipeline --tpa={$tpaKey} --apply";
|
||||
CLI::write("Dry-run ready. Apply with: {$applyHint}", 'yellow');
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Clear claim_dump_ref_id so link/update path can re-bind cleanly.
|
||||
if (!$statusCases && !empty($context['ticket']['id']) && !empty($context['ticket']['claim_dump_ref_id'])) {
|
||||
$db->table('ticket_master')
|
||||
->where('id', $context['ticket']['id'])
|
||||
->update(['claim_dump_ref_id' => null]);
|
||||
CLI::write('Cleared seed ticket.claim_dump_ref_id for retest.', 'yellow');
|
||||
}
|
||||
|
||||
CLI::write('Running Job 1 (Excel → dump)...', 'light_red');
|
||||
$job1 = $service->runTpaClaimDumpInsert($filePath, $fileId);
|
||||
CLI::write('Job1 result: ' . json_encode($job1));
|
||||
$result['job1'] = !empty($job1['status']) ? 'ok' : 'fail';
|
||||
if (empty($job1['status'])) {
|
||||
$db->table('claim_dump_files')->where('id', $fileId)->update([
|
||||
'status' => 'failed',
|
||||
'reason' => json_encode(['error_data' => $job1['message'] ?? 'Job1 failed']),
|
||||
]);
|
||||
$result['ok'] = false;
|
||||
$result['error'] = $job1['message'] ?? 'Job1 failed';
|
||||
return $result;
|
||||
}
|
||||
|
||||
$dumpCount = $db->table($cfg['table'])->where('file_id', $fileId)->where('is_active', 1)->countAllResults();
|
||||
CLI::write("Dump rows after Job1: {$dumpCount}", 'cyan');
|
||||
|
||||
CLI::write('Running Job 2 (dump → ticket_master)...', 'light_red');
|
||||
$job2 = $service->runTicketMasterInsert(['file_id' => $fileId]);
|
||||
CLI::write('Job2 result: ' . json_encode($job2));
|
||||
$result['job2'] = !empty($job2['status']) ? 'ok' : 'fail';
|
||||
|
||||
if (!empty($job2['status'])) {
|
||||
$db->table('claim_dump_files')->where('id', $fileId)->update([
|
||||
'status' => 'success',
|
||||
'reason' => null,
|
||||
]);
|
||||
} else {
|
||||
$db->table('claim_dump_files')->where('id', $fileId)->update([
|
||||
'status' => 'failed',
|
||||
'reason' => json_encode(['error_data' => $job2['message'] ?? 'Job2 failed']),
|
||||
]);
|
||||
$result['ok'] = false;
|
||||
$result['error'] = $job2['message'] ?? 'Job2 failed';
|
||||
return $result;
|
||||
}
|
||||
|
||||
$statusCol = $cfg['status_db_column'] ?? null;
|
||||
$selectCols = 'id, ticket_id, master_reject_reason, is_active';
|
||||
if ($statusCol) {
|
||||
$selectCols .= ', ' . $statusCol;
|
||||
}
|
||||
$afterDump = $db->table($cfg['table'])
|
||||
->select($selectCols)
|
||||
->where('file_id', $fileId)
|
||||
->orderBy('id', 'ASC')
|
||||
->get()->getResultArray();
|
||||
CLI::write('Dump AFTER Job2: ' . json_encode($afterDump, JSON_PRETTY_PRINT));
|
||||
|
||||
$linked = 0;
|
||||
foreach ($afterDump as $drow) {
|
||||
if (!empty($drow['ticket_id'])) {
|
||||
$linked++;
|
||||
}
|
||||
}
|
||||
$result['linked'] = $linked;
|
||||
if ($linked === 0 && empty($afterDump[0]['master_reject_reason'])) {
|
||||
$result['ok'] = false;
|
||||
$result['error'] = 'Job2 reported success but dump ticket_id is still null';
|
||||
CLI::error($result['error']);
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($statusCases) {
|
||||
$caseResults = $this->assertStatusCases($db, $cfg, $afterDump, $scenarios);
|
||||
$result['status_cases'] = $caseResults;
|
||||
foreach ($caseResults as $case) {
|
||||
if (empty($case['ok'])) {
|
||||
$result['ok'] = false;
|
||||
$result['error'] = 'Status case assertion failed for: ' . $case['label'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($truncate) {
|
||||
CLI::write('Running soft truncate...', 'light_red');
|
||||
$trunc = $service->softTruncateClaimDump($fileId);
|
||||
CLI::write('Truncate: ' . json_encode($trunc));
|
||||
$result['truncated'] = !empty($trunc['status']);
|
||||
if (empty($trunc['status'])) {
|
||||
$result['ok'] = false;
|
||||
$result['error'] = $trunc['message'] ?? 'Truncate failed';
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Three rows: empty status → 61, unmapped → 61, mapped → mapped_status_id.
|
||||
* Unique claim amounts so Job 2 creates/links distinct tickets.
|
||||
*/
|
||||
private function buildStatusCaseScenarios(array $cfg): array
|
||||
{
|
||||
$suffix = (string) (time() % 100000);
|
||||
$mappedStatus = (string) ($cfg['mapped_status'] ?? 'Paid');
|
||||
$mappedId = (int) ($cfg['mapped_status_id'] ?? 11);
|
||||
|
||||
return [
|
||||
[
|
||||
'label' => 'empty',
|
||||
'status_value' => '',
|
||||
'claim_amount' => '9' . $suffix . '.11',
|
||||
'expected_id' => 61,
|
||||
],
|
||||
[
|
||||
'label' => 'unmapped',
|
||||
'status_value' => 'ZZZ_UNMAPPED_STATUS',
|
||||
'claim_amount' => '9' . $suffix . '.22',
|
||||
'expected_id' => 61,
|
||||
],
|
||||
[
|
||||
'label' => 'mapped',
|
||||
'status_value' => $mappedStatus,
|
||||
'claim_amount' => '9' . $suffix . '.33',
|
||||
'expected_id' => $mappedId,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Match dump rows to scenarios by amount (and status text) and assert ticket claim_status_id.
|
||||
*/
|
||||
private function assertStatusCases($db, array $cfg, array $afterDump, array $scenarios): array
|
||||
{
|
||||
$statusCol = $cfg['status_db_column'] ?? null;
|
||||
$results = [];
|
||||
|
||||
// Resolve amount column from excel_fields source key claim_amount → need dump amount column.
|
||||
// Prefer joining via ticket_id and comparing expected_id on ticket_master.
|
||||
foreach ($scenarios as $scenario) {
|
||||
$match = null;
|
||||
foreach ($afterDump as $drow) {
|
||||
$dumpStatus = $statusCol !== null ? trim((string) ($drow[$statusCol] ?? '')) : '';
|
||||
$expectedStatus = trim((string) $scenario['status_value']);
|
||||
if ($dumpStatus === $expectedStatus) {
|
||||
$match = $drow;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$actualId = null;
|
||||
$ticketId = $match['ticket_id'] ?? null;
|
||||
if (!empty($ticketId)) {
|
||||
$ticket = $db->table('ticket_master')
|
||||
->select('id, claim_status_id')
|
||||
->where('id', $ticketId)
|
||||
->get()->getRowArray();
|
||||
$actualId = isset($ticket['claim_status_id']) ? (int) $ticket['claim_status_id'] : null;
|
||||
}
|
||||
|
||||
$ok = $match !== null
|
||||
&& !empty($ticketId)
|
||||
&& $actualId !== null
|
||||
&& $actualId === (int) $scenario['expected_id'];
|
||||
|
||||
$results[] = [
|
||||
'label' => $scenario['label'],
|
||||
'dump_status' => $scenario['status_value'],
|
||||
'expected_id' => (int) $scenario['expected_id'],
|
||||
'actual_id' => $actualId,
|
||||
'ticket_id' => $ticketId,
|
||||
'dump_id' => $match['id'] ?? null,
|
||||
'reject' => $match['master_reject_reason'] ?? null,
|
||||
'ok' => $ok,
|
||||
];
|
||||
|
||||
CLI::write(sprintf(
|
||||
'ASSERT [%s] status=%s expected=%d actual=%s ticket_id=%s dump_id=%s %s%s',
|
||||
$scenario['label'],
|
||||
$scenario['status_value'] === '' ? '(empty)' : $scenario['status_value'],
|
||||
(int) $scenario['expected_id'],
|
||||
$actualId === null ? 'null' : (string) $actualId,
|
||||
$ticketId ?? 'null',
|
||||
$match['id'] ?? 'null',
|
||||
$ok ? 'PASS' : 'FAIL',
|
||||
!empty($match['master_reject_reason']) ? ' reject=' . $match['master_reject_reason'] : ''
|
||||
), $ok ? 'green' : 'red');
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function resolveSeedContext($db, int $tpaId, string $tpaKey, array $cfg, string $statusOverride): array
|
||||
{
|
||||
$ticket = $db->table('ticket_master')
|
||||
->select('id, client_id, client_policy_id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, tpa_id')
|
||||
->where('tpa_id', $tpaId)
|
||||
->where('is_active', 1)
|
||||
->where('emp_code IS NOT NULL', null, false)
|
||||
->where('tpa_no IS NOT NULL', null, false)
|
||||
->where('claim_amount IS NOT NULL', null, false)
|
||||
->where('doa IS NOT NULL', null, false)
|
||||
->orderBy('id', 'DESC')
|
||||
->get()->getRowArray();
|
||||
|
||||
if (empty($ticket)) {
|
||||
CLI::write("[{$tpaKey}] No ticket for this tpa_id; falling back to any ticket with identity keys.", 'yellow');
|
||||
$ticket = $db->table('ticket_master')
|
||||
->select('id, client_id, client_policy_id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, tpa_id')
|
||||
->where('is_active', 1)
|
||||
->where('client_policy_id IS NOT NULL', null, false)
|
||||
->where('emp_code IS NOT NULL', null, false)
|
||||
->where('tpa_no IS NOT NULL', null, false)
|
||||
->where('claim_amount IS NOT NULL', null, false)
|
||||
->where('doa IS NOT NULL', null, false)
|
||||
->orderBy('id', 'DESC')
|
||||
->get()->getRowArray();
|
||||
}
|
||||
|
||||
if (empty($ticket)) {
|
||||
throw new \RuntimeException('No suitable ticket_master row found to seed Excel');
|
||||
}
|
||||
|
||||
$policy = $db->table('client_policy')
|
||||
->select('id, client_id, policy_no')
|
||||
->where('id', $ticket['client_policy_id'])
|
||||
->get()->getRowArray();
|
||||
if (empty($policy) || trim((string) ($policy['policy_no'] ?? '')) === '') {
|
||||
throw new \RuntimeException('client_policy / policy_no missing for seed ticket');
|
||||
}
|
||||
|
||||
$statusValue = $statusOverride !== '' ? $statusOverride : $cfg['default_status'];
|
||||
|
||||
return [
|
||||
'ticket' => $ticket,
|
||||
'client_id' => (int) ($ticket['client_id'] ?? $policy['client_id']),
|
||||
'client_policy_id' => (int) $ticket['client_policy_id'],
|
||||
'policy_no' => trim((string) $policy['policy_no']),
|
||||
'emp_code' => (string) $ticket['emp_code'],
|
||||
'tpa_no' => (string) $ticket['tpa_no'],
|
||||
'claim_amount' => (string) $ticket['claim_amount'],
|
||||
'doa' => (string) $ticket['doa'],
|
||||
'relation' => 'SELF',
|
||||
'status_value' => $statusValue,
|
||||
];
|
||||
}
|
||||
|
||||
private function extractExcelHeaders(object $service): array
|
||||
{
|
||||
$ref = new ReflectionClass($service);
|
||||
if (!$ref->hasProperty('mapping')) {
|
||||
return [];
|
||||
}
|
||||
$prop = $ref->getProperty('mapping');
|
||||
$prop->setAccessible(true);
|
||||
$mapping = $prop->getValue($service);
|
||||
if (!is_array($mapping)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
foreach ($mapping as $map) {
|
||||
$excel = $map['excel_column'] ?? null;
|
||||
if (is_array($excel)) {
|
||||
$name = trim((string) ($excel['col_name'] ?? ''));
|
||||
} else {
|
||||
$name = trim((string) $excel);
|
||||
}
|
||||
if ($name !== '') {
|
||||
$headers[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
private function buildExcelRow(array $headers, array $cfg, array $context): array
|
||||
{
|
||||
$row = array_fill(0, count($headers), '');
|
||||
$headerIndex = array_flip($headers);
|
||||
|
||||
foreach ($cfg['excel_fields'] as $excelHeader => $sourceKey) {
|
||||
if (!isset($headerIndex[$excelHeader])) {
|
||||
CLI::write("Warning: Excel header '{$excelHeader}' not in mapping; skipped.", 'yellow');
|
||||
continue;
|
||||
}
|
||||
$row[$headerIndex[$excelHeader]] = $context[$sourceKey] ?? '';
|
||||
}
|
||||
|
||||
// Ensure status header is filled even if excel_fields key differs.
|
||||
if (isset($headerIndex[$cfg['status_header']])) {
|
||||
$row[$headerIndex[$cfg['status_header']]] = $context['status_value'];
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
private function writeExcel(string $filePath, array $headers, array $rows, ?string $sheetName): void
|
||||
{
|
||||
// Backward compatible: single row flat array becomes one data row.
|
||||
if ($rows !== [] && !is_array($rows[0] ?? null)) {
|
||||
$rows = [$rows];
|
||||
}
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
if ($sheetName) {
|
||||
$sheet->setTitle($sheetName);
|
||||
}
|
||||
|
||||
foreach ($headers as $i => $header) {
|
||||
$sheet->setCellValue([$i + 1, 1], $header);
|
||||
}
|
||||
|
||||
foreach ($rows as $r => $rowValues) {
|
||||
foreach ($headers as $i => $header) {
|
||||
$sheet->setCellValue([$i + 1, $r + 2], $rowValues[$i] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
$writer = new Xlsx($spreadsheet);
|
||||
$writer->save($filePath);
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
unset($spreadsheet);
|
||||
}
|
||||
|
||||
private function createClaimDumpFile($db, array $data): int
|
||||
{
|
||||
$cols = array_column($db->query('SHOW COLUMNS FROM claim_dump_files')->getResultArray(), 'Field');
|
||||
$data = array_intersect_key($data, array_flip($cols));
|
||||
$db->table('claim_dump_files')->insert($data);
|
||||
$id = (int) $db->insertID();
|
||||
if ($id <= 0) {
|
||||
throw new \RuntimeException('Failed to insert claim_dump_files');
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
private function tableExists($db, string $table): bool
|
||||
{
|
||||
return !empty($db->query('SHOW TABLES LIKE ' . $db->escape($table))->getResultArray());
|
||||
}
|
||||
|
||||
private function hasFlag(string $name): bool
|
||||
{
|
||||
if (CLI::getOption($name) !== null) {
|
||||
return true;
|
||||
}
|
||||
return in_array('--' . $name, $_SERVER['argv'] ?? [], true);
|
||||
}
|
||||
|
||||
private function resolveOptionValue(string $name, string $default): string
|
||||
{
|
||||
$opt = CLI::getOption($name);
|
||||
if (is_string($opt) && $opt !== '') {
|
||||
return $opt;
|
||||
}
|
||||
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
foreach ($argv as $i => $arg) {
|
||||
if (preg_match('/^--' . preg_quote($name, '/') . '=(.+)$/', (string) $arg, $m)) {
|
||||
return trim($m[1]);
|
||||
}
|
||||
if ($arg === '--' . $name && isset($argv[$i + 1]) && strpos((string) $argv[$i + 1], '--') !== 0) {
|
||||
return (string) $argv[$i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
551
app/Commands/TestTpaStatusUpdate.php
Normal file
551
app/Commands/TestTpaStatusUpdate.php
Normal file
@ -0,0 +1,551 @@
|
||||
<?php
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
use App\Libraries\TpaClaimsImportFactory;
|
||||
use CodeIgniter\CLI\BaseCommand;
|
||||
use CodeIgniter\CLI\CLI;
|
||||
|
||||
/**
|
||||
* Dry-run / apply existing-ticket status+link update logic for any TPA.
|
||||
*
|
||||
* Usage:
|
||||
* php spark tpa:test-status
|
||||
* php spark tpa:test-status --tpa=all
|
||||
* php spark tpa:test-status --tpa=abhi --scenario
|
||||
* php spark tpa:test-status --tpa=fhpl --scenario --apply
|
||||
* php spark tpa:test-status --tpa=icici 69 --apply
|
||||
*/
|
||||
class TestTpaStatusUpdate extends BaseCommand
|
||||
{
|
||||
protected $group = 'TPA';
|
||||
protected $name = 'tpa:test-status';
|
||||
protected $description = 'Dry-run / apply existing ticket status+link updates for TPA claim dumps';
|
||||
protected $usage = 'tpa:test-status [file_id] [--tpa=NAME|all] [--scenario] [--apply] [--new-status=STATUS]';
|
||||
protected $arguments = [
|
||||
'file_id' => 'Optional claim_dump_files.id to process',
|
||||
];
|
||||
protected $options = [
|
||||
'--tpa' => 'TPA key: icici|abhi|mediassist|fhpl|rcare|vidal|all (default: all for --scenario)',
|
||||
'--scenario' => 'Seed one pending dump row matching an existing ticket',
|
||||
'--apply' => 'Actually run runTicketMasterInsert (default is map-only dry-run)',
|
||||
'--new-status' => 'Dump status string for scenario (defaults per TPA)',
|
||||
];
|
||||
|
||||
/**
|
||||
* Per-TPA dump column mapping used to seed scenario rows.
|
||||
*/
|
||||
private function tpaConfigs(): array
|
||||
{
|
||||
return [
|
||||
'icici' => [
|
||||
'env' => 'ICICI_PRIMARY_KEY_CONSTANT',
|
||||
'table' => 'claims_dump_icici',
|
||||
'status_column' => 'updated_status',
|
||||
'default_status'=> 'REJECTED',
|
||||
'same_status' => 'PAID',
|
||||
'dump_to_ticket'=> [
|
||||
'employee_member_id' => 'emp_code',
|
||||
'uhid' => 'tpa_no',
|
||||
'claimed_amount' => 'claim_amount',
|
||||
'doa' => 'doa',
|
||||
],
|
||||
'extra_dump' => [
|
||||
'relation' => 'SELF',
|
||||
],
|
||||
],
|
||||
'abhi' => [
|
||||
'env' => 'ABHI_PRIMARY_KEY_CONSTANT',
|
||||
'table' => 'claims_dump_abhi',
|
||||
'status_column' => 'claim_status',
|
||||
'default_status'=> 'Rejected',
|
||||
'same_status' => 'Settled',
|
||||
'dump_to_ticket'=> [
|
||||
'member_code' => 'emp_code',
|
||||
'healthcard_id' => 'tpa_no',
|
||||
'claimed_amount' => 'claim_amount',
|
||||
'doa' => 'doa',
|
||||
],
|
||||
'extra_dump' => [
|
||||
'relation' => 'SELF',
|
||||
],
|
||||
],
|
||||
'mediassist' => [
|
||||
'env' => 'MEDI_ASSIST_PRIMARY_KEY_CONSTANT',
|
||||
'table' => 'claims_dump_medi_assist',
|
||||
'status_column' => 'claim_status',
|
||||
'default_status'=> 'Rejected',
|
||||
'same_status' => 'Settled',
|
||||
'dump_to_ticket'=> [
|
||||
'pribenef_employee_code' => 'emp_code',
|
||||
'event_id' => 'tpa_no',
|
||||
'claim_amount' => 'claim_amount',
|
||||
'date_of_admission' => 'doa',
|
||||
],
|
||||
'extra_dump' => [
|
||||
'benef_relation' => 'SELF',
|
||||
],
|
||||
],
|
||||
'fhpl' => [
|
||||
'env' => 'FHPL_PRIMARY_KEY_CONSTANT',
|
||||
'table' => 'claims_dump_fhpl',
|
||||
'status_column' => 'current_claim_status',
|
||||
'default_status'=> 'Rejected',
|
||||
'same_status' => 'Settled',
|
||||
'dump_to_ticket'=> [
|
||||
'employee_id' => 'emp_code',
|
||||
'uhid_no' => 'tpa_no',
|
||||
'claim_amount' => 'claim_amount',
|
||||
'admission_date' => 'doa',
|
||||
],
|
||||
'extra_dump' => [
|
||||
'relationship' => 'SELF',
|
||||
],
|
||||
],
|
||||
'rcare' => [
|
||||
'env' => 'R_CARE_PRIMARY_KEY_CONSTANT',
|
||||
'table' => 'claims_dump_reliance',
|
||||
'status_column' => 'final_status',
|
||||
'default_status'=> 'Rejected',
|
||||
'same_status' => 'Settled',
|
||||
'dump_to_ticket'=> [
|
||||
'employee_member_id' => 'emp_code',
|
||||
'uhid' => 'tpa_no',
|
||||
'claimed_amount' => 'claim_amount',
|
||||
'doa_opd_treatment_from' => 'doa',
|
||||
],
|
||||
'extra_dump' => [
|
||||
'relation' => 'SELF',
|
||||
],
|
||||
],
|
||||
'vidal' => [
|
||||
'env' => 'VIDAL_PRIMARY_KEY_CONSTANT',
|
||||
'table' => 'claims_dump_vidal',
|
||||
'status_column' => 'claim_status',
|
||||
'default_status'=> 'Rejected',
|
||||
'same_status' => 'Settled',
|
||||
'dump_to_ticket'=> [
|
||||
'employee_number' => 'emp_code',
|
||||
'primary_policy_holder_card_id' => 'tpa_no',
|
||||
'claim_amount' => 'claim_amount',
|
||||
'date_of_admission' => 'doa',
|
||||
],
|
||||
'extra_dump' => [
|
||||
'relation' => 'SELF',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function run(array $params)
|
||||
{
|
||||
helper('utility_helper');
|
||||
|
||||
$db = db_connect();
|
||||
$apply = $this->hasFlag('apply');
|
||||
$scenario = $this->hasFlag('scenario');
|
||||
$tpaOpt = strtolower((string) ($this->resolveOptionValue('tpa', '') ?: (CLI::getOption('tpa') ?? '')));
|
||||
$fileId = $this->resolveFileId($params);
|
||||
$newStatusOverride = $this->resolveOptionValue('new-status', '');
|
||||
|
||||
$configs = $this->tpaConfigs();
|
||||
|
||||
// Resolve which TPAs to run
|
||||
if ($tpaOpt === '' || $tpaOpt === 'all') {
|
||||
$selected = array_keys($configs);
|
||||
if (!$scenario && $fileId <= 0) {
|
||||
$this->printUsage($configs);
|
||||
return;
|
||||
}
|
||||
} elseif (isset($configs[$tpaOpt])) {
|
||||
$selected = [$tpaOpt];
|
||||
} else {
|
||||
CLI::error("Unknown --tpa={$tpaOpt}. Use: " . implode('|', array_keys($configs)) . '|all');
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($selected as $tpaKey) {
|
||||
CLI::newLine();
|
||||
CLI::write(str_repeat('=', 60), 'yellow');
|
||||
CLI::write('TPA: ' . strtoupper($tpaKey), 'yellow');
|
||||
CLI::write(str_repeat('=', 60), 'yellow');
|
||||
|
||||
try {
|
||||
$this->runForTpa(
|
||||
$db,
|
||||
$tpaKey,
|
||||
$configs[$tpaKey],
|
||||
$scenario,
|
||||
$apply,
|
||||
$fileId,
|
||||
$newStatusOverride
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
CLI::error("[{$tpaKey}] " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function runForTpa(
|
||||
$db,
|
||||
string $tpaKey,
|
||||
array $cfg,
|
||||
bool $scenario,
|
||||
bool $apply,
|
||||
int $fileId,
|
||||
string $newStatusOverride
|
||||
): void {
|
||||
$tpaId = (int) env($cfg['env']);
|
||||
CLI::write("{$cfg['env']} = {$tpaId}", 'cyan');
|
||||
CLI::write("table={$cfg['table']}, status_column={$cfg['status_column']}", 'cyan');
|
||||
|
||||
if (!$this->tableExists($db, $cfg['table'])) {
|
||||
CLI::error("Table {$cfg['table']} does not exist. Skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
$newStatus = $newStatusOverride !== '' ? $newStatusOverride : $cfg['default_status'];
|
||||
$currentFileId = $fileId;
|
||||
|
||||
if ($scenario) {
|
||||
$currentFileId = $this->seedScenario($db, $tpaKey, $cfg, $tpaId, $newStatus);
|
||||
if ($currentFileId <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($currentFileId <= 0) {
|
||||
$this->listRecentFiles($db, $tpaId, $tpaKey);
|
||||
return;
|
||||
}
|
||||
|
||||
$file = $db->table('claim_dump_files')->where('id', $currentFileId)->get()->getRowArray();
|
||||
if (empty($file)) {
|
||||
CLI::error("claim_dump_files id={$currentFileId} not found");
|
||||
return;
|
||||
}
|
||||
|
||||
CLI::write('File: ' . json_encode([
|
||||
'id' => $file['id'],
|
||||
'tpa_id' => $file['tpa_id'],
|
||||
'status' => $file['status'],
|
||||
'file_name' => $file['file_name'],
|
||||
'client_policy_id' => $file['client_policy_id'],
|
||||
]));
|
||||
|
||||
if ((int) $file['tpa_id'] !== $tpaId) {
|
||||
CLI::error("File tpa_id={$file['tpa_id']} does not match {$tpaKey} ({$tpaId}). Skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
$pending = $db->table($cfg['table'])
|
||||
->where('file_id', $currentFileId)
|
||||
->where('is_active', 1)
|
||||
->where('ticket_id IS NULL', null, false)
|
||||
->where('master_reject_reason IS NULL', null, false)
|
||||
->countAllResults();
|
||||
CLI::write("Pending dump rows: {$pending}");
|
||||
|
||||
$service = TpaClaimsImportFactory::make($tpaId);
|
||||
|
||||
if ($apply) {
|
||||
CLI::write('APPLY mode...', 'light_red');
|
||||
|
||||
$beforeDump = $db->table($cfg['table'])
|
||||
->select($this->dumpSelectColumns($cfg))
|
||||
->where('file_id', $currentFileId)
|
||||
->where('is_active', 1)
|
||||
->get()->getResultArray();
|
||||
CLI::write('Dump BEFORE: ' . json_encode($beforeDump, JSON_PRETTY_PRINT));
|
||||
|
||||
$result = $service->runTicketMasterInsert(['file_id' => $currentFileId]);
|
||||
CLI::write('Result: ' . json_encode($result, JSON_PRETTY_PRINT));
|
||||
|
||||
// Mirror TicketServiceController Job 2 success/fail file status update.
|
||||
if (!empty($result['status'])) {
|
||||
$db->table('claim_dump_files')->where('id', $currentFileId)->update([
|
||||
'status' => 'success',
|
||||
'reason' => null,
|
||||
]);
|
||||
CLI::write("claim_dump_files.status => success", 'green');
|
||||
} else {
|
||||
$db->table('claim_dump_files')->where('id', $currentFileId)->update([
|
||||
'status' => 'failed',
|
||||
'reason' => json_encode([
|
||||
'error_summary' => [5 => 1],
|
||||
'error_data' => $result['message'] ?? 'Job 2 failed',
|
||||
]),
|
||||
]);
|
||||
CLI::write("claim_dump_files.status => failed", 'red');
|
||||
}
|
||||
|
||||
$afterDump = $db->table($cfg['table'])
|
||||
->select($this->dumpSelectColumns($cfg))
|
||||
->where('file_id', $currentFileId)
|
||||
->where('is_active', 1)
|
||||
->get()->getResultArray();
|
||||
CLI::write('Dump AFTER: ' . json_encode($afterDump, JSON_PRETTY_PRINT));
|
||||
|
||||
$ticketIds = array_values(array_filter(array_column($afterDump, 'ticket_id')));
|
||||
if (!empty($ticketIds)) {
|
||||
$tickets = $db->table('ticket_master')
|
||||
->select('id, claim_status_id, claim_dump_ref_id, emp_code, tpa_no, claim_amount, doa')
|
||||
->whereIn('id', $ticketIds)
|
||||
->get()->getResultArray();
|
||||
CLI::write('Tickets AFTER: ' . json_encode($tickets, JSON_PRETTY_PRINT));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CLI::write('DRY-RUN mode (mapClaimMasterData only)...', 'green');
|
||||
$ref = new \ReflectionClass($service);
|
||||
$method = $ref->getMethod('mapClaimMasterData');
|
||||
$method->setAccessible(true);
|
||||
$mapped = $method->invoke($service, $currentFileId);
|
||||
|
||||
if (empty($mapped['status'])) {
|
||||
CLI::write('mapClaimMasterData: ' . ($mapped['message'] ?? 'failed'));
|
||||
if (!empty($mapped['already_processed'])) {
|
||||
CLI::write('(already processed — dump rows were NOT deleted)', 'green');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$inserts = $mapped['mapped_array'] ?? [];
|
||||
$rejects = $mapped['rejected_reason_array'] ?? [];
|
||||
$updates = $mapped['status_update_array'] ?? [];
|
||||
|
||||
CLI::write('inserts=' . count($inserts) . ' updates=' . count($updates) . ' rejects=' . count($rejects));
|
||||
CLI::write('status_update_array: ' . (empty($updates) ? '(none)' : json_encode($updates, JSON_PRETTY_PRINT)));
|
||||
CLI::write('rejected_reason_array: ' . json_encode(array_slice($rejects, 0, 10), JSON_PRETTY_PRINT));
|
||||
|
||||
if (!empty($updates)) {
|
||||
$ids = array_column($updates, 'id');
|
||||
$before = $db->table('ticket_master')
|
||||
->select('id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id')
|
||||
->whereIn('id', $ids)
|
||||
->get()->getResultArray();
|
||||
CLI::write('Tickets BEFORE: ' . json_encode($before, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
CLI::write("To apply: php spark tpa:test-status --tpa={$tpaKey} {$currentFileId} --apply", 'green');
|
||||
}
|
||||
|
||||
private function seedScenario($db, string $tpaKey, array $cfg, int $tpaId, string $newStatus): int
|
||||
{
|
||||
CLI::write("Seeding scenario for {$tpaKey} (new_status={$newStatus})...", 'cyan');
|
||||
|
||||
$ticket = $db->table('ticket_master')
|
||||
->select('id, client_id, client_policy_id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, tpa_id')
|
||||
->where('tpa_id', $tpaId)
|
||||
->where('is_active', 1)
|
||||
->where('emp_code IS NOT NULL', null, false)
|
||||
->where('tpa_no IS NOT NULL', null, false)
|
||||
->where('claim_amount IS NOT NULL', null, false)
|
||||
->where('doa IS NOT NULL', null, false)
|
||||
->orderBy('id', 'DESC')
|
||||
->get()->getRowArray();
|
||||
|
||||
if (empty($ticket)) {
|
||||
// Fall back: any ticket with identity keys (still allows match by doa/emp/amount/tpa_no)
|
||||
$ticket = $db->table('ticket_master')
|
||||
->select('id, client_id, client_policy_id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, tpa_id')
|
||||
->where('is_active', 1)
|
||||
->where('emp_code IS NOT NULL', null, false)
|
||||
->where('tpa_no IS NOT NULL', null, false)
|
||||
->where('claim_amount IS NOT NULL', null, false)
|
||||
->where('doa IS NOT NULL', null, false)
|
||||
->orderBy('id', 'DESC')
|
||||
->get()->getRowArray();
|
||||
}
|
||||
|
||||
if (empty($ticket)) {
|
||||
CLI::error("[{$tpaKey}] No suitable existing ticket found to seed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
CLI::write('Using ticket: ' . json_encode([
|
||||
'id' => $ticket['id'],
|
||||
'emp_code' => $ticket['emp_code'],
|
||||
'tpa_no' => $ticket['tpa_no'],
|
||||
'claim_amount' => $ticket['claim_amount'],
|
||||
'doa' => $ticket['doa'],
|
||||
'claim_status_id' => $ticket['claim_status_id'],
|
||||
'claim_dump_ref_id' => $ticket['claim_dump_ref_id'],
|
||||
'tpa_id' => $ticket['tpa_id'],
|
||||
]));
|
||||
|
||||
if (!empty($ticket['claim_dump_ref_id'])) {
|
||||
$db->table('ticket_master')->where('id', $ticket['id'])->update(['claim_dump_ref_id' => null]);
|
||||
CLI::write('Cleared stale ticket.claim_dump_ref_id for retest.', 'yellow');
|
||||
}
|
||||
|
||||
$uploadDir = WRITEPATH . 'uploads' . DIRECTORY_SEPARATOR . 'claim_dump_excel' . DIRECTORY_SEPARATOR;
|
||||
if (!is_dir($uploadDir)) {
|
||||
mkdir($uploadDir, 0775, true);
|
||||
}
|
||||
$fileName = "{$tpaKey}_status_test_" . date('Ymd_His') . '.xlsx';
|
||||
file_put_contents($uploadDir . $fileName, 'placeholder');
|
||||
|
||||
$fileInsert = [
|
||||
'tpa_id' => $tpaId,
|
||||
'client_id' => $ticket['client_id'],
|
||||
'client_policy_id' => $ticket['client_policy_id'],
|
||||
'file_name' => $fileName,
|
||||
'status' => 'pending',
|
||||
'created_by' => 1,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'is_active' => 1,
|
||||
];
|
||||
$fileCols = array_column($db->query('SHOW COLUMNS FROM claim_dump_files')->getResultArray(), 'Field');
|
||||
$fileInsert = array_intersect_key($fileInsert, array_flip($fileCols));
|
||||
$db->table('claim_dump_files')->insert($fileInsert);
|
||||
$fileId = (int) $db->insertID();
|
||||
if ($fileId <= 0) {
|
||||
CLI::error("[{$tpaKey}] Failed to create claim_dump_files row.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
$dumpInsert = [
|
||||
'client_id' => $ticket['client_id'],
|
||||
'client_policy_id' => $ticket['client_policy_id'],
|
||||
'file_id' => $fileId,
|
||||
'is_active' => 1,
|
||||
'created_by' => 1,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'ticket_id' => null,
|
||||
'master_reject_reason' => null,
|
||||
$cfg['status_column'] => $newStatus,
|
||||
];
|
||||
|
||||
foreach ($cfg['dump_to_ticket'] as $dumpCol => $ticketCol) {
|
||||
$dumpInsert[$dumpCol] = $ticket[$ticketCol] ?? null;
|
||||
}
|
||||
foreach ($cfg['extra_dump'] as $col => $val) {
|
||||
$dumpInsert[$col] = $val;
|
||||
}
|
||||
|
||||
$dumpCols = array_column($db->query('SHOW COLUMNS FROM ' . $cfg['table'])->getResultArray(), 'Field');
|
||||
$dumpInsert = array_intersect_key($dumpInsert, array_flip($dumpCols));
|
||||
|
||||
$db->table($cfg['table'])->insert($dumpInsert);
|
||||
$dumpId = (int) $db->insertID();
|
||||
|
||||
CLI::write("Seeded file_id={$fileId}, dump_id={$dumpId}", 'green');
|
||||
return $fileId;
|
||||
}
|
||||
|
||||
private function dumpSelectColumns(array $cfg): string
|
||||
{
|
||||
$cols = ['id', 'ticket_id', 'master_reject_reason', 'file_id', $cfg['status_column']];
|
||||
foreach (array_keys($cfg['dump_to_ticket']) as $col) {
|
||||
$cols[] = $col;
|
||||
}
|
||||
return implode(', ', array_unique($cols));
|
||||
}
|
||||
|
||||
private function listRecentFiles($db, int $tpaId, string $tpaKey): void
|
||||
{
|
||||
$recent = $db->table('claim_dump_files')
|
||||
->select('id, tpa_id, client_id, client_policy_id, file_name, status, created_at')
|
||||
->where('tpa_id', $tpaId)
|
||||
->orderBy('id', 'DESC')
|
||||
->limit(5)
|
||||
->get()->getResultArray();
|
||||
|
||||
CLI::write("Recent {$tpaKey} claim_dump_files:");
|
||||
if (empty($recent)) {
|
||||
CLI::write('(none)');
|
||||
} else {
|
||||
foreach ($recent as $row) {
|
||||
CLI::write(json_encode($row));
|
||||
}
|
||||
}
|
||||
CLI::write("Seed: php spark tpa:test-status --tpa={$tpaKey} --scenario", 'green');
|
||||
CLI::write("Apply: php spark tpa:test-status --tpa={$tpaKey} --scenario --apply", 'green');
|
||||
}
|
||||
|
||||
private function printUsage(array $configs): void
|
||||
{
|
||||
CLI::write('Test existing-ticket status/link update for TPA claim dumps.', 'cyan');
|
||||
CLI::newLine();
|
||||
CLI::write('Dry-run all TPAs (seed scenario, no DB ticket update):');
|
||||
CLI::write(' php spark tpa:test-status --tpa=all --scenario');
|
||||
CLI::newLine();
|
||||
CLI::write('Apply all TPAs (writes to DB):');
|
||||
CLI::write(' php spark tpa:test-status --tpa=all --scenario --apply');
|
||||
CLI::newLine();
|
||||
CLI::write('Single TPA:');
|
||||
foreach (array_keys($configs) as $key) {
|
||||
CLI::write(" php spark tpa:test-status --tpa={$key} --scenario");
|
||||
CLI::write(" php spark tpa:test-status --tpa={$key} --scenario --apply");
|
||||
}
|
||||
CLI::newLine();
|
||||
CLI::write('Same-status (no status change, only null-link fill):');
|
||||
CLI::write(' php spark tpa:test-status --tpa=abhi --scenario --new-status Settled');
|
||||
}
|
||||
|
||||
private function tableExists($db, string $table): bool
|
||||
{
|
||||
return !empty($db->query("SHOW TABLES LIKE " . $db->escape($table))->getResultArray());
|
||||
}
|
||||
|
||||
private function resolveFileId(array $params): int
|
||||
{
|
||||
if (!empty($params['file_id'])) {
|
||||
return (int) $params['file_id'];
|
||||
}
|
||||
if (!empty($params[0]) && is_numeric($params[0])) {
|
||||
return (int) $params[0];
|
||||
}
|
||||
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
foreach ($argv as $i => $arg) {
|
||||
if (preg_match('/^--file[_-]id=(.+)$/', (string) $arg, $m)) {
|
||||
return (int) $m[1];
|
||||
}
|
||||
if (in_array($arg, ['--file_id', '--file-id'], true) && isset($argv[$i + 1])) {
|
||||
return (int) $argv[$i + 1];
|
||||
}
|
||||
// Bare numeric arg (e.g. `tpa:test-status --tpa=icici 70 --apply`)
|
||||
if (is_numeric($arg) && (int) $arg > 0 && $i > 0 && strpos((string) $argv[$i - 1], 'spark') === false) {
|
||||
$prev = (string) ($argv[$i - 1] ?? '');
|
||||
// Skip values belonging to options like --tpa=... or --new-status STATUS
|
||||
if ($prev === '--tpa' || $prev === '--new-status' || $prev === '--file_id' || $prev === '--file-id') {
|
||||
continue;
|
||||
}
|
||||
return (int) $arg;
|
||||
}
|
||||
}
|
||||
|
||||
return (int) (CLI::getOption('file_id') ?? CLI::getOption('file-id') ?? 0);
|
||||
}
|
||||
|
||||
private function hasFlag(string $name): bool
|
||||
{
|
||||
if (CLI::getOption($name) !== null) {
|
||||
return true;
|
||||
}
|
||||
return in_array('--' . $name, $_SERVER['argv'] ?? [], true);
|
||||
}
|
||||
|
||||
private function resolveOptionValue(string $name, string $default): string
|
||||
{
|
||||
$opt = CLI::getOption($name);
|
||||
if (is_string($opt) && $opt !== '') {
|
||||
return $opt;
|
||||
}
|
||||
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
foreach ($argv as $i => $arg) {
|
||||
if (preg_match('/^--' . preg_quote($name, '/') . '=(.+)$/', (string) $arg, $m)) {
|
||||
return trim($m[1]);
|
||||
}
|
||||
if ($arg === '--' . $name && isset($argv[$i + 1]) && strpos((string) $argv[$i + 1], '--') !== 0) {
|
||||
return (string) $argv[$i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
15
app/Common.php
Executable file
15
app/Common.php
Executable file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* The goal of this file is to allow developers a location
|
||||
* where they can overwrite core procedural functions and
|
||||
* replace them with their own. This file is loaded during
|
||||
* the bootstrap process and is called during the framework's
|
||||
* execution.
|
||||
*
|
||||
* This can be looked at as a `master helper` file that is
|
||||
* loaded early on, and may also contain additional functions
|
||||
* that you'd like to use throughout your entire application
|
||||
*
|
||||
* @see: https://codeigniter.com/user_guide/extending/common.html
|
||||
*/
|
||||
339
app/Config/Acl.php
Normal file
339
app/Config/Acl.php
Normal file
@ -0,0 +1,339 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
class Acl
|
||||
{
|
||||
public array $rules = [
|
||||
|
||||
// ===================== PUBLIC / AUTH =====================
|
||||
'#^/login#' => ['public' => true],
|
||||
'#^/downloadCdSplitUpFile#' => ['public' => true],
|
||||
'#^/logout#' => ['public' => true],
|
||||
'#^/auth#' => ['public' => true],
|
||||
'#^/oauth2callback#' => ['public' => true],
|
||||
'#^/loginPos#' => ['public' => true],
|
||||
'#^/getVerifyPosMobileNo#' => ['public' => true],
|
||||
'#^/getVerifiedPosUserData#' => ['public' => true],
|
||||
'#^/chat#' => ['public' => true],
|
||||
'#^/widget#' => ['public' => true],
|
||||
'#^/chatbottest#' => ['public' => true],
|
||||
'#^/chatbot#' => ['public' => true],
|
||||
'#^/getSSORedirectUrl#' => ['public' => true],
|
||||
'#^/getEmployeePolicy#' => ['public' => true],
|
||||
'#^/downloadFileTableFile#' => ['public' => true],
|
||||
'#^/storage/file-download#' => ['public' => true],
|
||||
'#^/fileDownload#' => ['public' => true],
|
||||
'#^/downloadClaimFile#' => ['public' => true],
|
||||
'#^/swagger#' => ['roles' => [ADMIN_ROLE_ID]],
|
||||
'#^/getEmployeeActiveOrInactivePolicy#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/test/chartbrewDashboardDemo#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/bulkEcardDownloadAsZip#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/sheet#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/sendextraparam#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/test/testingquerys#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/test/viewrfq#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/fedeploy#' => ['roles' => [ADMIN_ROLE_ID]],
|
||||
'#^/storage/files#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/visitOffBoardCheck#' => ['roles' => [ADMIN_ROLE_ID]],
|
||||
'#^/logs#' => ['roles' => [ADMIN_ROLE_ID]],
|
||||
'#^/util/log_list#' => ['roles' => [ADMIN_ROLE_ID]],
|
||||
'#^/util/view_log#' => ['roles' => [ADMIN_ROLE_ID]],
|
||||
'#^/util/download_log#' => ['roles' => [ADMIN_ROLE_ID]],
|
||||
'#^/metaDashboardDemo#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/metaTpaDashboardDemo#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/sales/dashboard#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, STAFF_ROLE_ID]],
|
||||
'#^/sales#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, STAFF_ROLE_ID]],
|
||||
'#^/expense#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, STAFF_ROLE_ID]],
|
||||
'#^/sendDataToTPA#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID, MANAGER_ROLE_ID]],
|
||||
'#^/swagger#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID, MANAGER_ROLE_ID]],
|
||||
'#^/viewClaimFile#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID]],
|
||||
'#^/employee/tpaReportsDashboard#' => ['roles' => [ACCOUNT_MANAGER_ROLE_ID, ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
'#^/policy_tranction/sendInstallmentRemainderMail#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID]],
|
||||
|
||||
|
||||
|
||||
// ===================== PUBLIC DOWNLOADS / FORMS =====================
|
||||
'#^/download-#' => ['public' => true],
|
||||
'#^/claim-form-download#' => ['public' => true],
|
||||
'#^/claims-feedback-form#' => ['public' => true],
|
||||
'#^/autobookstackLogin#' => ['public' => true],
|
||||
|
||||
// ===================== DASHBOARD =====================
|
||||
'#^/dashboard#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
|
||||
// ===================== VIEW INCEPTION PAGE =====================
|
||||
'#^/hrFileList#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/hrFileDownload#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/sendPushNotification#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/sendEmail#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/getPolicyLevelEmployeeSummaryData#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/cdTransactionData#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/claimsSearch#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/getBackToEnrolledDetails#' => [
|
||||
'roles' => [ ADMIN_ROLE_ID,HEAD_ROLE_ID, MANAGER_ROLE_ID, STAFF_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
|
||||
// ===================== USER MANAGEMENT =====================
|
||||
'#^/user#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
|
||||
// ===================== CLIENT =====================
|
||||
'#^/client#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
|
||||
// ===================== EMPLOYEE / ENROLLMENT =====================
|
||||
'#^/employee#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [ENROLLMENT_TEAM_ID]
|
||||
],
|
||||
|
||||
// ===================== MASTERS =====================
|
||||
'#^/master#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/util#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
|
||||
// ===================== DIGIT MOTOR =====================
|
||||
'#^/digit-motor#' => [
|
||||
'roles' => [ HEAD_ROLE_ID, ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID, STAFF_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID, POS_TEAM_ID, BUSINESS_TEAM_ID, MANAGEMENT_TEAM_ID]
|
||||
],
|
||||
|
||||
// ===================== POLICY TRANSACTION / BDS =====================
|
||||
'#^/policy_tranction#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
'#^/dmsSearch#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
'#^/downloadGdriveFile#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
'#^/driveListFiles#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
'#^/bds_upload#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
|
||||
// ===================== REPORTS =====================
|
||||
'#^/bdsReport#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
|
||||
// ===================== PAYOUT / COMMISSION =====================
|
||||
'#^/payout#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
'#^/commission#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
'#^/importRules#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [FINANCE_TEAM_ID,POS_TEAM_ID]
|
||||
],
|
||||
|
||||
// ===================== CLAIMS / TICKETS =====================
|
||||
'#^/ticket#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketSave#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketList#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketConversationSave#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketConversationList#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketType#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketHistoryList#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/ticketAutoFetchDetails#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/initiateClaim#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/non-eb-claim#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/departments#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/get_ticket_data#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/claimView#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
'#^/claim_mis#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [CLAIMS_TEAM_ID]
|
||||
],
|
||||
|
||||
// ===================== LEADS / SALES =====================
|
||||
'#^/leads#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [BUSINESS_SUPPORT_TEAM_ID, SALES_TEAM_ID]
|
||||
],
|
||||
'#^/rfq#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID],
|
||||
'teams' => [BUSINESS_SUPPORT_TEAM_ID, SALES_TEAM_ID]
|
||||
],
|
||||
'#^/sales#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID],
|
||||
'teams' => [BUSINESS_SUPPORT_TEAM_ID, SALES_TEAM_ID]
|
||||
],
|
||||
|
||||
// ===================== CMS / CONTENT =====================
|
||||
'#^/add_image_index#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/add_advertise_image#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/remove_advertise_image#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/getAdvertiseImage#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/showAdvertiseImage#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/frontend_content#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/FAQ#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
'#^/getFEContent#' => [
|
||||
'roles' => [ HEAD_ROLE_ID,ADMIN_ROLE_ID, MANAGER_ROLE_ID, ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
|
||||
// ===================== LOGS =====================
|
||||
'#^/logs#' => [
|
||||
'roles' => [ADMIN_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
|
||||
// ===================== RATE LIMIT ADMIN =====================
|
||||
'#^/security/rate-limits#' => [
|
||||
'roles' => [ADMIN_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
|
||||
// ===================== INTERNAL TEST =====================
|
||||
'#^/test#' => [
|
||||
'roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
|
||||
// ===================== API (JWT / SIGNED) =====================
|
||||
'#^/api#' => ['public' => true],
|
||||
'#^/employeeRest#' => ['public' => true],
|
||||
'#^/clientApi#' => ['public' => true],
|
||||
|
||||
// ===================== WEBHOOKS / 3RD PARTY =====================
|
||||
'#^/dispatchWebhookData#' => ['public' => true],
|
||||
'#^/retrieveWebhookData#' => ['public' => true],
|
||||
'#^/ecardRequest#' => ['public' => true],
|
||||
'#^/getTPAID#' => ['public' => true],
|
||||
'#^/fileDownload#' => ['public' => true],
|
||||
'#^/generateAuthToken#' => ['public' => true],
|
||||
'#^/createEnrollmentBatch#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID], 'teams' => [ENROLLMENT_TEAM_ID]],
|
||||
'#^/getEnrollmentBatchStatus#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID], 'teams' => [ENROLLMENT_TEAM_ID]],
|
||||
'#^/fetchUHIDDetails#' => ['roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID], 'teams' => [ENROLLMENT_TEAM_ID]],
|
||||
'#^/FhplGetBenefDetails#' => ['roles' => [ADMIN_ROLE_ID]],
|
||||
'#^/EcardRequest#' => ['public' => true],
|
||||
'#^/HospitalNetwork#' => ['public' => true],
|
||||
'#^/VidalGetBenefDetails#' => ['public' => true],
|
||||
'#^/ClaimDetail#' => ['public' => true],
|
||||
'#^/SubmitClaim#' => ['roles' => [ADMIN_ROLE_ID,HEAD_ROLE_ID,ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID]],
|
||||
'#^/IntimateClaim#' => ['roles' => [ADMIN_ROLE_ID,HEAD_ROLE_ID,ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID]],
|
||||
'#^/IRSubmission#' => ['roles' => [ADMIN_ROLE_ID,HEAD_ROLE_ID,ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID]],
|
||||
'#^/ClaimStatusUpdate#' => ['roles' => [ADMIN_ROLE_ID,HEAD_ROLE_ID,ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID]],
|
||||
'#^/syncTpaClaimToNhance#' => ['roles' => [ADMIN_ROLE_ID,HEAD_ROLE_ID,ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID]],
|
||||
'#^/HealthIndiaGetBenefDetails#' => ['roles' => [ADMIN_ROLE_ID,HEAD_ROLE_ID,ACCOUNT_MANAGER_ROLE_ID,STAFF_ROLE_ID]],
|
||||
'#^/testTracelog#' => ['roles' => [ADMIN_ROLE_ID]],
|
||||
|
||||
// ===================== CLI =====================
|
||||
'#^/cli/#' => ['public' => true],
|
||||
|
||||
// ===================== DEFAULT DENY (ZERO TRUST) =====================
|
||||
'#^/#' => [
|
||||
'roles' => [ADMIN_ROLE_ID, HEAD_ROLE_ID],
|
||||
'teams' => []
|
||||
],
|
||||
];
|
||||
}
|
||||
180
app/Config/App.php
Executable file
180
app/Config/App.php
Executable file
@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class App extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Base Site URL
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* URL to your CodeIgniter root. Typically, this will be your base URL,
|
||||
* WITH a trailing slash:
|
||||
*
|
||||
* http://example.com/
|
||||
*/
|
||||
public string $baseURL = 'http://localhost/nhance/';
|
||||
|
||||
/**
|
||||
* Allowed Hostnames in the Site URL other than the hostname in the baseURL.
|
||||
* If you want to accept multiple Hostnames, set this.
|
||||
*
|
||||
* E.g. When your site URL ($baseURL) is 'http://example.com/', and your site
|
||||
* also accepts 'http://media.example.com/' and
|
||||
* 'http://accounts.example.com/':
|
||||
* ['media.example.com', 'accounts.example.com']
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
public array $allowedHostnames = [];
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Index File
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Typically this will be your index.php file, unless you've renamed it to
|
||||
* something else. If you are using mod_rewrite to remove the page set this
|
||||
* variable so that it is blank.
|
||||
*/
|
||||
public string $indexPage = 'index.php';
|
||||
|
||||
|
||||
public $compressOutput = true;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* URI PROTOCOL
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* This item determines which server global should be used to retrieve the
|
||||
* URI string. The default setting of 'REQUEST_URI' works for most servers.
|
||||
* If your links do not seem to work, try one of the other delicious flavors:
|
||||
*
|
||||
* 'REQUEST_URI' Uses $_SERVER['REQUEST_URI']
|
||||
* 'QUERY_STRING' Uses $_SERVER['QUERY_STRING']
|
||||
* 'PATH_INFO' Uses $_SERVER['PATH_INFO']
|
||||
*
|
||||
* WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded!
|
||||
*/
|
||||
public string $uriProtocol = 'REQUEST_URI';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Default Locale
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The Locale roughly represents the language and location that your visitor
|
||||
* is viewing the site from. It affects the language strings and other
|
||||
* strings (like currency markers, numbers, etc), that your program
|
||||
* should run under for this request.
|
||||
*/
|
||||
public string $defaultLocale = 'en';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Negotiate Locale
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* If true, the current Request object will automatically determine the
|
||||
* language to use based on the value of the Accept-Language header.
|
||||
*
|
||||
* If false, no automatic detection will be performed.
|
||||
*/
|
||||
public bool $negotiateLocale = false;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Supported Locales
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* If $negotiateLocale is true, this array lists the locales supported
|
||||
* by the application in descending order of priority. If no match is
|
||||
* found, the first locale will be used.
|
||||
*
|
||||
* IncomingRequest::setLocale() also uses this list.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public array $supportedLocales = ['en'];
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Application Timezone
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The default timezone that will be used in your application to display
|
||||
* dates with the date helper, and can be retrieved through app_timezone()
|
||||
*
|
||||
* @see https://www.php.net/manual/en/timezones.php for list of timezones supported by PHP.
|
||||
*/
|
||||
public string $appTimezone = 'UTC';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Default Character Set
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* This determines which character set is used by default in various methods
|
||||
* that require a character set to be provided.
|
||||
*
|
||||
* @see http://php.net/htmlspecialchars for a list of supported charsets.
|
||||
*/
|
||||
public string $charset = 'UTF-8';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Force Global Secure Requests
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* If true, this will force every request made to this application to be
|
||||
* made via a secure connection (HTTPS). If the incoming request is not
|
||||
* secure, the user will be redirected to a secure version of the page
|
||||
* and the HTTP Strict Transport Security header will be set.
|
||||
*/
|
||||
public bool $forceGlobalSecureRequests = false;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Reverse Proxy IPs
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* If your server is behind a reverse proxy, you must whitelist the proxy
|
||||
* IP addresses from which CodeIgniter should trust headers such as
|
||||
* X-Forwarded-For or Client-IP in order to properly identify
|
||||
* the visitor's IP address.
|
||||
*
|
||||
* You need to set a proxy IP address or IP address with subnets and
|
||||
* the HTTP header for the client IP address.
|
||||
*
|
||||
* Here are some examples:
|
||||
* [
|
||||
* '10.0.1.200' => 'X-Forwarded-For',
|
||||
* '192.168.5.0/24' => 'X-Real-IP',
|
||||
* ]
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $proxyIPs = [];
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Content Security Policy
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Enables the Response's Content Secure Policy to restrict the sources that
|
||||
* can be used for images, scripts, CSS files, audio, video, etc. If enabled,
|
||||
* the Response object will populate default values for the policy from the
|
||||
* `ContentSecurityPolicy.php` file. Controllers can always add to those
|
||||
* restrictions at run time.
|
||||
*
|
||||
* For a better understanding of CSP, see these documents:
|
||||
*
|
||||
* @see http://www.html5rocks.com/en/tutorials/security/content-security-policy/
|
||||
* @see http://www.w3.org/TR/CSP/
|
||||
*/
|
||||
public bool $CSPEnabled = false;
|
||||
}
|
||||
106
app/Config/Autoload.php
Executable file
106
app/Config/Autoload.php
Executable file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\AutoloadConfig;
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------
|
||||
* AUTOLOADER CONFIGURATION
|
||||
* -------------------------------------------------------------------
|
||||
*
|
||||
* This file defines the namespaces and class maps so the Autoloader
|
||||
* can find the files as needed.
|
||||
*
|
||||
* NOTE: If you use an identical key in $psr4 or $classmap, then
|
||||
* the values in this file will overwrite the framework's values.
|
||||
*
|
||||
* NOTE: This class is required prior to Autoloader instantiation,
|
||||
* and does not extend BaseConfig.
|
||||
*
|
||||
* @immutable
|
||||
*/
|
||||
class Autoload extends AutoloadConfig
|
||||
{
|
||||
/**
|
||||
* -------------------------------------------------------------------
|
||||
* Namespaces
|
||||
* -------------------------------------------------------------------
|
||||
* This maps the locations of any namespaces in your application to
|
||||
* their location on the file system. These are used by the autoloader
|
||||
* to locate files the first time they have been instantiated.
|
||||
*
|
||||
* The '/app' and '/system' directories are already mapped for you.
|
||||
* you may change the name of the 'App' namespace if you wish,
|
||||
* but this should be done prior to creating any namespaced classes,
|
||||
* else you will need to modify all of those classes for this to work.
|
||||
*
|
||||
* Prototype:
|
||||
* $psr4 = [
|
||||
* 'CodeIgniter' => SYSTEMPATH,
|
||||
* 'App' => APPPATH
|
||||
* ];
|
||||
*
|
||||
* @var array<string, array<int, string>|string>
|
||||
* @phpstan-var array<string, string|list<string>>
|
||||
*/
|
||||
public $psr4 = [
|
||||
APP_NAMESPACE => APPPATH, // For custom app namespace
|
||||
'Config' => APPPATH . 'Config',
|
||||
'Helpers' => APPPATH . 'Helpers',
|
||||
'App\\Libraries' => APPPATH . 'Libraries',
|
||||
];
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------
|
||||
* Class Map
|
||||
* -------------------------------------------------------------------
|
||||
* The class map provides a map of class names and their exact
|
||||
* location on the drive. Classes loaded in this manner will have
|
||||
* slightly faster performance because they will not have to be
|
||||
* searched for within one or more directories as they would if they
|
||||
* were being autoloaded through a namespace.
|
||||
*
|
||||
* Prototype:
|
||||
* $classmap = [
|
||||
* 'MyClass' => '/path/to/class/file.php'
|
||||
* ];
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $classmap = [];
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------
|
||||
* Files
|
||||
* -------------------------------------------------------------------
|
||||
* The files array provides a list of paths to __non-class__ files
|
||||
* that will be autoloaded. This can be useful for bootstrap operations
|
||||
* or for loading functions.
|
||||
*
|
||||
* Prototype:
|
||||
* $files = [
|
||||
* '/path/to/my/file.php',
|
||||
* ];
|
||||
*
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
public $files = [];
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------
|
||||
* Helpers
|
||||
* -------------------------------------------------------------------
|
||||
* Prototype:
|
||||
* $helpers = [
|
||||
* 'form',
|
||||
* ];
|
||||
*
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
public $helpers = ['uuid','session','utility', 'form', 'url', 'oauth', 'fileupload',
|
||||
'excel_import_export', 'file', 'drive','ExcelSanitizeHelper', 'api_helper','exception','sms_helper','sanitizeInputArrayAdvanced'
|
||||
];
|
||||
}
|
||||
64
app/Config/BdsConfig.php
Normal file
64
app/Config/BdsConfig.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class BdsConfig extends BaseConfig
|
||||
{
|
||||
public bool $installmentReminderEnabled = false;
|
||||
|
||||
public int $installmentReminderBusinessDays = 5;
|
||||
|
||||
public bool $installmentReminderFetchOverduePendingUtr = false;
|
||||
|
||||
public bool $installmentReminderDaily = false;
|
||||
|
||||
/** @var string[] Lowercase three-letter day abbreviations, e.g. mon, tue */
|
||||
public array $installmentReminderDays = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->installmentReminderEnabled = $this->envToBool(
|
||||
env('bds.installmentReminder.enabled', 'true')
|
||||
);
|
||||
$this->installmentReminderBusinessDays = max(
|
||||
0,
|
||||
(int) env('bds.installmentReminder.businessDays', 5)
|
||||
);
|
||||
$this->installmentReminderFetchOverduePendingUtr = $this->envToBool(
|
||||
env('bds.installmentReminder.fetchOverduePendingUtr', 'false')
|
||||
);
|
||||
$this->installmentReminderDaily = $this->envToBool(
|
||||
env('bds.installmentReminder.daily', 'true')
|
||||
);
|
||||
|
||||
$days = env('bds.installmentReminder.days', 'mon,tue,wed,thu,fri');
|
||||
$this->installmentReminderDays = array_values(array_filter(array_map(
|
||||
static fn (string $day): string => strtolower(substr(trim($day), 0, 3)),
|
||||
explode(',', (string) $days)
|
||||
)));
|
||||
}
|
||||
|
||||
public function shouldFetchInstallmentRemindersToday(): bool
|
||||
{
|
||||
if (!$this->installmentReminderEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->installmentReminderDaily) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$today = strtolower(date('D'));
|
||||
|
||||
return in_array($today, $this->installmentReminderDays, true);
|
||||
}
|
||||
|
||||
private function envToBool(mixed $value): bool
|
||||
{
|
||||
return in_array(strtolower((string) $value), ['1', 'true', 'yes', 'on'], true);
|
||||
}
|
||||
}
|
||||
34
app/Config/Boot/development.php
Executable file
34
app/Config/Boot/development.php
Executable file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| ERROR DISPLAY
|
||||
|--------------------------------------------------------------------------
|
||||
| In development, we want to show as many errors as possible to help
|
||||
| make sure they don't make it to production. And save us hours of
|
||||
| painful debugging.
|
||||
|
|
||||
| If you set 'display_errors' to '1', CI4's detailed error report will show.
|
||||
*/
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '1');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DEBUG BACKTRACES
|
||||
|--------------------------------------------------------------------------
|
||||
| If true, this constant will tell the error screens to display debug
|
||||
| backtraces along with the other error information. If you would
|
||||
| prefer to not see this, set this value to false.
|
||||
*/
|
||||
defined('SHOW_DEBUG_BACKTRACE') || define('SHOW_DEBUG_BACKTRACE', true);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DEBUG MODE
|
||||
|--------------------------------------------------------------------------
|
||||
| Debug mode is an experimental flag that can allow changes throughout
|
||||
| the system. This will control whether Kint is loaded, and a few other
|
||||
| items. It can always be used within your own application too.
|
||||
*/
|
||||
defined('CI_DEBUG') || define('CI_DEBUG', true);
|
||||
23
app/Config/Boot/production.php
Executable file
23
app/Config/Boot/production.php
Executable file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| ERROR DISPLAY
|
||||
|--------------------------------------------------------------------------
|
||||
| Don't show ANY in production environments. Instead, let the system catch
|
||||
| it and display a generic error message.
|
||||
|
|
||||
| If you set 'display_errors' to '1', CI4's detailed error report will show.
|
||||
*/
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DEBUG MODE
|
||||
|--------------------------------------------------------------------------
|
||||
| Debug mode is an experimental flag that can allow changes throughout
|
||||
| the system. It's not widely used currently, and may not survive
|
||||
| release of the framework.
|
||||
*/
|
||||
defined('CI_DEBUG') || define('CI_DEBUG', false);
|
||||
38
app/Config/Boot/test.php
Executable file
38
app/Config/Boot/test.php
Executable file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* The environment testing is reserved for PHPUnit testing. It has special
|
||||
* conditions built into the framework at various places to assist with that.
|
||||
* You can’t use it for your development.
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| ERROR DISPLAY
|
||||
|--------------------------------------------------------------------------
|
||||
| In development, we want to show as many errors as possible to help
|
||||
| make sure they don't make it to production. And save us hours of
|
||||
| painful debugging.
|
||||
*/
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '1');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DEBUG BACKTRACES
|
||||
|--------------------------------------------------------------------------
|
||||
| If true, this constant will tell the error screens to display debug
|
||||
| backtraces along with the other error information. If you would
|
||||
| prefer to not see this, set this value to false.
|
||||
*/
|
||||
defined('SHOW_DEBUG_BACKTRACE') || define('SHOW_DEBUG_BACKTRACE', true);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DEBUG MODE
|
||||
|--------------------------------------------------------------------------
|
||||
| Debug mode is an experimental flag that can allow changes throughout
|
||||
| the system. It's not widely used currently, and may not survive
|
||||
| release of the framework.
|
||||
*/
|
||||
defined('CI_DEBUG') || define('CI_DEBUG', true);
|
||||
38
app/Config/Boot/testing.php
Executable file
38
app/Config/Boot/testing.php
Executable file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* The environment testing is reserved for PHPUnit testing. It has special
|
||||
* conditions built into the framework at various places to assist with that.
|
||||
* You can’t use it for your development.
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| ERROR DISPLAY
|
||||
|--------------------------------------------------------------------------
|
||||
| In development, we want to show as many errors as possible to help
|
||||
| make sure they don't make it to production. And save us hours of
|
||||
| painful debugging.
|
||||
*/
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '1');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DEBUG BACKTRACES
|
||||
|--------------------------------------------------------------------------
|
||||
| If true, this constant will tell the error screens to display debug
|
||||
| backtraces along with the other error information. If you would
|
||||
| prefer to not see this, set this value to false.
|
||||
*/
|
||||
defined('SHOW_DEBUG_BACKTRACE') || define('SHOW_DEBUG_BACKTRACE', true);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DEBUG MODE
|
||||
|--------------------------------------------------------------------------
|
||||
| Debug mode is an experimental flag that can allow changes throughout
|
||||
| the system. It's not widely used currently, and may not survive
|
||||
| release of the framework.
|
||||
*/
|
||||
defined('CI_DEBUG') || define('CI_DEBUG', true);
|
||||
38
app/Config/Boot/uat.php
Executable file
38
app/Config/Boot/uat.php
Executable file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* The environment testing is reserved for PHPUnit testing. It has special
|
||||
* conditions built into the framework at various places to assist with that.
|
||||
* You can’t use it for your development.
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| ERROR DISPLAY
|
||||
|--------------------------------------------------------------------------
|
||||
| In development, we want to show as many errors as possible to help
|
||||
| make sure they don't make it to production. And save us hours of
|
||||
| painful debugging.
|
||||
*/
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '1');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DEBUG BACKTRACES
|
||||
|--------------------------------------------------------------------------
|
||||
| If true, this constant will tell the error screens to display debug
|
||||
| backtraces along with the other error information. If you would
|
||||
| prefer to not see this, set this value to false.
|
||||
*/
|
||||
defined('SHOW_DEBUG_BACKTRACE') || define('SHOW_DEBUG_BACKTRACE', true);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DEBUG MODE
|
||||
|--------------------------------------------------------------------------
|
||||
| Debug mode is an experimental flag that can allow changes throughout
|
||||
| the system. It's not widely used currently, and may not survive
|
||||
| release of the framework.
|
||||
*/
|
||||
defined('CI_DEBUG') || define('CI_DEBUG', true);
|
||||
20
app/Config/CURLRequest.php
Executable file
20
app/Config/CURLRequest.php
Executable file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class CURLRequest extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* CURLRequest Share Options
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Whether share options between requests or not.
|
||||
*
|
||||
* If true, all the options won't be reset between requests.
|
||||
* It may cause an error request with unnecessary headers.
|
||||
*/
|
||||
public bool $shareOptions = false;
|
||||
}
|
||||
171
app/Config/Cache.php
Executable file
171
app/Config/Cache.php
Executable file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Cache\CacheInterface;
|
||||
use CodeIgniter\Cache\Handlers\DummyHandler;
|
||||
use CodeIgniter\Cache\Handlers\FileHandler;
|
||||
use CodeIgniter\Cache\Handlers\MemcachedHandler;
|
||||
use CodeIgniter\Cache\Handlers\PredisHandler;
|
||||
use CodeIgniter\Cache\Handlers\RedisHandler;
|
||||
use CodeIgniter\Cache\Handlers\WincacheHandler;
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class Cache extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Primary Handler
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The name of the preferred handler that should be used. If for some reason
|
||||
* it is not available, the $backupHandler will be used in its place.
|
||||
*/
|
||||
public string $handler = 'file';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Backup Handler
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The name of the handler that will be used in case the first one is
|
||||
* unreachable. Often, 'file' is used here since the filesystem is
|
||||
* always available, though that's not always practical for the app.
|
||||
*/
|
||||
public string $backupHandler = 'dummy';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Cache Directory Path
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The path to where cache files should be stored, if using a file-based
|
||||
* system.
|
||||
*
|
||||
* @deprecated Use the driver-specific variant under $file
|
||||
*/
|
||||
public string $storePath = WRITEPATH . 'cache/';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Cache Include Query String
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Whether to take the URL query string into consideration when generating
|
||||
* output cache files. Valid options are:
|
||||
*
|
||||
* false = Disabled
|
||||
* true = Enabled, take all query parameters into account.
|
||||
* Please be aware that this may result in numerous cache
|
||||
* files generated for the same page over and over again.
|
||||
* ['q'] = Enabled, but only take into account the specified list
|
||||
* of query parameters.
|
||||
*
|
||||
* @var bool|string[]
|
||||
*/
|
||||
public $cacheQueryString = false;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Key Prefix
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* This string is added to all cache item names to help avoid collisions
|
||||
* if you run multiple applications with the same cache engine.
|
||||
*/
|
||||
public string $prefix = '';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Default TTL
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The default number of seconds to save items when none is specified.
|
||||
*
|
||||
* WARNING: This is not used by framework handlers where 60 seconds is
|
||||
* hard-coded, but may be useful to projects and modules. This will replace
|
||||
* the hard-coded value in a future release.
|
||||
*/
|
||||
public int $ttl = 60;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Reserved Characters
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* A string of reserved characters that will not be allowed in keys or tags.
|
||||
* Strings that violate this restriction will cause handlers to throw.
|
||||
* Default: {}()/\@:
|
||||
*
|
||||
* NOTE: The default set is required for PSR-6 compliance.
|
||||
*/
|
||||
public string $reservedCharacters = '{}()/\@:';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* File settings
|
||||
* --------------------------------------------------------------------------
|
||||
* Your file storage preferences can be specified below, if you are using
|
||||
* the File driver.
|
||||
*
|
||||
* @var array<string, int|string|null>
|
||||
*/
|
||||
public array $file = [
|
||||
'storePath' => WRITEPATH . 'cache/',
|
||||
'mode' => 0640,
|
||||
];
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------------
|
||||
* Memcached settings
|
||||
* -------------------------------------------------------------------------
|
||||
* Your Memcached servers can be specified below, if you are using
|
||||
* the Memcached drivers.
|
||||
*
|
||||
* @see https://codeigniter.com/user_guide/libraries/caching.html#memcached
|
||||
*
|
||||
* @var array<string, bool|int|string>
|
||||
*/
|
||||
public array $memcached = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 11211,
|
||||
'weight' => 1,
|
||||
'raw' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------------
|
||||
* Redis settings
|
||||
* -------------------------------------------------------------------------
|
||||
* Your Redis server can be specified below, if you are using
|
||||
* the Redis or Predis drivers.
|
||||
*
|
||||
* @var array<string, int|string|null>
|
||||
*/
|
||||
public array $redis = [
|
||||
'host' => '127.0.0.1',
|
||||
'password' => null,
|
||||
'port' => 6379,
|
||||
'timeout' => 0,
|
||||
'database' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Available Cache Handlers
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* This is an array of cache engine alias' and class names. Only engines
|
||||
* that are listed here are allowed to be used.
|
||||
*
|
||||
* @var array<string, class-string<CacheInterface>>
|
||||
*/
|
||||
public array $validHandlers = [
|
||||
'dummy' => DummyHandler::class,
|
||||
'file' => FileHandler::class,
|
||||
'memcached' => MemcachedHandler::class,
|
||||
'predis' => PredisHandler::class,
|
||||
'redis' => RedisHandler::class,
|
||||
'wincache' => WincacheHandler::class,
|
||||
];
|
||||
}
|
||||
137
app/Config/Constants.php
Executable file
137
app/Config/Constants.php
Executable file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
| --------------------------------------------------------------------
|
||||
| App Namespace
|
||||
| --------------------------------------------------------------------
|
||||
|
|
||||
| This defines the default Namespace that is used throughout
|
||||
| CodeIgniter to refer to the Application directory. Change
|
||||
| this constant to change the namespace that all application
|
||||
| classes should use.
|
||||
|
|
||||
| NOTE: changing this will require manually modifying the
|
||||
| existing namespaces of App\* namespaced-classes.
|
||||
*/
|
||||
defined('APP_NAMESPACE') || define('APP_NAMESPACE', 'App');
|
||||
|
||||
/*
|
||||
| --------------------------------------------------------------------------
|
||||
| Composer Path
|
||||
| --------------------------------------------------------------------------
|
||||
|
|
||||
| The path that Composer's autoload file is expected to live. By default,
|
||||
| the vendor folder is in the Root directory, but you can customize that here.
|
||||
*/
|
||||
defined('COMPOSER_PATH') || define('COMPOSER_PATH', ROOTPATH . 'vendor/autoload.php');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Timing Constants
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Provide simple ways to work with the myriad of PHP functions that
|
||||
| require information to be in seconds.
|
||||
*/
|
||||
defined('SECOND') || define('SECOND', 1);
|
||||
defined('MINUTE') || define('MINUTE', 60);
|
||||
defined('HOUR') || define('HOUR', 3600);
|
||||
defined('DAY') || define('DAY', 86400);
|
||||
defined('WEEK') || define('WEEK', 604800);
|
||||
defined('MONTH') || define('MONTH', 2_592_000);
|
||||
defined('YEAR') || define('YEAR', 31_536_000);
|
||||
defined('DECADE') || define('DECADE', 315_360_000);
|
||||
|
||||
/*
|
||||
| --------------------------------------------------------------------------
|
||||
| Exit Status Codes
|
||||
| --------------------------------------------------------------------------
|
||||
|
|
||||
| Used to indicate the conditions under which the script is exit()ing.
|
||||
| While there is no universal standard for error codes, there are some
|
||||
| broad conventions. Three such conventions are mentioned below, for
|
||||
| those who wish to make use of them. The CodeIgniter defaults were
|
||||
| chosen for the least overlap with these conventions, while still
|
||||
| leaving room for others to be defined in future versions and user
|
||||
| applications.
|
||||
|
|
||||
| The three main conventions used for determining exit status codes
|
||||
| are as follows:
|
||||
|
|
||||
| Standard C/C++ Library (stdlibc):
|
||||
| http://www.gnu.org/software/libc/manual/html_node/Exit-Status.html
|
||||
| (This link also contains other GNU-specific conventions)
|
||||
| BSD sysexits.h:
|
||||
| http://www.gsp.com/cgi-bin/man.cgi?section=3&topic=sysexits
|
||||
| Bash scripting:
|
||||
| http://tldp.org/LDP/abs/html/exitcodes.html
|
||||
|
|
||||
*/
|
||||
defined('EXIT_SUCCESS') || define('EXIT_SUCCESS', 0); // no errors
|
||||
defined('EXIT_ERROR') || define('EXIT_ERROR', 1); // generic error
|
||||
defined('EXIT_CONFIG') || define('EXIT_CONFIG', 3); // configuration error
|
||||
defined('EXIT_UNKNOWN_FILE') || define('EXIT_UNKNOWN_FILE', 4); // file not found
|
||||
defined('EXIT_UNKNOWN_CLASS') || define('EXIT_UNKNOWN_CLASS', 5); // unknown class
|
||||
defined('EXIT_UNKNOWN_METHOD') || define('EXIT_UNKNOWN_METHOD', 6); // unknown class member
|
||||
defined('EXIT_USER_INPUT') || define('EXIT_USER_INPUT', 7); // invalid user input
|
||||
defined('EXIT_DATABASE') || define('EXIT_DATABASE', 8); // database error
|
||||
defined('EXIT__AUTO_MIN') || define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code
|
||||
defined('EXIT__AUTO_MAX') || define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code
|
||||
|
||||
/**
|
||||
* @deprecated Use \CodeIgniter\Events\Events::PRIORITY_LOW instead.
|
||||
*/
|
||||
define('EVENT_PRIORITY_LOW', 200);
|
||||
|
||||
/**
|
||||
* @deprecated Use \CodeIgniter\Events\Events::PRIORITY_NORMAL instead.
|
||||
*/
|
||||
define('EVENT_PRIORITY_NORMAL', 100);
|
||||
|
||||
/**
|
||||
* @deprecated Use \CodeIgniter\Events\Events::PRIORITY_HIGH instead.
|
||||
*/
|
||||
define('EVENT_PRIORITY_HIGH', 10);
|
||||
|
||||
/**
|
||||
* @User Teams Constant
|
||||
*/
|
||||
define('ENROLLMENT_TEAM_ID', '1');
|
||||
define('CLAIMS_TEAM_ID', '2');
|
||||
// define('BUSINESS_TEAM_ID', '3'); // old don't delete it
|
||||
define('BUSINESS_TEAM_ID', '4'); // 25th OCT 2025 changed
|
||||
define('FINANCE_TEAM_ID', '4');
|
||||
define('SALES_TEAM_ID', '5');
|
||||
define('MANAGEMENT_TEAM_ID', '6');
|
||||
define('BUSINESS_SUPPORT_TEAM_ID', '7');
|
||||
define('POS_TEAM_ID', '8');
|
||||
|
||||
/**
|
||||
* @User Role Constant
|
||||
*/
|
||||
define('ADMIN_ROLE_ID', 1);
|
||||
define('MANAGER_ROLE_ID', 2);
|
||||
define('ACCOUNT_MANAGER_ROLE_ID', 3);
|
||||
define('STAFF_ROLE_ID', 4);
|
||||
define('HEAD_ROLE_ID', 5);
|
||||
|
||||
/**
|
||||
* @Upload Allowed Extensions by Business Context
|
||||
*/
|
||||
define('UPLOAD_ALLOWED_EXTENSIONS', [
|
||||
'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg',
|
||||
'pdf', 'doc', 'docx', 'odt', 'rtf',
|
||||
'xls', 'xlsx', 'ods', 'csv', 'txt',
|
||||
]);
|
||||
|
||||
define('UPLOAD_EXT_IMAGES', ['jpg', 'jpeg', 'png']);
|
||||
define('UPLOAD_EXT_KYC_DOCS', ['pdf', 'jpg', 'jpeg', 'png']);
|
||||
define('UPLOAD_EXT_CLAIM_DOCS', ['pdf', 'jpg', 'jpeg', 'png']);
|
||||
define('UPLOAD_EXT_POLICY_DOCS', ['pdf', 'jpg', 'jpeg', 'png', 'xls', 'xlsx']);
|
||||
define('UPLOAD_EXT_LEAD_FILES', ['xls', 'xlsx', 'pdf', 'jpg', 'jpeg', 'png']);
|
||||
define('UPLOAD_EXT_EXCEL', ['xls', 'xlsx', 'ods', 'csv']);
|
||||
define('UPLOAD_EXT_MAIL_ATTACHMENTS', ['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx', 'xls', 'xlsx']);
|
||||
define('UPLOAD_EXT_NON_EB_RACK_RATE', ['pdf', 'xls', 'xlsx']);
|
||||
define('UPLOAD_EXT_ASSET_FILES', ['pdf', 'xls', 'xlsx', 'csv']);
|
||||
define('UPLOAD_EXT_INSURER_CLAIM_FORM', ['pdf', 'doc', 'docx']);
|
||||
define('INSURER_DEFAULT_CLAIM_FORM_FILE', 'Claim_Form_IRDAI.pdf');
|
||||
176
app/Config/ContentSecurityPolicy.php
Executable file
176
app/Config/ContentSecurityPolicy.php
Executable file
@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
/**
|
||||
* Stores the default settings for the ContentSecurityPolicy, if you
|
||||
* choose to use it. The values here will be read in and set as defaults
|
||||
* for the site. If needed, they can be overridden on a page-by-page basis.
|
||||
*
|
||||
* Suggested reference for explanations:
|
||||
*
|
||||
* @see https://www.html5rocks.com/en/tutorials/security/content-security-policy/
|
||||
*/
|
||||
class ContentSecurityPolicy extends BaseConfig
|
||||
{
|
||||
// -------------------------------------------------------------------------
|
||||
// Broadbrush CSP management
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Default CSP report context
|
||||
*/
|
||||
public bool $reportOnly = false;
|
||||
|
||||
/**
|
||||
* Specifies a URL where a browser will send reports
|
||||
* when a content security policy is violated.
|
||||
*/
|
||||
public ?string $reportURI = null;
|
||||
|
||||
/**
|
||||
* Instructs user agents to rewrite URL schemes, changing
|
||||
* HTTP to HTTPS. This directive is for websites with
|
||||
* large numbers of old URLs that need to be rewritten.
|
||||
*/
|
||||
public bool $upgradeInsecureRequests = false;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Sources allowed
|
||||
// NOTE: once you set a policy to 'none', it cannot be further restricted
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Will default to self if not overridden
|
||||
*
|
||||
* @var string|string[]|null
|
||||
*/
|
||||
public $defaultSrc;
|
||||
|
||||
/**
|
||||
* Lists allowed scripts' URLs.
|
||||
*
|
||||
* @var string|string[]
|
||||
*/
|
||||
public $scriptSrc = 'self';
|
||||
|
||||
/**
|
||||
* Lists allowed stylesheets' URLs.
|
||||
*
|
||||
* @var string|string[]
|
||||
*/
|
||||
public $styleSrc = 'self';
|
||||
|
||||
/**
|
||||
* Defines the origins from which images can be loaded.
|
||||
*
|
||||
* @var string|string[]
|
||||
*/
|
||||
public $imageSrc = 'self';
|
||||
|
||||
/**
|
||||
* Restricts the URLs that can appear in a page's `<base>` element.
|
||||
*
|
||||
* Will default to self if not overridden
|
||||
*
|
||||
* @var string|string[]|null
|
||||
*/
|
||||
public $baseURI;
|
||||
|
||||
/**
|
||||
* Lists the URLs for workers and embedded frame contents
|
||||
*
|
||||
* @var string|string[]
|
||||
*/
|
||||
public $childSrc = 'self';
|
||||
|
||||
/**
|
||||
* Limits the origins that you can connect to (via XHR,
|
||||
* WebSockets, and EventSource).
|
||||
*
|
||||
* @var string|string[]
|
||||
*/
|
||||
public $connectSrc = 'self';
|
||||
|
||||
/**
|
||||
* Specifies the origins that can serve web fonts.
|
||||
*
|
||||
* @var string|string[]
|
||||
*/
|
||||
public $fontSrc;
|
||||
|
||||
/**
|
||||
* Lists valid endpoints for submission from `<form>` tags.
|
||||
*
|
||||
* @var string|string[]
|
||||
*/
|
||||
public $formAction = 'self';
|
||||
|
||||
/**
|
||||
* Specifies the sources that can embed the current page.
|
||||
* This directive applies to `<frame>`, `<iframe>`, `<embed>`,
|
||||
* and `<applet>` tags. This directive can't be used in
|
||||
* `<meta>` tags and applies only to non-HTML resources.
|
||||
*
|
||||
* @var string|string[]|null
|
||||
*/
|
||||
public $frameAncestors;
|
||||
|
||||
/**
|
||||
* The frame-src directive restricts the URLs which may
|
||||
* be loaded into nested browsing contexts.
|
||||
*
|
||||
* @var array|string|null
|
||||
*/
|
||||
public $frameSrc;
|
||||
|
||||
/**
|
||||
* Restricts the origins allowed to deliver video and audio.
|
||||
*
|
||||
* @var string|string[]|null
|
||||
*/
|
||||
public $mediaSrc;
|
||||
|
||||
/**
|
||||
* Allows control over Flash and other plugins.
|
||||
*
|
||||
* @var string|string[]
|
||||
*/
|
||||
public $objectSrc = 'self';
|
||||
|
||||
/**
|
||||
* @var string|string[]|null
|
||||
*/
|
||||
public $manifestSrc;
|
||||
|
||||
/**
|
||||
* Limits the kinds of plugins a page may invoke.
|
||||
*
|
||||
* @var string|string[]|null
|
||||
*/
|
||||
public $pluginTypes;
|
||||
|
||||
/**
|
||||
* List of actions allowed.
|
||||
*
|
||||
* @var string|string[]|null
|
||||
*/
|
||||
public $sandbox;
|
||||
|
||||
/**
|
||||
* Nonce tag for style
|
||||
*/
|
||||
public string $styleNonceTag = '{csp-style-nonce}';
|
||||
|
||||
/**
|
||||
* Nonce tag for script
|
||||
*/
|
||||
public string $scriptNonceTag = '{csp-script-nonce}';
|
||||
|
||||
/**
|
||||
* Replace nonce tag automatically
|
||||
*/
|
||||
public bool $autoNonce = true;
|
||||
}
|
||||
107
app/Config/Cookie.php
Executable file
107
app/Config/Cookie.php
Executable file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use DateTimeInterface;
|
||||
|
||||
class Cookie extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Cookie Prefix
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Set a cookie name prefix if you need to avoid collisions.
|
||||
*/
|
||||
public string $prefix = '';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Cookie Expires Timestamp
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Default expires timestamp for cookies. Setting this to `0` will mean the
|
||||
* cookie will not have the `Expires` attribute and will behave as a session
|
||||
* cookie.
|
||||
*
|
||||
* @var DateTimeInterface|int|string
|
||||
*/
|
||||
public $expires = 0;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Cookie Path
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Typically will be a forward slash.
|
||||
*/
|
||||
public string $path = '/';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Cookie Domain
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Set to `.your-domain.com` for site-wide cookies.
|
||||
*/
|
||||
public string $domain = '';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Cookie Secure
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Cookie will only be set if a secure HTTPS connection exists.
|
||||
*/
|
||||
public bool $secure = false;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Cookie HTTPOnly
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Cookie will only be accessible via HTTP(S) (no JavaScript).
|
||||
*/
|
||||
public bool $httponly = true;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Cookie SameSite
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Configure cookie SameSite setting. Allowed values are:
|
||||
* - None
|
||||
* - Lax
|
||||
* - Strict
|
||||
* - ''
|
||||
*
|
||||
* Alternatively, you can use the constant names:
|
||||
* - `Cookie::SAMESITE_NONE`
|
||||
* - `Cookie::SAMESITE_LAX`
|
||||
* - `Cookie::SAMESITE_STRICT`
|
||||
*
|
||||
* Defaults to `Lax` for compatibility with modern browsers. Setting `''`
|
||||
* (empty string) means default SameSite attribute set by browsers (`Lax`)
|
||||
* will be set on cookies. If set to `None`, `$secure` must also be set.
|
||||
*
|
||||
* @phpstan-var 'None'|'Lax'|'Strict'|''
|
||||
*/
|
||||
public string $samesite = 'Lax';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Cookie Raw
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* This flag allows setting a "raw" cookie, i.e., its name and value are
|
||||
* not URL encoded using `rawurlencode()`.
|
||||
*
|
||||
* If this is set to `true`, cookie names should be compliant of RFC 2616's
|
||||
* list of allowed characters.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes
|
||||
* @see https://tools.ietf.org/html/rfc2616#section-2.2
|
||||
*/
|
||||
public bool $raw = false;
|
||||
}
|
||||
144
app/Config/Database.php
Executable file
144
app/Config/Database.php
Executable file
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Database\Config;
|
||||
|
||||
/**
|
||||
* Database Configuration
|
||||
*/
|
||||
class Database extends Config
|
||||
{
|
||||
/**
|
||||
* The directory that holds the Migrations
|
||||
* and Seeds directories.
|
||||
*/
|
||||
public string $filesPath = APPPATH . 'Database' . DIRECTORY_SEPARATOR;
|
||||
|
||||
/**
|
||||
* Lets you choose which connection group to
|
||||
* use if no other is specified.
|
||||
*/
|
||||
public string $defaultGroup = 'default';
|
||||
public string $enableSSL;
|
||||
|
||||
/**
|
||||
* The default database connection.
|
||||
*/
|
||||
public array $default = [
|
||||
'DSN' => '',
|
||||
'hostname' => 'localhost',
|
||||
'username' => '',
|
||||
'password' => '',
|
||||
'database' => '',
|
||||
'DBDriver' => 'MySQLi',
|
||||
'DBPrefix' => '',
|
||||
'pConnect' => false,
|
||||
'DBDebug' => true,
|
||||
'charset' => 'utf8',
|
||||
'DBCollat' => 'utf8_general_ci',
|
||||
'swapPre' => '',
|
||||
'encrypt' =>false,
|
||||
'compress' => false,
|
||||
'strictOn' => false,
|
||||
'failover' => [],
|
||||
'port' => 3306,
|
||||
'numberNative' => false,
|
||||
];
|
||||
|
||||
|
||||
public array $book_stack = [];
|
||||
|
||||
/**
|
||||
* This database connection is used when
|
||||
* running PHPUnit database tests.
|
||||
*/
|
||||
public array $tests = [
|
||||
'DSN' => '',
|
||||
'hostname' => '119.18.54.85',
|
||||
'username' => 'venbaehn_nhance_user1',
|
||||
'password' => 'iM~X7(cR+}A-',
|
||||
'database' => 'venbaehn_nhance',
|
||||
'DBDriver' => 'SQLite3',
|
||||
'DBPrefix' => 'db_', // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS
|
||||
'pConnect' => false,
|
||||
'DBDebug' => true,
|
||||
'charset' => 'utf8',
|
||||
'DBCollat' => 'utf8_general_ci',
|
||||
'swapPre' => '',
|
||||
'encrypt' => false,
|
||||
'compress' => false,
|
||||
'strictOn' => false,
|
||||
'failover' => [],
|
||||
'port' => 3306,
|
||||
'foreignKeys' => true,
|
||||
'busyTimeout' => 1000,
|
||||
];
|
||||
|
||||
public $preDB = [
|
||||
'DSN' => '',
|
||||
'hostname' => 'localhost',
|
||||
'username' => 'root',
|
||||
'password' => 'root',
|
||||
'database' => 'other_db',
|
||||
'DBDriver' => 'MySQLi',
|
||||
'DBPrefix' => '',
|
||||
'pConnect' => false,
|
||||
'DBDebug' => (ENVIRONMENT !== 'production'),
|
||||
'charset' => 'utf8',
|
||||
'DBCollat' => 'utf8_general_ci',
|
||||
];
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
// Initialize book_stack configuration with environment variables
|
||||
$this->book_stack = [
|
||||
'DSN' => '',
|
||||
'hostname' => getenv('bookStack_hostname'),
|
||||
'username' => getenv('bookStack_username'),
|
||||
'password' => getenv('bookStack_password'),
|
||||
'database' => getenv('bookStack_database'),
|
||||
'DBDriver' => 'MySQLi',
|
||||
'DBPrefix' => '',
|
||||
'pConnect' => false,
|
||||
'DBDebug' => true,
|
||||
'charset' => 'utf8',
|
||||
'DBCollat' => 'utf8_general_ci',
|
||||
'swapPre' => '',
|
||||
'encrypt' => false,
|
||||
'compress' => false,
|
||||
'strictOn' => false,
|
||||
'failover' => [],
|
||||
'port' => 3306,
|
||||
'foreignKeys' => true,
|
||||
'busyTimeout' => 1000,
|
||||
];
|
||||
|
||||
// Ensure that we always set the database group to 'tests' if
|
||||
// we are currently running an automated test suite, so that
|
||||
// we don't overwrite live data on accident.
|
||||
if (ENVIRONMENT === 'testing') {
|
||||
$this->defaultGroup = 'tests';
|
||||
}
|
||||
|
||||
$this->enableSSL = env('DB_SSL_ENABLE') ?? false;
|
||||
|
||||
if ($this->enableSSL === true || $this->enableSSL === 'true' || $this->enableSSL === 1 || $this->enableSSL === '1') {
|
||||
|
||||
|
||||
// Enable SSL authentication
|
||||
$this->default['encrypt'] = [
|
||||
'ssl_ca' => ROOTPATH . 'ca.pem',
|
||||
'ssl_verify' => true,
|
||||
];
|
||||
$this->preDB['encrypt'] = [
|
||||
'ssl_ca' => ROOTPATH . 'ca.pem',
|
||||
'ssl_verify' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
45
app/Config/DigitMotor.php
Normal file
45
app/Config/DigitMotor.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class DigitMotor extends BaseConfig
|
||||
{
|
||||
public string $baseUrl;
|
||||
public string $authPath;
|
||||
public string $executorPath;
|
||||
public string $username;
|
||||
public string $password;
|
||||
public string $environment;
|
||||
public int $timeout;
|
||||
public int $tokenLeewaySec;
|
||||
/** Partner key required by Digit policy PDF headerParam.Authorization */
|
||||
public string $pdfAuthKey;
|
||||
public array $integrationIds;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->baseUrl = rtrim((string) env('DIGIT_MOTOR_BASE_URL', 'https://preprod-oneapi.godigit.com'), '/');
|
||||
$this->authPath = (string) env('DIGIT_MOTOR_AUTH_PATH', '/OneAPI/v1/auth');
|
||||
$this->executorPath = (string) env('DIGIT_MOTOR_EXECUTOR_PATH', '/OneAPI/v1/executor');
|
||||
$this->username = (string) env('DIGIT_MOTOR_USERNAME', '');
|
||||
$this->password = (string) env('DIGIT_MOTOR_PASSWORD', '');
|
||||
$this->environment = (string) env('DIGIT_MOTOR_ENVIRONMENT', 'staging');
|
||||
$this->timeout = (int) env('DIGIT_MOTOR_TIMEOUT', 30);
|
||||
$this->tokenLeewaySec = (int) env('DIGIT_MOTOR_TOKEN_LEEWAY_SEC', 60);
|
||||
$this->pdfAuthKey = (string) env('DIGIT_MOTOR_PDF_AUTH_KEY', '');
|
||||
|
||||
// Integration IDs from NHANCE Digit API kit (CURL / Bruno export)
|
||||
$this->integrationIds = [
|
||||
'quickQuote' => (string) env('DIGIT_MOTOR_IID_QUICK_QUOTE', '29266-0100'),
|
||||
'createQuote' => (string) env('DIGIT_MOTOR_IID_CREATE_QUOTE', '29268-0100'),
|
||||
'kycStatus' => (string) env('DIGIT_MOTOR_IID_KYC', '29269-0100'),
|
||||
'payment' => (string) env('DIGIT_MOTOR_IID_PAYMENT', '29270-0100'),
|
||||
'policyStatus' => (string) env('DIGIT_MOTOR_IID_POLICY_STATUS', '29271-0100'),
|
||||
'policyPdf' => (string) env('DIGIT_MOTOR_IID_POLICY_PDF', '29272-0100'),
|
||||
];
|
||||
}
|
||||
}
|
||||
46
app/Config/DocTypes.php
Executable file
46
app/Config/DocTypes.php
Executable file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
/**
|
||||
* @immutable
|
||||
*/
|
||||
class DocTypes
|
||||
{
|
||||
/**
|
||||
* List of valid document types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $list = [
|
||||
'xhtml11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
|
||||
'xhtml1-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
|
||||
'xhtml1-trans' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
|
||||
'xhtml1-frame' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
|
||||
'xhtml-basic11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">',
|
||||
'html5' => '<!DOCTYPE html>',
|
||||
'html4-strict' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
|
||||
'html4-trans' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
|
||||
'html4-frame' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
|
||||
'mathml1' => '<!DOCTYPE math SYSTEM "http://www.w3.org/Math/DTD/mathml1/mathml.dtd">',
|
||||
'mathml2' => '<!DOCTYPE math PUBLIC "-//W3C//DTD MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/mathml2.dtd">',
|
||||
'svg10' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">',
|
||||
'svg11' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">',
|
||||
'svg11-basic' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Basic//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd">',
|
||||
'svg11-tiny' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">',
|
||||
'xhtml-math-svg-xh' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">',
|
||||
'xhtml-math-svg-sh' => '<!DOCTYPE svg:svg PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">',
|
||||
'xhtml-rdfa-1' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">',
|
||||
'xhtml-rdfa-2' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd">',
|
||||
];
|
||||
|
||||
/**
|
||||
* Whether to remove the solidus (`/`) character for void HTML elements (e.g. `<input>`)
|
||||
* for HTML5 compatibility.
|
||||
*
|
||||
* Set to:
|
||||
* `true` - to be HTML5 compatible
|
||||
* `false` - to be XHTML compatible
|
||||
*/
|
||||
public bool $html5 = true;
|
||||
}
|
||||
131
app/Config/Email.php
Executable file
131
app/Config/Email.php
Executable file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class Email extends BaseConfig
|
||||
{
|
||||
public string $fromEmail = '';
|
||||
public string $fromName = '';
|
||||
public string $recipients = '';
|
||||
|
||||
/**
|
||||
* The "user agent"
|
||||
*/
|
||||
public string $userAgent = 'CodeIgniter';
|
||||
|
||||
/**
|
||||
* The mail sending protocol: mail, sendmail, smtp
|
||||
*/
|
||||
public string $protocol = 'smtp';
|
||||
|
||||
/**
|
||||
* The server path to Sendmail.
|
||||
*/
|
||||
public string $mailPath = '/usr/sbin/sendmail';
|
||||
|
||||
/**
|
||||
* SMTP Server Hostname
|
||||
*/
|
||||
public string $SMTPHost = 'smtp.gmail.com';
|
||||
|
||||
/**
|
||||
* SMTP Username
|
||||
*/
|
||||
public string $SMTPUser = 'venbatechnologies@gmail.com';
|
||||
|
||||
/**
|
||||
* SMTP Password
|
||||
*/
|
||||
public string $SMTPPass = 'dsfllmxdzxxkcxgz';
|
||||
|
||||
/**
|
||||
* SMTP Port
|
||||
*/
|
||||
public int $SMTPPort = 465;
|
||||
|
||||
/**
|
||||
* SMTP Timeout (in seconds)
|
||||
*/
|
||||
public int $SMTPTimeout = 1000;
|
||||
|
||||
/**
|
||||
* Enable persistent SMTP connections
|
||||
*/
|
||||
public bool $SMTPKeepAlive = false;
|
||||
|
||||
/**
|
||||
* SMTP Encryption.
|
||||
*
|
||||
* @var string '', 'tls' or 'ssl'. 'tls' will issue a STARTTLS command
|
||||
* to the server. 'ssl' means implicit SSL. Connection on port
|
||||
* 465 should set this to ''.
|
||||
*/
|
||||
public string $SMTPCrypto = 'ssl';
|
||||
|
||||
/**
|
||||
* Enable word-wrap
|
||||
*/
|
||||
public bool $wordWrap = true;
|
||||
|
||||
/**
|
||||
* Character count to wrap at
|
||||
*/
|
||||
public int $wrapChars = 76;
|
||||
|
||||
/**
|
||||
* Type of mail, either 'text' or 'html'
|
||||
*/
|
||||
public string $mailType = 'html';
|
||||
|
||||
/**
|
||||
* Character set (utf-8, iso-8859-1, etc.)
|
||||
*/
|
||||
public string $charset = 'UTF-8';
|
||||
|
||||
/**
|
||||
* Whether to validate the email address
|
||||
*/
|
||||
public bool $validate = false;
|
||||
|
||||
/**
|
||||
* Email Priority. 1 = highest. 5 = lowest. 3 = normal
|
||||
*/
|
||||
public int $priority = 3;
|
||||
|
||||
/**
|
||||
* Newline character. (Use “\r\n” to comply with RFC 822)
|
||||
*/
|
||||
public string $CRLF = "\r\n";
|
||||
|
||||
/**
|
||||
* Newline character. (Use “\r\n” to comply with RFC 822)
|
||||
*/
|
||||
public string $newline = "\r\n";
|
||||
|
||||
/**
|
||||
* Enable BCC Batch Mode.
|
||||
*/
|
||||
public bool $BCCBatchMode = false;
|
||||
|
||||
/**
|
||||
* Number of emails in each BCC batch
|
||||
*/
|
||||
public int $BCCBatchSize = 200;
|
||||
|
||||
/**
|
||||
* Enable notify message from server
|
||||
*/
|
||||
public bool $DSN = false;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* SMTP Debugging level. Set to 0 to disable debugging.
|
||||
* Set to 1 to output server connection status only.
|
||||
* Set to 2 for more detailed debugging output.
|
||||
* Set to 3 for maximum debugging output.
|
||||
*/
|
||||
public int $SMTPDebug = 2;
|
||||
}
|
||||
92
app/Config/Encryption.php
Executable file
92
app/Config/Encryption.php
Executable file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
/**
|
||||
* Encryption configuration.
|
||||
*
|
||||
* These are the settings used for encryption, if you don't pass a parameter
|
||||
* array to the encrypter for creation/initialization.
|
||||
*/
|
||||
class Encryption extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Encryption Key Starter
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* If you use the Encryption class you must set an encryption key (seed).
|
||||
* You need to ensure it is long enough for the cipher and mode you plan to use.
|
||||
* See the user guide for more info.
|
||||
*/
|
||||
public string $key = '';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Encryption Driver to Use
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* One of the supported encryption drivers.
|
||||
*
|
||||
* Available drivers:
|
||||
* - OpenSSL
|
||||
* - Sodium
|
||||
*/
|
||||
public string $driver = 'OpenSSL';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* SodiumHandler's Padding Length in Bytes
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* This is the number of bytes that will be padded to the plaintext message
|
||||
* before it is encrypted. This value should be greater than zero.
|
||||
*
|
||||
* See the user guide for more information on padding.
|
||||
*/
|
||||
public int $blockSize = 16;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Encryption digest
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* HMAC digest to use, e.g. 'SHA512' or 'SHA256'. Default value is 'SHA512'.
|
||||
*/
|
||||
public string $digest = 'SHA512';
|
||||
|
||||
/**
|
||||
* Whether the cipher-text should be raw. If set to false, then it will be base64 encoded.
|
||||
* This setting is only used by OpenSSLHandler.
|
||||
*
|
||||
* Set to false for CI3 Encryption compatibility.
|
||||
*/
|
||||
public bool $rawData = true;
|
||||
|
||||
/**
|
||||
* Encryption key info.
|
||||
* This setting is only used by OpenSSLHandler.
|
||||
*
|
||||
* Set to 'encryption' for CI3 Encryption compatibility.
|
||||
*/
|
||||
public string $encryptKeyInfo = '';
|
||||
|
||||
/**
|
||||
* Authentication key info.
|
||||
* This setting is only used by OpenSSLHandler.
|
||||
*
|
||||
* Set to 'authentication' for CI3 Encryption compatibility.
|
||||
*/
|
||||
public string $authKeyInfo = '';
|
||||
|
||||
/**
|
||||
* Cipher to use.
|
||||
* This setting is only used by OpenSSLHandler.
|
||||
*
|
||||
* Set to 'AES-128-CBC' to decrypt encrypted data that encrypted
|
||||
* by CI3 Encryption default configuration.
|
||||
*/
|
||||
public string $cipher = 'AES-256-CTR';
|
||||
}
|
||||
55
app/Config/Events.php
Executable file
55
app/Config/Events.php
Executable file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Events\Events;
|
||||
use CodeIgniter\Exceptions\FrameworkException;
|
||||
use CodeIgniter\HotReloader\HotReloader;
|
||||
|
||||
/*
|
||||
* --------------------------------------------------------------------
|
||||
* Application Events
|
||||
* --------------------------------------------------------------------
|
||||
* Events allow you to tap into the execution of the program without
|
||||
* modifying or extending core files. This file provides a central
|
||||
* location to define your events, though they can always be added
|
||||
* at run-time, also, if needed.
|
||||
*
|
||||
* You create code that can execute by subscribing to events with
|
||||
* the 'on()' method. This accepts any form of callable, including
|
||||
* Closures, that will be executed when the event is triggered.
|
||||
*
|
||||
* Example:
|
||||
* Events::on('create', [$myInstance, 'myMethod']);
|
||||
*/
|
||||
|
||||
Events::on('pre_system', static function () {
|
||||
if (ENVIRONMENT !== 'testing') {
|
||||
if (ini_get('zlib.output_compression')) {
|
||||
throw FrameworkException::forEnabledZlibOutputCompression();
|
||||
}
|
||||
|
||||
while (ob_get_level() > 0) {
|
||||
ob_end_flush();
|
||||
}
|
||||
|
||||
ob_start(static fn ($buffer) => $buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* --------------------------------------------------------------------
|
||||
* Debug Toolbar Listeners.
|
||||
* --------------------------------------------------------------------
|
||||
* If you delete, they will no longer be collected.
|
||||
*/
|
||||
if (CI_DEBUG && ! is_cli()) {
|
||||
Events::on('DBQuery', 'CodeIgniter\Debug\Toolbar\Collectors\Database::collect');
|
||||
Services::toolbar()->respond();
|
||||
// Hot Reload route - for framework use on the hot reloader.
|
||||
if (ENVIRONMENT === 'development') {
|
||||
Services::routes()->get('__hot-reload', static function () {
|
||||
(new HotReloader())->run();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
104
app/Config/Exceptions.php
Executable file
104
app/Config/Exceptions.php
Executable file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use App\Debug\CorsExceptionHandler;
|
||||
use CodeIgniter\Debug\ExceptionHandlerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Setup how the exception handler works.
|
||||
*/
|
||||
class Exceptions extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* LOG EXCEPTIONS?
|
||||
* --------------------------------------------------------------------------
|
||||
* If true, then exceptions will be logged
|
||||
* through Services::Log.
|
||||
*
|
||||
* Default: true
|
||||
*/
|
||||
public bool $log = true;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* DO NOT LOG STATUS CODES
|
||||
* --------------------------------------------------------------------------
|
||||
* Any status codes here will NOT be logged if logging is turned on.
|
||||
* By default, only 404 (Page Not Found) exceptions are ignored.
|
||||
*/
|
||||
public array $ignoreCodes = [404];
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Error Views Path
|
||||
* --------------------------------------------------------------------------
|
||||
* This is the path to the directory that contains the 'cli' and 'html'
|
||||
* directories that hold the views used to generate errors.
|
||||
*
|
||||
* Default: APPPATH.'Views/errors'
|
||||
*/
|
||||
public string $errorViewPath = APPPATH . 'Views/errors';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* HIDE FROM DEBUG TRACE
|
||||
* --------------------------------------------------------------------------
|
||||
* Any data that you would like to hide from the debug trace.
|
||||
* In order to specify 2 levels, use "/" to separate.
|
||||
* ex. ['server', 'setup/password', 'secret_token']
|
||||
*/
|
||||
public array $sensitiveDataInTrace = [];
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* LOG DEPRECATIONS INSTEAD OF THROWING?
|
||||
* --------------------------------------------------------------------------
|
||||
* By default, CodeIgniter converts deprecations into exceptions. Also,
|
||||
* starting in PHP 8.1 will cause a lot of deprecated usage warnings.
|
||||
* Use this option to temporarily cease the warnings and instead log those.
|
||||
* This option also works for user deprecations.
|
||||
*/
|
||||
public bool $logDeprecations = true;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* LOG LEVEL THRESHOLD FOR DEPRECATIONS
|
||||
* --------------------------------------------------------------------------
|
||||
* If `$logDeprecations` is set to `true`, this sets the log level
|
||||
* to which the deprecation will be logged. This should be one of the log
|
||||
* levels recognized by PSR-3.
|
||||
*
|
||||
* The related `Config\Logger::$threshold` should be adjusted, if needed,
|
||||
* to capture logging the deprecations.
|
||||
*/
|
||||
public string $deprecationLogLevel = LogLevel::WARNING;
|
||||
|
||||
/*
|
||||
* DEFINE THE HANDLERS USED
|
||||
* --------------------------------------------------------------------------
|
||||
* Given the HTTP status code, returns exception handler that
|
||||
* should be used to deal with this error. By default, it will run CodeIgniter's
|
||||
* default handler and display the error information in the expected format
|
||||
* for CLI, HTTP, or AJAX requests, as determined by is_cli() and the expected
|
||||
* response format.
|
||||
*
|
||||
* Custom handlers can be returned if you want to handle one or more specific
|
||||
* error codes yourself like:
|
||||
*
|
||||
* if (in_array($statusCode, [400, 404, 500])) {
|
||||
* return new \App\Libraries\MyExceptionHandler();
|
||||
* }
|
||||
* if ($exception instanceOf PageNotFoundException) {
|
||||
* return new \App\Libraries\MyExceptionHandler();
|
||||
* }
|
||||
*/
|
||||
public function handler(int $statusCode, Throwable $exception): ExceptionHandlerInterface
|
||||
{
|
||||
return new CorsExceptionHandler($this);
|
||||
}
|
||||
}
|
||||
30
app/Config/Feature.php
Executable file
30
app/Config/Feature.php
Executable file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
/**
|
||||
* Enable/disable backward compatibility breaking features.
|
||||
*/
|
||||
class Feature extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* Enable multiple filters for a route or not.
|
||||
*
|
||||
* If you enable this:
|
||||
* - CodeIgniter\CodeIgniter::handleRequest() uses:
|
||||
* - CodeIgniter\Filters\Filters::enableFilters(), instead of enableFilter()
|
||||
* - CodeIgniter\CodeIgniter::tryToRouteIt() uses:
|
||||
* - CodeIgniter\Router\Router::getFilters(), instead of getFilter()
|
||||
* - CodeIgniter\Router\Router::handle() uses:
|
||||
* - property $filtersInfo, instead of $filterInfo
|
||||
* - CodeIgniter\Router\RouteCollection::getFiltersForRoute(), instead of getFilterForRoute()
|
||||
*/
|
||||
public bool $multipleFilters = true;
|
||||
|
||||
/**
|
||||
* Use improved new auto routing instead of the default legacy version.
|
||||
*/
|
||||
public bool $autoRoutesImproved = false;
|
||||
}
|
||||
106
app/Config/Filters.php
Executable file
106
app/Config/Filters.php
Executable file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use CodeIgniter\Filters\CSRF;
|
||||
use CodeIgniter\Filters\DebugToolbar;
|
||||
use CodeIgniter\Filters\Honeypot;
|
||||
use CodeIgniter\Filters\InvalidChars;
|
||||
use CodeIgniter\Filters\SecureHeaders;
|
||||
|
||||
use App\Filters\AuthMVC;
|
||||
use App\Filters\HttpRequestLog;
|
||||
use App\Filters\CloseDbConnection;
|
||||
use App\Filters\AuthClientApi;
|
||||
use App\Filters\CommissionApiFilter;
|
||||
use App\Filters\VerifyAppSignature;
|
||||
use App\Filters\Cors;
|
||||
use App\Filters\SecurityInputFilter;
|
||||
use App\Filters\GlobalPostFileUploadGuard;
|
||||
use App\Filters\AclFilter;
|
||||
use App\Filters\RateLimitFilter;
|
||||
use App\Filters\JwtApiRateLimitFilter;
|
||||
use App\Filters\AuthApiRateLimitFilter;
|
||||
|
||||
use App\Filters\AuthJWT;
|
||||
|
||||
class Filters extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* Configures aliases for Filter classes to
|
||||
* make reading things nicer and simpler.
|
||||
*
|
||||
* @var array<string, array<int, string>|string> [filter_name => classname]
|
||||
* or [filter_name => [classname1, classname2, ...]]
|
||||
* @phpstan-var array<string, class-string|list<class-string>>
|
||||
*/
|
||||
public array $aliases = [
|
||||
'csrf' => CSRF::class,
|
||||
'toolbar' => DebugToolbar::class,
|
||||
'honeypot' => Honeypot::class,
|
||||
'invalidchars' => InvalidChars::class,
|
||||
'secureheaders' => SecureHeaders::class,
|
||||
'authMVC' => AuthMVC::class,
|
||||
'HttpRequestLog' => HttpRequestLog::class,
|
||||
'authJWT' => AuthJWT::class,
|
||||
'AuthClientApi' => AuthClientApi::class,
|
||||
'CloseDbConnection' => CloseDbConnection::class,
|
||||
'CommissionApiFilter'=> CommissionApiFilter::class,
|
||||
'appSignature' => VerifyAppSignature::class,
|
||||
'Cors' => Cors::class,
|
||||
'SecurityInputFilter' => SecurityInputFilter::class,
|
||||
'GlobalPostFileUploadGuard' => GlobalPostFileUploadGuard::class,
|
||||
'AclFilter' => AclFilter::class,
|
||||
'ratelimit' => RateLimitFilter::class,
|
||||
'AuthApiRateLimitFilter' => AuthApiRateLimitFilter::class,
|
||||
'JwtApiRateLimitFilter' => JwtApiRateLimitFilter::class,
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
* List of filter aliases that are always
|
||||
* applied before and after every request.
|
||||
*
|
||||
* @var array<string, array<string, array<string, string>>>|array<string, array<string>>
|
||||
* @phpstan-var array<string, list<string>>|array<string, array<string, array<string, string>>>
|
||||
*/
|
||||
public array $globals = [
|
||||
'before' => [
|
||||
'HttpRequestLog' => ['except' => 'cli/*'],
|
||||
'Cors',
|
||||
'AclFilter' => ['except' => ['login', 'logout', 'auth/*', 'oauth2callback','claim-form-download', 'claims-feedback-form', 'autobookstackLogin','employeeRest/*','processjob','getCommission','downloadEmployeeEcardZip', 'downloadClaimFile/*', 'api/v1/*', 'cronCdLowBalanceAlert', 'sendCroneRemainderMail']],
|
||||
'SecurityInputFilter' => ['except' => ['/client/notification/create','/ticket/crud_mail_template/*','test_mail','leads/sendMail', 'ticket/reply', 'policy_tranction/retail-policy/template', 'policy_tranction/retail-policy/send-test-mail'] ],
|
||||
'GlobalPostFileUploadGuard'
|
||||
// 'csrf',
|
||||
// 'invalidchars',
|
||||
],
|
||||
'after' => [
|
||||
'CloseDbConnection',
|
||||
'Cors',
|
||||
// 'secureheaders',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* List of filter aliases that works on a
|
||||
* particular HTTP method (GET, POST, etc.).
|
||||
*
|
||||
* Example:
|
||||
* 'post' => ['foo', 'bar']
|
||||
*
|
||||
* If you use this, you should disable auto-routing because auto-routing
|
||||
* permits any HTTP method to access a controller. Accessing the controller
|
||||
* with a method you don't expect could bypass the filter.
|
||||
*/
|
||||
public array $methods = [];
|
||||
|
||||
/**
|
||||
* List of filter aliases that should run on any
|
||||
* before or after URI patterns.
|
||||
*
|
||||
* Example:
|
||||
* 'isLoggedIn' => ['before' => ['account/*', 'profiles/*']]
|
||||
*/
|
||||
// public array $filters = [ 'Cors' => ['before' => ['employeeRest/*']]];
|
||||
}
|
||||
12
app/Config/ForeignCharacters.php
Executable file
12
app/Config/ForeignCharacters.php
Executable file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\ForeignCharacters as BaseForeignCharacters;
|
||||
|
||||
/**
|
||||
* @immutable
|
||||
*/
|
||||
class ForeignCharacters extends BaseForeignCharacters
|
||||
{
|
||||
}
|
||||
77
app/Config/Format.php
Executable file
77
app/Config/Format.php
Executable file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use CodeIgniter\Format\FormatterInterface;
|
||||
use CodeIgniter\Format\JSONFormatter;
|
||||
use CodeIgniter\Format\XMLFormatter;
|
||||
|
||||
class Format extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Available Response Formats
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* When you perform content negotiation with the request, these are the
|
||||
* available formats that your application supports. This is currently
|
||||
* only used with the API\ResponseTrait. A valid Formatter must exist
|
||||
* for the specified format.
|
||||
*
|
||||
* These formats are only checked when the data passed to the respond()
|
||||
* method is an array.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public array $supportedResponseFormats = [
|
||||
'application/json',
|
||||
'application/xml', // machine-readable XML
|
||||
'text/xml', // human-readable XML
|
||||
];
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Formatters
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Lists the class to use to format responses with of a particular type.
|
||||
* For each mime type, list the class that should be used. Formatters
|
||||
* can be retrieved through the getFormatter() method.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $formatters = [
|
||||
'application/json' => JSONFormatter::class,
|
||||
'application/xml' => XMLFormatter::class,
|
||||
'text/xml' => XMLFormatter::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Formatters Options
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Additional Options to adjust default formatters behaviour.
|
||||
* For each mime type, list the additional options that should be used.
|
||||
*
|
||||
* @var array<string, int>
|
||||
*/
|
||||
public array $formatterOptions = [
|
||||
'application/json' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
|
||||
'application/xml' => 0,
|
||||
'text/xml' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* A Factory method to return the appropriate formatter for the given mime type.
|
||||
*
|
||||
* @return FormatterInterface
|
||||
*
|
||||
* @deprecated This is an alias of `\CodeIgniter\Format\Format::getFormatter`. Use that instead.
|
||||
*/
|
||||
public function getFormatter(string $mime)
|
||||
{
|
||||
return Services::format()->getFormatter($mime);
|
||||
}
|
||||
}
|
||||
42
app/Config/Generators.php
Executable file
42
app/Config/Generators.php
Executable file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class Generators extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Generator Commands' Views
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* This array defines the mapping of generator commands to the view files
|
||||
* they are using. If you need to customize them for your own, copy these
|
||||
* view files in your own folder and indicate the location here.
|
||||
*
|
||||
* You will notice that the views have special placeholders enclosed in
|
||||
* curly braces `{...}`. These placeholders are used internally by the
|
||||
* generator commands in processing replacements, thus you are warned
|
||||
* not to delete them or modify the names. If you will do so, you may
|
||||
* end up disrupting the scaffolding process and throw errors.
|
||||
*
|
||||
* YOU HAVE BEEN WARNED!
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $views = [
|
||||
'make:cell' => 'CodeIgniter\Commands\Generators\Views\cell.tpl.php',
|
||||
'make:cell_view' => 'CodeIgniter\Commands\Generators\Views\cell_view.tpl.php',
|
||||
'make:command' => 'CodeIgniter\Commands\Generators\Views\command.tpl.php',
|
||||
'make:config' => 'CodeIgniter\Commands\Generators\Views\config.tpl.php',
|
||||
'make:controller' => 'CodeIgniter\Commands\Generators\Views\controller.tpl.php',
|
||||
'make:entity' => 'CodeIgniter\Commands\Generators\Views\entity.tpl.php',
|
||||
'make:filter' => 'CodeIgniter\Commands\Generators\Views\filter.tpl.php',
|
||||
'make:migration' => 'CodeIgniter\Commands\Generators\Views\migration.tpl.php',
|
||||
'make:model' => 'CodeIgniter\Commands\Generators\Views\model.tpl.php',
|
||||
'make:seeder' => 'CodeIgniter\Commands\Generators\Views\seeder.tpl.php',
|
||||
'make:validation' => 'CodeIgniter\Commands\Generators\Views\validation.tpl.php',
|
||||
'session:migration' => 'CodeIgniter\Commands\Generators\Views\migration.tpl.php',
|
||||
];
|
||||
}
|
||||
42
app/Config/Honeypot.php
Executable file
42
app/Config/Honeypot.php
Executable file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class Honeypot extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* Makes Honeypot visible or not to human
|
||||
*/
|
||||
public bool $hidden = true;
|
||||
|
||||
/**
|
||||
* Honeypot Label Content
|
||||
*/
|
||||
public string $label = 'Fill This Field';
|
||||
|
||||
/**
|
||||
* Honeypot Field Name
|
||||
*/
|
||||
public string $name = 'honeypot';
|
||||
|
||||
/**
|
||||
* Honeypot HTML Template
|
||||
*/
|
||||
public string $template = '<label>{label}</label><input type="text" name="{name}" value="">';
|
||||
|
||||
/**
|
||||
* Honeypot container
|
||||
*
|
||||
* If you enabled CSP, you can remove `style="display:none"`.
|
||||
*/
|
||||
public string $container = '<div style="display:none">{template}</div>';
|
||||
|
||||
/**
|
||||
* The id attribute for Honeypot container tag
|
||||
*
|
||||
* Used when CSP is enabled.
|
||||
*/
|
||||
public string $containerId = 'hpc';
|
||||
}
|
||||
18
app/Config/HrLoginDiagnoseRoutes.php
Normal file
18
app/Config/HrLoginDiagnoseRoutes.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* HR Login Diagnosis routes (loaded via Config\Routing::$routeFiles).
|
||||
*
|
||||
* @var \CodeIgniter\Router\RouteCollection $routes
|
||||
*/
|
||||
|
||||
$routes->group('hrLoginDiagnose', ['filter' => 'authMVC'], static function ($routes) {
|
||||
$routes->get('/', 'HrLoginDiagnoseController::index');
|
||||
$routes->get('', 'HrLoginDiagnoseController::index');
|
||||
$routes->post('check', 'HrLoginDiagnoseController::check');
|
||||
});
|
||||
|
||||
// Fallback when request path still includes index.php (wrong baseURL / form action).
|
||||
$routes->get('index.php/hrLoginDiagnose', 'HrLoginDiagnoseController::index', ['filter' => 'authMVC']);
|
||||
$routes->get('index.php/hrLoginDiagnose/', 'HrLoginDiagnoseController::index', ['filter' => 'authMVC']);
|
||||
$routes->post('index.php/hrLoginDiagnose/check', 'HrLoginDiagnoseController::check', ['filter' => 'authMVC']);
|
||||
31
app/Config/Images.php
Executable file
31
app/Config/Images.php
Executable file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use CodeIgniter\Images\Handlers\GDHandler;
|
||||
use CodeIgniter\Images\Handlers\ImageMagickHandler;
|
||||
|
||||
class Images extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* Default handler used if no other handler is specified.
|
||||
*/
|
||||
public string $defaultHandler = 'gd';
|
||||
|
||||
/**
|
||||
* The path to the image library.
|
||||
* Required for ImageMagick, GraphicsMagick, or NetPBM.
|
||||
*/
|
||||
public string $libraryPath = '/usr/local/bin/convert';
|
||||
|
||||
/**
|
||||
* The available handler classes.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $handlers = [
|
||||
'gd' => GDHandler::class,
|
||||
'imagick' => ImageMagickHandler::class,
|
||||
];
|
||||
}
|
||||
66
app/Config/Kint.php
Executable file
66
app/Config/Kint.php
Executable file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use Kint\Parser\ConstructablePluginInterface;
|
||||
use Kint\Renderer\AbstractRenderer;
|
||||
use Kint\Renderer\Rich\TabPluginInterface;
|
||||
use Kint\Renderer\Rich\ValuePluginInterface;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Kint
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* We use Kint's `RichRenderer` and `CLIRenderer`. This area contains options
|
||||
* that you can set to customize how Kint works for you.
|
||||
*
|
||||
* @see https://kint-php.github.io/kint/ for details on these settings.
|
||||
*/
|
||||
class Kint extends BaseConfig
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global Settings
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* @var list<class-string<ConstructablePluginInterface>|ConstructablePluginInterface>|null
|
||||
*/
|
||||
public $plugins;
|
||||
|
||||
public int $maxDepth = 6;
|
||||
public bool $displayCalledFrom = true;
|
||||
public bool $expanded = false;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| RichRenderer Settings
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
public string $richTheme = 'aante-light.css';
|
||||
public bool $richFolder = false;
|
||||
public int $richSort = AbstractRenderer::SORT_FULL;
|
||||
|
||||
/**
|
||||
* @var array<string, class-string<ValuePluginInterface>>|null
|
||||
*/
|
||||
public $richObjectPlugins;
|
||||
|
||||
/**
|
||||
* @var array<string, class-string<TabPluginInterface>>|null
|
||||
*/
|
||||
public $richTabPlugins;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| CLI Settings
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
public bool $cliColors = true;
|
||||
public bool $cliForceUTF8 = false;
|
||||
public bool $cliDetectWidth = true;
|
||||
public int $cliMinWidth = 40;
|
||||
}
|
||||
148
app/Config/Logger.php
Executable file
148
app/Config/Logger.php
Executable file
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use CodeIgniter\Log\Handlers\FileHandler;
|
||||
|
||||
class Logger extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Error Logging Threshold
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* You can enable error logging by setting a threshold over zero. The
|
||||
* threshold determines what gets logged. Any values below or equal to the
|
||||
* threshold will be logged.
|
||||
*
|
||||
* Threshold options are:
|
||||
*
|
||||
* - 0 = Disables logging, Error logging TURNED OFF
|
||||
* - 1 = Emergency Messages - System is unusable
|
||||
* - 2 = Alert Messages - Action Must Be Taken Immediately
|
||||
* - 3 = Critical Messages - Application component unavailable, unexpected exception.
|
||||
* - 4 = Runtime Errors - Don't need immediate action, but should be monitored.
|
||||
* - 5 = Warnings - Exceptional occurrences that are not errors.
|
||||
* - 6 = Notices - Normal but significant events.
|
||||
* - 7 = Info - Interesting events, like user logging in, etc.
|
||||
* - 8 = Debug - Detailed debug information.
|
||||
* - 9 = All Messages
|
||||
*
|
||||
* You can also pass an array with threshold levels to show individual error types
|
||||
*
|
||||
* array(1, 2, 3, 8) = Emergency, Alert, Critical, and Debug messages
|
||||
*
|
||||
* For a live site you'll usually enable Critical or higher (3) to be logged otherwise
|
||||
* your log files will fill up very fast.
|
||||
*
|
||||
* @var array|int
|
||||
*/
|
||||
public $threshold = (ENVIRONMENT === 'production') ? 4 : 9;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Date Format for Logs
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Each item that is logged has an associated date. You can use PHP date
|
||||
* codes to set your own date formatting
|
||||
*/
|
||||
public string $dateFormat = 'Y-m-d H:i:s';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Log Handlers
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The logging system supports multiple actions to be taken when something
|
||||
* is logged. This is done by allowing for multiple Handlers, special classes
|
||||
* designed to write the log to their chosen destinations, whether that is
|
||||
* a file on the getServer, a cloud-based service, or even taking actions such
|
||||
* as emailing the dev team.
|
||||
*
|
||||
* Each handler is defined by the class name used for that handler, and it
|
||||
* MUST implement the `CodeIgniter\Log\Handlers\HandlerInterface` interface.
|
||||
*
|
||||
* The value of each key is an array of configuration items that are sent
|
||||
* to the constructor of each handler. The only required configuration item
|
||||
* is the 'handles' element, which must be an array of integer log levels.
|
||||
* This is most easily handled by using the constants defined in the
|
||||
* `Psr\Log\LogLevel` class.
|
||||
*
|
||||
* Handlers are executed in the order defined in this array, starting with
|
||||
* the handler on top and continuing down.
|
||||
*/
|
||||
public array $handlers = [
|
||||
/*
|
||||
* --------------------------------------------------------------------
|
||||
* File Handler
|
||||
* --------------------------------------------------------------------
|
||||
*/
|
||||
FileHandler::class => [
|
||||
// The log levels that this handler will handle.
|
||||
'handles' => [
|
||||
'critical',
|
||||
'alert',
|
||||
'emergency',
|
||||
'debug',
|
||||
'error',
|
||||
'info',
|
||||
'notice',
|
||||
'warning',
|
||||
],
|
||||
|
||||
/*
|
||||
* The default filename extension for log files.
|
||||
* An extension of 'php' allows for protecting the log files via basic
|
||||
* scripting, when they are to be stored under a publicly accessible directory.
|
||||
*
|
||||
* NOTE: Leaving it blank will default to 'log'.
|
||||
*/
|
||||
'fileExtension' => '',
|
||||
|
||||
/*
|
||||
* The file system permissions to be applied on newly created log files.
|
||||
*
|
||||
* IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal
|
||||
* integer notation (i.e. 0700, 0644, etc.)
|
||||
*/
|
||||
'filePermissions' => 0644,
|
||||
|
||||
/*
|
||||
* Logging Directory Path
|
||||
*
|
||||
* By default, logs are written to WRITEPATH . 'logs/'
|
||||
* Specify a different destination here, if desired.
|
||||
*/
|
||||
'path' => '',
|
||||
],
|
||||
|
||||
/*
|
||||
* The ChromeLoggerHandler requires the use of the Chrome web browser
|
||||
* and the ChromeLogger extension. Uncomment this block to use it.
|
||||
*/
|
||||
// 'CodeIgniter\Log\Handlers\ChromeLoggerHandler' => [
|
||||
// /*
|
||||
// * The log levels that this handler will handle.
|
||||
// */
|
||||
// 'handles' => ['critical', 'alert', 'emergency', 'debug',
|
||||
// 'error', 'info', 'notice', 'warning'],
|
||||
// ],
|
||||
|
||||
/*
|
||||
* The ErrorlogHandler writes the logs to PHP's native `error_log()` function.
|
||||
* Uncomment this block to use it.
|
||||
*/
|
||||
// 'CodeIgniter\Log\Handlers\ErrorlogHandler' => [
|
||||
// /* The log levels this handler can handle. */
|
||||
// 'handles' => ['critical', 'alert', 'emergency', 'debug', 'error', 'info', 'notice', 'warning'],
|
||||
//
|
||||
// /*
|
||||
// * The message type where the error should go. Can be 0 or 4, or use the
|
||||
// * class constants: `ErrorlogHandler::TYPE_OS` (0) or `ErrorlogHandler::TYPE_SAPI` (4)
|
||||
// */
|
||||
// 'messageType' => 0,
|
||||
// ],
|
||||
];
|
||||
}
|
||||
50
app/Config/Migrations.php
Executable file
50
app/Config/Migrations.php
Executable file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class Migrations extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Enable/Disable Migrations
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Migrations are enabled by default.
|
||||
*
|
||||
* You should enable migrations whenever you intend to do a schema migration
|
||||
* and disable it back when you're done.
|
||||
*/
|
||||
public bool $enabled = true;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Migrations Table
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* This is the name of the table that will store the current migrations state.
|
||||
* When migrations runs it will store in a database table which migration
|
||||
* files have already been run.
|
||||
*/
|
||||
public string $table = 'migrations';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Timestamp Format
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* This is the format that will be used when creating new migrations
|
||||
* using the CLI command:
|
||||
* > php spark make:migration
|
||||
*
|
||||
* NOTE: if you set an unsupported format, migration runner will not find
|
||||
* your migration files.
|
||||
*
|
||||
* Supported formats:
|
||||
* - YmdHis_
|
||||
* - Y-m-d-His_
|
||||
* - Y_m_d_His_
|
||||
*/
|
||||
public string $timestampFormat = 'Y-m-d-His_';
|
||||
}
|
||||
534
app/Config/Mimes.php
Executable file
534
app/Config/Mimes.php
Executable file
@ -0,0 +1,534 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
/**
|
||||
* Mimes
|
||||
*
|
||||
* This file contains an array of mime types. It is used by the
|
||||
* Upload class to help identify allowed file types.
|
||||
*
|
||||
* When more than one variation for an extension exist (like jpg, jpeg, etc)
|
||||
* the most common one should be first in the array to aid the guess*
|
||||
* methods. The same applies when more than one mime-type exists for a
|
||||
* single extension.
|
||||
*
|
||||
* When working with mime types, please make sure you have the ´fileinfo´
|
||||
* extension enabled to reliably detect the media types.
|
||||
*
|
||||
* @immutable
|
||||
*/
|
||||
class Mimes
|
||||
{
|
||||
/**
|
||||
* Map of extensions to mime types.
|
||||
*/
|
||||
public static array $mimes = [
|
||||
'hqx' => [
|
||||
'application/mac-binhex40',
|
||||
'application/mac-binhex',
|
||||
'application/x-binhex40',
|
||||
'application/x-mac-binhex40',
|
||||
],
|
||||
'cpt' => 'application/mac-compactpro',
|
||||
'csv' => [
|
||||
'text/csv',
|
||||
'text/x-comma-separated-values',
|
||||
'text/comma-separated-values',
|
||||
'application/vnd.ms-excel',
|
||||
'application/x-csv',
|
||||
'text/x-csv',
|
||||
'application/csv',
|
||||
'application/excel',
|
||||
'application/vnd.msexcel',
|
||||
'text/plain',
|
||||
],
|
||||
'bin' => [
|
||||
'application/macbinary',
|
||||
'application/mac-binary',
|
||||
'application/octet-stream',
|
||||
'application/x-binary',
|
||||
'application/x-macbinary',
|
||||
],
|
||||
'dms' => 'application/octet-stream',
|
||||
'lha' => 'application/octet-stream',
|
||||
'lzh' => 'application/octet-stream',
|
||||
'exe' => [
|
||||
'application/octet-stream',
|
||||
'application/vnd.microsoft.portable-executable',
|
||||
'application/x-dosexec',
|
||||
'application/x-msdownload',
|
||||
],
|
||||
'class' => 'application/octet-stream',
|
||||
'psd' => [
|
||||
'application/x-photoshop',
|
||||
'image/vnd.adobe.photoshop',
|
||||
],
|
||||
'so' => 'application/octet-stream',
|
||||
'sea' => 'application/octet-stream',
|
||||
'dll' => 'application/octet-stream',
|
||||
'oda' => 'application/oda',
|
||||
'pdf' => [
|
||||
'application/pdf',
|
||||
'application/force-download',
|
||||
'application/x-download',
|
||||
],
|
||||
'ai' => [
|
||||
'application/pdf',
|
||||
'application/postscript',
|
||||
],
|
||||
'eps' => 'application/postscript',
|
||||
'ps' => 'application/postscript',
|
||||
'smi' => 'application/smil',
|
||||
'smil' => 'application/smil',
|
||||
'mif' => 'application/vnd.mif',
|
||||
'xls' => [
|
||||
'application/vnd.ms-excel',
|
||||
'application/msexcel',
|
||||
'application/x-msexcel',
|
||||
'application/x-ms-excel',
|
||||
'application/x-excel',
|
||||
'application/x-dos_ms_excel',
|
||||
'application/xls',
|
||||
'application/x-xls',
|
||||
'application/excel',
|
||||
'application/download',
|
||||
'application/vnd.ms-office',
|
||||
'application/msword',
|
||||
],
|
||||
'ppt' => [
|
||||
'application/vnd.ms-powerpoint',
|
||||
'application/powerpoint',
|
||||
'application/vnd.ms-office',
|
||||
'application/msword',
|
||||
],
|
||||
'pptx' => [
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
],
|
||||
'wbxml' => 'application/wbxml',
|
||||
'wmlc' => 'application/wmlc',
|
||||
'dcr' => 'application/x-director',
|
||||
'dir' => 'application/x-director',
|
||||
'dxr' => 'application/x-director',
|
||||
'dvi' => 'application/x-dvi',
|
||||
'gtar' => 'application/x-gtar',
|
||||
'gz' => 'application/x-gzip',
|
||||
'gzip' => 'application/x-gzip',
|
||||
'php' => [
|
||||
'application/x-php',
|
||||
'application/x-httpd-php',
|
||||
'application/php',
|
||||
'text/php',
|
||||
'text/x-php',
|
||||
'application/x-httpd-php-source',
|
||||
],
|
||||
'php4' => 'application/x-httpd-php',
|
||||
'php3' => 'application/x-httpd-php',
|
||||
'phtml' => 'application/x-httpd-php',
|
||||
'phps' => 'application/x-httpd-php-source',
|
||||
'js' => [
|
||||
'application/x-javascript',
|
||||
'text/plain',
|
||||
],
|
||||
'swf' => 'application/x-shockwave-flash',
|
||||
'sit' => 'application/x-stuffit',
|
||||
'tar' => 'application/x-tar',
|
||||
'tgz' => [
|
||||
'application/x-tar',
|
||||
'application/x-gzip-compressed',
|
||||
],
|
||||
'z' => 'application/x-compress',
|
||||
'xhtml' => 'application/xhtml+xml',
|
||||
'xht' => 'application/xhtml+xml',
|
||||
'zip' => [
|
||||
'application/x-zip',
|
||||
'application/zip',
|
||||
'application/x-zip-compressed',
|
||||
'application/s-compressed',
|
||||
'multipart/x-zip',
|
||||
],
|
||||
'rar' => [
|
||||
'application/vnd.rar',
|
||||
'application/x-rar',
|
||||
'application/rar',
|
||||
'application/x-rar-compressed',
|
||||
],
|
||||
'mid' => 'audio/midi',
|
||||
'midi' => 'audio/midi',
|
||||
'mpga' => 'audio/mpeg',
|
||||
'mp2' => 'audio/mpeg',
|
||||
'mp3' => [
|
||||
'audio/mpeg',
|
||||
'audio/mpg',
|
||||
'audio/mpeg3',
|
||||
'audio/mp3',
|
||||
],
|
||||
'aif' => [
|
||||
'audio/x-aiff',
|
||||
'audio/aiff',
|
||||
],
|
||||
'aiff' => [
|
||||
'audio/x-aiff',
|
||||
'audio/aiff',
|
||||
],
|
||||
'aifc' => 'audio/x-aiff',
|
||||
'ram' => 'audio/x-pn-realaudio',
|
||||
'rm' => 'audio/x-pn-realaudio',
|
||||
'rpm' => 'audio/x-pn-realaudio-plugin',
|
||||
'ra' => 'audio/x-realaudio',
|
||||
'rv' => 'video/vnd.rn-realvideo',
|
||||
'wav' => [
|
||||
'audio/x-wav',
|
||||
'audio/wave',
|
||||
'audio/wav',
|
||||
],
|
||||
'bmp' => [
|
||||
'image/bmp',
|
||||
'image/x-bmp',
|
||||
'image/x-bitmap',
|
||||
'image/x-xbitmap',
|
||||
'image/x-win-bitmap',
|
||||
'image/x-windows-bmp',
|
||||
'image/ms-bmp',
|
||||
'image/x-ms-bmp',
|
||||
'application/bmp',
|
||||
'application/x-bmp',
|
||||
'application/x-win-bitmap',
|
||||
],
|
||||
'gif' => 'image/gif',
|
||||
'jpg' => [
|
||||
'image/jpeg',
|
||||
'image/pjpeg',
|
||||
],
|
||||
'jpeg' => [
|
||||
'image/jpeg',
|
||||
'image/pjpeg',
|
||||
],
|
||||
'jpe' => [
|
||||
'image/jpeg',
|
||||
'image/pjpeg',
|
||||
],
|
||||
'jp2' => [
|
||||
'image/jp2',
|
||||
'video/mj2',
|
||||
'image/jpx',
|
||||
'image/jpm',
|
||||
],
|
||||
'j2k' => [
|
||||
'image/jp2',
|
||||
'video/mj2',
|
||||
'image/jpx',
|
||||
'image/jpm',
|
||||
],
|
||||
'jpf' => [
|
||||
'image/jp2',
|
||||
'video/mj2',
|
||||
'image/jpx',
|
||||
'image/jpm',
|
||||
],
|
||||
'jpg2' => [
|
||||
'image/jp2',
|
||||
'video/mj2',
|
||||
'image/jpx',
|
||||
'image/jpm',
|
||||
],
|
||||
'jpx' => [
|
||||
'image/jp2',
|
||||
'video/mj2',
|
||||
'image/jpx',
|
||||
'image/jpm',
|
||||
],
|
||||
'jpm' => [
|
||||
'image/jp2',
|
||||
'video/mj2',
|
||||
'image/jpx',
|
||||
'image/jpm',
|
||||
],
|
||||
'mj2' => [
|
||||
'image/jp2',
|
||||
'video/mj2',
|
||||
'image/jpx',
|
||||
'image/jpm',
|
||||
],
|
||||
'mjp2' => [
|
||||
'image/jp2',
|
||||
'video/mj2',
|
||||
'image/jpx',
|
||||
'image/jpm',
|
||||
],
|
||||
'png' => [
|
||||
'image/png',
|
||||
'image/x-png',
|
||||
],
|
||||
'webp' => 'image/webp',
|
||||
'tif' => 'image/tiff',
|
||||
'tiff' => 'image/tiff',
|
||||
'css' => [
|
||||
'text/css',
|
||||
'text/plain',
|
||||
],
|
||||
'html' => [
|
||||
'text/html',
|
||||
'text/plain',
|
||||
],
|
||||
'htm' => [
|
||||
'text/html',
|
||||
'text/plain',
|
||||
],
|
||||
'shtml' => [
|
||||
'text/html',
|
||||
'text/plain',
|
||||
],
|
||||
'txt' => 'text/plain',
|
||||
'text' => 'text/plain',
|
||||
'log' => [
|
||||
'text/plain',
|
||||
'text/x-log',
|
||||
],
|
||||
'rtx' => 'text/richtext',
|
||||
'rtf' => 'text/rtf',
|
||||
'xml' => [
|
||||
'application/xml',
|
||||
'text/xml',
|
||||
'text/plain',
|
||||
],
|
||||
'xsl' => [
|
||||
'application/xml',
|
||||
'text/xsl',
|
||||
'text/xml',
|
||||
],
|
||||
'mpeg' => 'video/mpeg',
|
||||
'mpg' => 'video/mpeg',
|
||||
'mpe' => 'video/mpeg',
|
||||
'qt' => 'video/quicktime',
|
||||
'mov' => 'video/quicktime',
|
||||
'avi' => [
|
||||
'video/x-msvideo',
|
||||
'video/msvideo',
|
||||
'video/avi',
|
||||
'application/x-troff-msvideo',
|
||||
],
|
||||
'movie' => 'video/x-sgi-movie',
|
||||
'doc' => [
|
||||
'application/msword',
|
||||
'application/vnd.ms-office',
|
||||
],
|
||||
'docx' => [
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/zip',
|
||||
'application/msword',
|
||||
'application/x-zip',
|
||||
],
|
||||
'dot' => [
|
||||
'application/msword',
|
||||
'application/vnd.ms-office',
|
||||
],
|
||||
'dotx' => [
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/zip',
|
||||
'application/msword',
|
||||
],
|
||||
'xlsx' => [
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'application/zip',
|
||||
'application/vnd.ms-excel',
|
||||
'application/msword',
|
||||
'application/x-zip',
|
||||
],
|
||||
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
|
||||
'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
|
||||
'word' => [
|
||||
'application/msword',
|
||||
'application/octet-stream',
|
||||
],
|
||||
'xl' => 'application/excel',
|
||||
'eml' => 'message/rfc822',
|
||||
'json' => [
|
||||
'application/json',
|
||||
'text/json',
|
||||
],
|
||||
'pem' => [
|
||||
'application/x-x509-user-cert',
|
||||
'application/x-pem-file',
|
||||
'application/octet-stream',
|
||||
],
|
||||
'p10' => [
|
||||
'application/x-pkcs10',
|
||||
'application/pkcs10',
|
||||
],
|
||||
'p12' => 'application/x-pkcs12',
|
||||
'p7a' => 'application/x-pkcs7-signature',
|
||||
'p7c' => [
|
||||
'application/pkcs7-mime',
|
||||
'application/x-pkcs7-mime',
|
||||
],
|
||||
'p7m' => [
|
||||
'application/pkcs7-mime',
|
||||
'application/x-pkcs7-mime',
|
||||
],
|
||||
'p7r' => 'application/x-pkcs7-certreqresp',
|
||||
'p7s' => 'application/pkcs7-signature',
|
||||
'crt' => [
|
||||
'application/x-x509-ca-cert',
|
||||
'application/x-x509-user-cert',
|
||||
'application/pkix-cert',
|
||||
],
|
||||
'crl' => [
|
||||
'application/pkix-crl',
|
||||
'application/pkcs-crl',
|
||||
],
|
||||
'der' => 'application/x-x509-ca-cert',
|
||||
'kdb' => 'application/octet-stream',
|
||||
'pgp' => 'application/pgp',
|
||||
'gpg' => 'application/gpg-keys',
|
||||
'sst' => 'application/octet-stream',
|
||||
'csr' => 'application/octet-stream',
|
||||
'rsa' => 'application/x-pkcs7',
|
||||
'cer' => [
|
||||
'application/pkix-cert',
|
||||
'application/x-x509-ca-cert',
|
||||
],
|
||||
'3g2' => 'video/3gpp2',
|
||||
'3gp' => [
|
||||
'video/3gp',
|
||||
'video/3gpp',
|
||||
],
|
||||
'mp4' => 'video/mp4',
|
||||
'm4a' => 'audio/x-m4a',
|
||||
'f4v' => [
|
||||
'video/mp4',
|
||||
'video/x-f4v',
|
||||
],
|
||||
'flv' => 'video/x-flv',
|
||||
'webm' => 'video/webm',
|
||||
'aac' => 'audio/x-acc',
|
||||
'm4u' => 'application/vnd.mpegurl',
|
||||
'm3u' => 'text/plain',
|
||||
'xspf' => 'application/xspf+xml',
|
||||
'vlc' => 'application/videolan',
|
||||
'wmv' => [
|
||||
'video/x-ms-wmv',
|
||||
'video/x-ms-asf',
|
||||
],
|
||||
'au' => 'audio/x-au',
|
||||
'ac3' => 'audio/ac3',
|
||||
'flac' => 'audio/x-flac',
|
||||
'ogg' => [
|
||||
'audio/ogg',
|
||||
'video/ogg',
|
||||
'application/ogg',
|
||||
],
|
||||
'kmz' => [
|
||||
'application/vnd.google-earth.kmz',
|
||||
'application/zip',
|
||||
'application/x-zip',
|
||||
],
|
||||
'kml' => [
|
||||
'application/vnd.google-earth.kml+xml',
|
||||
'application/xml',
|
||||
'text/xml',
|
||||
],
|
||||
'ics' => 'text/calendar',
|
||||
'ical' => 'text/calendar',
|
||||
'zsh' => 'text/x-scriptzsh',
|
||||
'7zip' => [
|
||||
'application/x-compressed',
|
||||
'application/x-zip-compressed',
|
||||
'application/zip',
|
||||
'multipart/x-zip',
|
||||
],
|
||||
'cdr' => [
|
||||
'application/cdr',
|
||||
'application/coreldraw',
|
||||
'application/x-cdr',
|
||||
'application/x-coreldraw',
|
||||
'image/cdr',
|
||||
'image/x-cdr',
|
||||
'zz-application/zz-winassoc-cdr',
|
||||
],
|
||||
'wma' => [
|
||||
'audio/x-ms-wma',
|
||||
'video/x-ms-asf',
|
||||
],
|
||||
'jar' => [
|
||||
'application/java-archive',
|
||||
'application/x-java-application',
|
||||
'application/x-jar',
|
||||
'application/x-compressed',
|
||||
],
|
||||
'svg' => [
|
||||
'image/svg+xml',
|
||||
'image/svg',
|
||||
'application/xml',
|
||||
'text/xml',
|
||||
],
|
||||
'vcf' => 'text/x-vcard',
|
||||
'srt' => [
|
||||
'text/srt',
|
||||
'text/plain',
|
||||
],
|
||||
'vtt' => [
|
||||
'text/vtt',
|
||||
'text/plain',
|
||||
],
|
||||
'ico' => [
|
||||
'image/x-icon',
|
||||
'image/x-ico',
|
||||
'image/vnd.microsoft.icon',
|
||||
],
|
||||
'stl' => [
|
||||
'application/sla',
|
||||
'application/vnd.ms-pki.stl',
|
||||
'application/x-navistyle',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Attempts to determine the best mime type for the given file extension.
|
||||
*
|
||||
* @return string|null The mime type found, or none if unable to determine.
|
||||
*/
|
||||
public static function guessTypeFromExtension(string $extension)
|
||||
{
|
||||
$extension = trim(strtolower($extension), '. ');
|
||||
|
||||
if (! array_key_exists($extension, static::$mimes)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return is_array(static::$mimes[$extension]) ? static::$mimes[$extension][0] : static::$mimes[$extension];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to determine the best file extension for a given mime type.
|
||||
*
|
||||
* @param string|null $proposedExtension - default extension (in case there is more than one with the same mime type)
|
||||
*
|
||||
* @return string|null The extension determined, or null if unable to match.
|
||||
*/
|
||||
public static function guessExtensionFromType(string $type, ?string $proposedExtension = null)
|
||||
{
|
||||
$type = trim(strtolower($type), '. ');
|
||||
|
||||
$proposedExtension = trim(strtolower($proposedExtension ?? ''));
|
||||
|
||||
if (
|
||||
$proposedExtension !== ''
|
||||
&& array_key_exists($proposedExtension, static::$mimes)
|
||||
&& in_array($type, (array) static::$mimes[$proposedExtension], true)
|
||||
) {
|
||||
// The detected mime type matches with the proposed extension.
|
||||
return $proposedExtension;
|
||||
}
|
||||
|
||||
// Reverse check the mime type list if no extension was proposed.
|
||||
// This search is order sensitive!
|
||||
foreach (static::$mimes as $ext => $types) {
|
||||
if (in_array($type, (array) $types, true)) {
|
||||
return $ext;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
84
app/Config/Modules.php
Executable file
84
app/Config/Modules.php
Executable file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Modules\Modules as BaseModules;
|
||||
|
||||
/**
|
||||
* Modules Configuration.
|
||||
*
|
||||
* NOTE: This class is required prior to Autoloader instantiation,
|
||||
* and does not extend BaseConfig.
|
||||
*
|
||||
* @immutable
|
||||
*/
|
||||
class Modules extends BaseModules
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Enable Auto-Discovery?
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* If true, then auto-discovery will happen across all elements listed in
|
||||
* $aliases below. If false, no auto-discovery will happen at all,
|
||||
* giving a slight performance boost.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $enabled = true;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Enable Auto-Discovery Within Composer Packages?
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* If true, then auto-discovery will happen across all namespaces loaded
|
||||
* by Composer, as well as the namespaces configured locally.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $discoverInComposer = true;
|
||||
|
||||
/**
|
||||
* The Composer package list for Auto-Discovery
|
||||
* This setting is optional.
|
||||
*
|
||||
* E.g.:
|
||||
* [
|
||||
* 'only' => [
|
||||
* // List up all packages to auto-discover
|
||||
* 'codeigniter4/shield',
|
||||
* ],
|
||||
* ]
|
||||
* or
|
||||
* [
|
||||
* 'exclude' => [
|
||||
* // List up packages to exclude.
|
||||
* 'pestphp/pest',
|
||||
* ],
|
||||
* ]
|
||||
*
|
||||
* @var array{only?: list<string>, exclude?: list<string>}
|
||||
*/
|
||||
public $composerPackages = [];
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Auto-Discovery Rules
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Aliases list of all discovery classes that will be active and used during
|
||||
* the current application request.
|
||||
*
|
||||
* If it is not listed, only the base application elements will be used.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
public $aliases = [
|
||||
'events',
|
||||
'filters',
|
||||
'registrars',
|
||||
'routes',
|
||||
'services',
|
||||
];
|
||||
}
|
||||
37
app/Config/Pager.php
Executable file
37
app/Config/Pager.php
Executable file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class Pager extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Templates
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Pagination links are rendered out using views to configure their
|
||||
* appearance. This array contains aliases and the view names to
|
||||
* use when rendering the links.
|
||||
*
|
||||
* Within each view, the Pager object will be available as $pager,
|
||||
* and the desired group as $pagerGroup;
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $templates = [
|
||||
'default_full' => 'CodeIgniter\Pager\Views\default_full',
|
||||
'default_simple' => 'CodeIgniter\Pager\Views\default_simple',
|
||||
'default_head' => 'CodeIgniter\Pager\Views\default_head',
|
||||
];
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Items Per Page
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The default number of results shown in a single page.
|
||||
*/
|
||||
public int $perPage = 20;
|
||||
}
|
||||
80
app/Config/Paths.php
Executable file
80
app/Config/Paths.php
Executable file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
/**
|
||||
* Paths
|
||||
*
|
||||
* Holds the paths that are used by the system to
|
||||
* locate the main directories, app, system, etc.
|
||||
*
|
||||
* Modifying these allows you to restructure your application,
|
||||
* share a system folder between multiple applications, and more.
|
||||
*
|
||||
* All paths are relative to the project's root folder.
|
||||
*
|
||||
* NOTE: This class is required prior to Autoloader instantiation,
|
||||
* and does not extend BaseConfig.
|
||||
*
|
||||
* @immutable
|
||||
*/
|
||||
class Paths
|
||||
{
|
||||
/**
|
||||
* ---------------------------------------------------------------
|
||||
* SYSTEM FOLDER NAME
|
||||
* ---------------------------------------------------------------
|
||||
*
|
||||
* This must contain the name of your "system" folder. Include
|
||||
* the path if the folder is not in the same directory as this file.
|
||||
*/
|
||||
public string $systemDirectory = __DIR__ . '/../../system';
|
||||
|
||||
/**
|
||||
* ---------------------------------------------------------------
|
||||
* APPLICATION FOLDER NAME
|
||||
* ---------------------------------------------------------------
|
||||
*
|
||||
* If you want this front controller to use a different "app"
|
||||
* folder than the default one you can set its name here. The folder
|
||||
* can also be renamed or relocated anywhere on your server. If
|
||||
* you do, use a full server path.
|
||||
*
|
||||
* @see http://codeigniter.com/user_guide/general/managing_apps.html
|
||||
*/
|
||||
public string $appDirectory = __DIR__ . '/..';
|
||||
|
||||
/**
|
||||
* ---------------------------------------------------------------
|
||||
* WRITABLE DIRECTORY NAME
|
||||
* ---------------------------------------------------------------
|
||||
*
|
||||
* This variable must contain the name of your "writable" directory.
|
||||
* The writable directory allows you to group all directories that
|
||||
* need write permission to a single place that can be tucked away
|
||||
* for maximum security, keeping it out of the app and/or
|
||||
* system directories.
|
||||
*/
|
||||
public string $writableDirectory = __DIR__ . '/../../writable';
|
||||
|
||||
/**
|
||||
* ---------------------------------------------------------------
|
||||
* TESTS DIRECTORY NAME
|
||||
* ---------------------------------------------------------------
|
||||
*
|
||||
* This variable must contain the name of your "tests" directory.
|
||||
*/
|
||||
public string $testsDirectory = __DIR__ . '/../../tests';
|
||||
|
||||
/**
|
||||
* ---------------------------------------------------------------
|
||||
* VIEW DIRECTORY NAME
|
||||
* ---------------------------------------------------------------
|
||||
*
|
||||
* This variable must contain the name of the directory that
|
||||
* contains the view files used by your application. By
|
||||
* default this is in `app/Views`. This value
|
||||
* is used when no value is provided to `Services::renderer()`.
|
||||
*/
|
||||
public string $viewDirectory = __DIR__ . '/../Views';
|
||||
}
|
||||
387
app/Config/PtRenewalReminderConfig.php
Normal file
387
app/Config/PtRenewalReminderConfig.php
Normal file
@ -0,0 +1,387 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use Config\Database;
|
||||
|
||||
class PtRenewalReminderConfig extends BaseConfig
|
||||
{
|
||||
public bool $enabled = false;
|
||||
|
||||
public bool $daily = false;
|
||||
|
||||
/** @var string[] Lowercase three-letter day abbreviations, e.g. mon, tue */
|
||||
public array $days = [];
|
||||
|
||||
/** @var int[] Client type ids: 1=Group, 2=Individual */
|
||||
public array $clientTypes = [];
|
||||
|
||||
/** Whitelisted: renewal_date | policy_end_date */
|
||||
public string $dateField = 'renewal_date';
|
||||
|
||||
/** @var int[] Reminder offsets: positive = days before due, negative = days after due */
|
||||
public array $daysBeforeList = [30];
|
||||
|
||||
public bool $includeClientEmail = false;
|
||||
|
||||
/** When true, also include policies whose dateField is already past */
|
||||
public bool $includeOverdue = false;
|
||||
|
||||
/** @var string[] */
|
||||
public array $toEmails = [];
|
||||
|
||||
/** @var string[] */
|
||||
public array $ccEmails = [];
|
||||
|
||||
/** @var string[] */
|
||||
public array $bccEmails = [];
|
||||
|
||||
public string $fromMail = '';
|
||||
|
||||
/** Loaded from notifications.subject when present */
|
||||
public string $mailSubject = '';
|
||||
|
||||
/** Loaded from notifications.mail_content when present */
|
||||
public string $mailContent = '';
|
||||
|
||||
public ?int $notificationId = null;
|
||||
|
||||
private const ALLOWED_DATE_FIELDS = ['renewal_date', 'policy_end_date'];
|
||||
|
||||
public const TEMPLATE_NAME = 'retail_reminder_mail';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
// Env defaults (seed / fallback)
|
||||
$defaults = [
|
||||
'enabled' => env('pt.renewalReminder.enabled', 'true'),
|
||||
'daily' => env('pt.renewalReminder.daily', 'true'),
|
||||
'days' => env('pt.renewalReminder.days', 'mon,tue,wed,thu,fri'),
|
||||
'clientTypes' => env('pt.renewalReminder.clientTypes', '2'),
|
||||
'dateField' => env('pt.renewalReminder.dateField', 'renewal_date'),
|
||||
'daysBefore' => env('pt.renewalReminder.daysBefore', '30'),
|
||||
'includeClientEmail' => env('pt.renewalReminder.includeClientEmail', 'true'),
|
||||
'includeOverdue' => env('pt.renewalReminder.includeOverdue', 'false'),
|
||||
'toEmails' => env('pt.renewalReminder.toEmails', ''),
|
||||
'ccEmails' => env('pt.renewalReminder.ccEmails', ''),
|
||||
'bccEmails' => env('pt.renewalReminder.bccEmails', ''),
|
||||
'fromMail' => env('pt.renewalReminder.fromMail', ''),
|
||||
];
|
||||
|
||||
$dbConfig = $this->loadConfigFromNotification();
|
||||
$merged = array_merge($defaults, $dbConfig);
|
||||
|
||||
$this->applyMergedConfig($merged);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function loadConfigFromNotification(): array
|
||||
{
|
||||
try {
|
||||
$db = Database::connect();
|
||||
$row = $db->table('notifications')
|
||||
->where('template_name', self::TEMPLATE_NAME)
|
||||
->groupStart()
|
||||
->where('client_id', null)
|
||||
->orWhere('client_id', 0)
|
||||
->orWhere('client_id', '')
|
||||
->groupEnd()
|
||||
->orderBy('id', 'DESC')
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
if (empty($row)) {
|
||||
log_message('error', '[PtRenewalReminderConfig] No notifications row for retail_reminder_mail; using env defaults');
|
||||
return [];
|
||||
}
|
||||
|
||||
$this->notificationId = isset($row['id']) ? (int) $row['id'] : null;
|
||||
$this->mailSubject = trim((string) ($row['subject'] ?? ''));
|
||||
$this->mailContent = (string) ($row['mail_content'] ?? '');
|
||||
|
||||
$json = trim((string) ($row['config_json'] ?? ''));
|
||||
if ($json === '') {
|
||||
log_message('error', '[PtRenewalReminderConfig] config_json empty for notification_id=' . ($this->notificationId ?? 'n/a'));
|
||||
return [];
|
||||
}
|
||||
|
||||
$decoded = json_decode($json, true);
|
||||
if (! is_array($decoded)) {
|
||||
log_message('error', '[PtRenewalReminderConfig] Invalid config_json for notification_id=' . ($this->notificationId ?? 'n/a'));
|
||||
return [];
|
||||
}
|
||||
|
||||
return $decoded;
|
||||
} catch (\Throwable $e) {
|
||||
log_message(
|
||||
'error',
|
||||
'[PtRenewalReminderConfig] Failed loading notification config: ' . $e->getMessage()
|
||||
);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $merged
|
||||
*/
|
||||
private function applyMergedConfig(array $merged): void
|
||||
{
|
||||
$this->enabled = $this->toBool($merged['enabled'] ?? false);
|
||||
$this->daily = $this->toBool($merged['daily'] ?? false);
|
||||
|
||||
$daysRaw = $merged['days'] ?? 'mon,tue,wed,thu,fri';
|
||||
if (is_array($daysRaw)) {
|
||||
$daysRaw = implode(',', $daysRaw);
|
||||
}
|
||||
$this->days = array_values(array_filter(array_map(
|
||||
static fn (string $day): string => strtolower(substr(trim($day), 0, 3)),
|
||||
explode(',', (string) $daysRaw)
|
||||
)));
|
||||
|
||||
$clientTypesRaw = $merged['clientTypes'] ?? '2';
|
||||
if (is_array($clientTypesRaw)) {
|
||||
$this->clientTypes = array_values(array_unique(array_filter(array_map(
|
||||
static fn ($type): int => (int) $type,
|
||||
$clientTypesRaw
|
||||
), static fn (int $type): bool => $type > 0)));
|
||||
} else {
|
||||
$this->clientTypes = array_values(array_unique(array_filter(array_map(
|
||||
static fn (string $type): int => (int) trim($type),
|
||||
explode(',', (string) $clientTypesRaw)
|
||||
), static fn (int $type): bool => $type > 0)));
|
||||
}
|
||||
|
||||
$dateField = strtolower(trim((string) ($merged['dateField'] ?? 'renewal_date')));
|
||||
if (! in_array($dateField, self::ALLOWED_DATE_FIELDS, true)) {
|
||||
log_message(
|
||||
'error',
|
||||
'[PtRenewalReminderConfig] Invalid dateField "' . $dateField . '", falling back to renewal_date'
|
||||
);
|
||||
$dateField = 'renewal_date';
|
||||
}
|
||||
$this->dateField = $dateField;
|
||||
|
||||
$this->daysBeforeList = self::parseDaysBeforeList($merged['daysBefore'] ?? 30);
|
||||
|
||||
$this->includeClientEmail = $this->toBool($merged['includeClientEmail'] ?? true);
|
||||
$this->includeOverdue = $this->toBool($merged['includeOverdue'] ?? false);
|
||||
|
||||
$this->toEmails = $this->parseEmailList($merged['toEmails'] ?? '');
|
||||
$this->ccEmails = $this->parseEmailList($merged['ccEmails'] ?? '');
|
||||
$this->bccEmails = $this->parseEmailList($merged['bccEmails'] ?? '');
|
||||
$this->fromMail = trim((string) ($merged['fromMail'] ?? ''));
|
||||
}
|
||||
|
||||
public function shouldRunToday(): bool
|
||||
{
|
||||
if (! $this->enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->daily) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$today = strtolower(date('D'));
|
||||
|
||||
return in_array($today, $this->days, true);
|
||||
}
|
||||
|
||||
public function getDateField(): string
|
||||
{
|
||||
return $this->dateField;
|
||||
}
|
||||
|
||||
/**
|
||||
* First / primary offset (legacy callers).
|
||||
*/
|
||||
public function getDaysBefore(): int
|
||||
{
|
||||
return $this->daysBeforeList[0] ?? 30;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getDaysBeforeList(): array
|
||||
{
|
||||
return $this->daysBeforeList;
|
||||
}
|
||||
|
||||
public function getDaysBeforeDisplay(): string
|
||||
{
|
||||
return implode(', ', $this->daysBeforeList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map each offset to the calendar date it matches (today + offset).
|
||||
*
|
||||
* @return array<int, string> offset => Y-m-d
|
||||
*/
|
||||
public function getTargetDates(?string $today = null): array
|
||||
{
|
||||
$today = $today ?: date('Y-m-d');
|
||||
$map = [];
|
||||
foreach ($this->daysBeforeList as $offset) {
|
||||
$map[(int) $offset] = date('Y-m-d', strtotime($today . ' ' . sprintf('%+d days', (int) $offset)));
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse "30, 15, 7, 1, -2, -5, -10" (or a single int / array) into unique offsets.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function parseDaysBeforeList(mixed $value): array
|
||||
{
|
||||
if (is_int($value) || is_float($value)) {
|
||||
$raw = [(int) $value];
|
||||
} elseif (is_array($value)) {
|
||||
$raw = $value;
|
||||
} else {
|
||||
$raw = preg_split('/[,\s]+/', trim((string) $value), -1, PREG_SPLIT_NO_EMPTY) ?: [];
|
||||
}
|
||||
|
||||
$out = [];
|
||||
$seen = [];
|
||||
foreach ($raw as $item) {
|
||||
$item = trim((string) $item);
|
||||
if ($item === '' || ! is_numeric($item)) {
|
||||
continue;
|
||||
}
|
||||
$n = (int) $item;
|
||||
if ($n > 365 || $n < -365 || isset($seen[$n])) {
|
||||
continue;
|
||||
}
|
||||
$seen[$n] = true;
|
||||
$out[] = $n;
|
||||
}
|
||||
|
||||
return $out !== [] ? $out : [30];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getClientTypes(): array
|
||||
{
|
||||
return $this->clientTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getToEmails(): array
|
||||
{
|
||||
return $this->toEmails;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getCcEmails(): array
|
||||
{
|
||||
return $this->ccEmails;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getBccEmails(): array
|
||||
{
|
||||
return $this->bccEmails;
|
||||
}
|
||||
|
||||
public function getFromMail(): string
|
||||
{
|
||||
return $this->fromMail;
|
||||
}
|
||||
|
||||
public function includeClientEmail(): bool
|
||||
{
|
||||
return $this->includeClientEmail;
|
||||
}
|
||||
|
||||
public function includeOverdue(): bool
|
||||
{
|
||||
return $this->includeOverdue;
|
||||
}
|
||||
|
||||
public function getMailSubject(): string
|
||||
{
|
||||
return $this->mailSubject;
|
||||
}
|
||||
|
||||
public function getMailContent(): string
|
||||
{
|
||||
return $this->mailContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten config for UI / API response.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'enabled' => $this->enabled,
|
||||
'daily' => $this->daily,
|
||||
'days' => implode(',', $this->days),
|
||||
'clientTypes' => $this->clientTypes,
|
||||
'dateField' => $this->dateField,
|
||||
'daysBefore' => $this->getDaysBeforeDisplay(),
|
||||
'includeClientEmail' => $this->includeClientEmail,
|
||||
'includeOverdue' => $this->includeOverdue,
|
||||
'toEmails' => implode(',', $this->toEmails),
|
||||
'ccEmails' => implode(',', $this->ccEmails),
|
||||
'bccEmails' => implode(',', $this->bccEmails),
|
||||
'fromMail' => $this->fromMail,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function parseEmailList(mixed $value): array
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
$emails = array_map('trim', $value);
|
||||
} else {
|
||||
$emails = array_map('trim', explode(',', (string) $value));
|
||||
}
|
||||
|
||||
$unique = [];
|
||||
foreach ($emails as $email) {
|
||||
if ($email === '' || ! filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
continue;
|
||||
}
|
||||
$key = strtolower($email);
|
||||
if (! isset($unique[$key])) {
|
||||
$unique[$key] = $email;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($unique);
|
||||
}
|
||||
|
||||
private function toBool(mixed $value): bool
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return in_array(strtolower((string) $value), ['1', 'true', 'yes', 'on'], true);
|
||||
}
|
||||
}
|
||||
28
app/Config/Publisher.php
Executable file
28
app/Config/Publisher.php
Executable file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\Publisher as BasePublisher;
|
||||
|
||||
/**
|
||||
* Publisher Configuration
|
||||
*
|
||||
* Defines basic security restrictions for the Publisher class
|
||||
* to prevent abuse by injecting malicious files into a project.
|
||||
*/
|
||||
class Publisher extends BasePublisher
|
||||
{
|
||||
/**
|
||||
* A list of allowed destinations with a (pseudo-)regex
|
||||
* of allowed files for each destination.
|
||||
* Attempts to publish to directories not in this list will
|
||||
* result in a PublisherException. Files that do no fit the
|
||||
* pattern will cause copy/merge to fail.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public $restrictions = [
|
||||
ROOTPATH => '*',
|
||||
FCPATH => '#\.(s?css|js|map|html?|xml|json|webmanifest|ttf|eot|woff2?|gif|jpe?g|tiff?|png|webp|bmp|ico|svg)$#i',
|
||||
];
|
||||
}
|
||||
89
app/Config/RateLimiter.php
Normal file
89
app/Config/RateLimiter.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class RateLimiter extends BaseConfig
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| JWT / Authenticated API Routes
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
public array $jwtApi = [
|
||||
'limit' => 60, // max requests
|
||||
'window' => 60, // window in seconds
|
||||
'violation_soft' => 3, // violations before soft block
|
||||
];
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Auth API Routes (verifyMobile, verifyOTP, etc.)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
public array $authApi = [
|
||||
'limit' => 10, // max requests per window
|
||||
'window' => 180, // window in seconds (3 min)
|
||||
'violation_soft' => 3, // failed attempts before soft block
|
||||
];
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User-Level Progressive Block Durations (seconds)
|
||||
| 0 = permanent until manual unblock
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
public array $userBlock = [
|
||||
'soft_duration' => 0, // permanent, manual unblock only
|
||||
'medium_duration' => 7200, // 2 hours
|
||||
'hard_duration' => 86400, // 24 hours
|
||||
// attempts while at a block level before escalating to next
|
||||
'medium_trigger' => 1, // attempts during soft → medium
|
||||
'hard_trigger' => 1, // attempts during medium → hard
|
||||
];
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| IP-Level Throttle & Progressive Block (independent of user)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
public array $ipBlock = [
|
||||
'limit' => 120, // max requests per window
|
||||
'window' => 60, // window in seconds
|
||||
'violation_soft' => 5, // violations before soft block
|
||||
'soft_duration' => 0, // permanent, manual unblock only
|
||||
'medium_duration' => 7200, // 2 hours
|
||||
'hard_duration' => 86400, // 24 hours
|
||||
'medium_trigger' => 1, // attempts during soft → medium
|
||||
'hard_trigger' => 1, // attempts during medium → hard
|
||||
];
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Key Prefixes
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
public array $cacheKeys = [
|
||||
'ip_count' => 'rl_ip_count_',
|
||||
'ip_violations' => 'rl_ip_viol_',
|
||||
'ip_block' => 'rl_ip_block_',
|
||||
'ip_block_hits' => 'rl_ip_blkhit_',
|
||||
'user_count' => 'rl_usr_count_',
|
||||
'user_violations' => 'rl_usr_viol_',
|
||||
'user_block' => 'rl_usr_block_',
|
||||
'user_block_hits' => 'rl_usr_blkhit_',
|
||||
];
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTP Status Codes per block level
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
public array $statusCodes = [
|
||||
'throttle' => 429,
|
||||
'soft' => 429,
|
||||
'medium' => 403,
|
||||
'hard' => 451,
|
||||
];
|
||||
}
|
||||
82
app/Config/RfqConfig.php
Normal file
82
app/Config/RfqConfig.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class RfqConfig extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* Google Drive parent folder IDs for RFQ and QCR sheets.
|
||||
* Values are loaded from .env (RFQ_PARENT_FOLDER_ID).
|
||||
*/
|
||||
public string $rfqParentFolderId = '';
|
||||
public string $qcrParentFolderId = '';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->rfqParentFolderId = env('RFQ_PARENT_FOLDER_ID', '1MnYh5PTPDlc9mYMGsjmTv02y8EZ-BQf1');
|
||||
$this->qcrParentFolderId = env('RFQ_PARENT_FOLDER_ID', '1MnYh5PTPDlc9mYMGsjmTv02y8EZ-BQf1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Default permissions for created sheets.
|
||||
* Copied from GoogleSheetController::$config.
|
||||
*/
|
||||
public array $permissions = [
|
||||
'editors' => [
|
||||
'vitvelz@gmail.com',
|
||||
'velz1990@gmail.com',
|
||||
'venkateshraman786@gmail.com',
|
||||
],
|
||||
'viewers' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* Default protections for RFQ sheets.
|
||||
* Copied from GoogleSheetController::$config.
|
||||
*/
|
||||
public array $protections = [
|
||||
[
|
||||
'range' => 'RFQ Page!B12:C12',
|
||||
'users' => [
|
||||
'velz1990@gmail.com',
|
||||
'vitvelz@gmail.com',
|
||||
'firebase-adminsdk-mcdfe@nhance-ee8d1.iam.gserviceaccount.com',
|
||||
],
|
||||
'groups' => [],
|
||||
],
|
||||
[
|
||||
'range' => 'Claims Page!A1',
|
||||
'users' => [
|
||||
'velz1990@gmail.com',
|
||||
'venkateshraman786@gmail.com',
|
||||
'firebase-adminsdk-mcdfe@nhance-ee8d1.iam.gserviceaccount.com',
|
||||
],
|
||||
'groups' => [],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Placeholder token for past 3 years claims table (written as multi-row values at that cell).
|
||||
*/
|
||||
public string $rfqClaimsPlaceholder = '{{CLAIMS_DETAILS}}';
|
||||
|
||||
/**
|
||||
* Maps Google Sheet placeholder => internal key from buildRfqPlaceholderData().
|
||||
* Add these exact strings in your RFQ templates (any sheet/tab).
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $rfqPlaceholders = [
|
||||
'{{INSURED_NAME}}' => 'insured_name',
|
||||
'{{COMMUNICATION_ADDRESS}}' => 'communication_address',
|
||||
'{{GST}}' => 'gst',
|
||||
'{{PAN}}' => 'pan',
|
||||
'{{POLICY_PERIOD}}' => 'policy_period',
|
||||
'{{OPPORTUNITY_TYPE}}' => 'opportunity_type',
|
||||
'{{RISK_LOCATION}}' => 'risk_location',
|
||||
'{{OCCUPANCY}}' => 'occupancy',
|
||||
'{{CLAIMS_DETAILS}}' => 'claims_details',
|
||||
];
|
||||
}
|
||||
1325
app/Config/Routes.php
Executable file
1325
app/Config/Routes.php
Executable file
File diff suppressed because it is too large
Load Diff
114
app/Config/Routing.php
Executable file
114
app/Config/Routing.php
Executable file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\Routing as BaseRouting;
|
||||
|
||||
/**
|
||||
* Routing configuration
|
||||
*/
|
||||
class Routing extends BaseRouting
|
||||
{
|
||||
/**
|
||||
* An array of files that contain route definitions.
|
||||
* Route files are read in order, with the first match
|
||||
* found taking precedence.
|
||||
*
|
||||
* Default: APPPATH . 'Config/Routes.php'
|
||||
*/
|
||||
public array $routeFiles = [
|
||||
APPPATH . 'Config/Routes.php',
|
||||
APPPATH . 'Config/HrLoginDiagnoseRoutes.php',
|
||||
];
|
||||
|
||||
/**
|
||||
* The default namespace to use for Controllers when no other
|
||||
* namespace has been specified.
|
||||
*
|
||||
* Default: 'App\Controllers'
|
||||
*/
|
||||
public string $defaultNamespace = 'App\Controllers';
|
||||
|
||||
/**
|
||||
* The default controller to use when no other controller has been
|
||||
* specified.
|
||||
*
|
||||
* Default: 'Home'
|
||||
*/
|
||||
public string $defaultController = 'Home';
|
||||
|
||||
/**
|
||||
* The default method to call on the controller when no other
|
||||
* method has been set in the route.
|
||||
*
|
||||
* Default: 'index'
|
||||
*/
|
||||
public string $defaultMethod = 'index';
|
||||
|
||||
/**
|
||||
* Whether to translate dashes in URIs to underscores.
|
||||
* Primarily useful when using the auto-routing.
|
||||
*
|
||||
* Default: false
|
||||
*/
|
||||
public bool $translateURIDashes = false;
|
||||
|
||||
/**
|
||||
* Sets the class/method that should be called if routing doesn't
|
||||
* find a match. It can be either a closure or the controller/method
|
||||
* name exactly like a route is defined: Users::index
|
||||
*
|
||||
* This setting is passed to the Router class and handled there.
|
||||
*
|
||||
* If you want to use a closure, you will have to set it in the
|
||||
* class constructor or the routes file by calling:
|
||||
*
|
||||
* $routes->set404Override(function() {
|
||||
* // Do something here
|
||||
* });
|
||||
*
|
||||
* Example:
|
||||
* public $override404 = 'App\Errors::show404';
|
||||
*/
|
||||
public ?string $override404 = null;
|
||||
|
||||
/**
|
||||
* If TRUE, the system will attempt to match the URI against
|
||||
* Controllers by matching each segment against folders/files
|
||||
* in APPPATH/Controllers, when a match wasn't found against
|
||||
* defined routes.
|
||||
*
|
||||
* If FALSE, will stop searching and do NO automatic routing.
|
||||
*/
|
||||
public bool $autoRoute = false;
|
||||
|
||||
/**
|
||||
* If TRUE, will enable the use of the 'prioritize' option
|
||||
* when defining routes.
|
||||
*
|
||||
* Default: false
|
||||
*/
|
||||
public bool $prioritize = false;
|
||||
|
||||
/**
|
||||
* Map of URI segments and namespaces. For Auto Routing (Improved).
|
||||
*
|
||||
* The key is the first URI segment. The value is the controller namespace.
|
||||
* E.g.,
|
||||
* [
|
||||
* 'blog' => 'Acme\Blog\Controllers',
|
||||
* ]
|
||||
*
|
||||
* @var array [ uri_segment => namespace ]
|
||||
*/
|
||||
public array $moduleRoutes = [];
|
||||
}
|
||||
101
app/Config/Security.php
Executable file
101
app/Config/Security.php
Executable file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class Security extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* CSRF Protection Method
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Protection Method for Cross Site Request Forgery protection.
|
||||
*
|
||||
* @var string 'cookie' or 'session'
|
||||
*/
|
||||
public string $csrfProtection = 'cookie';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* CSRF Token Randomization
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Randomize the CSRF Token for added security.
|
||||
*/
|
||||
public bool $tokenRandomize = true;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* CSRF Token Name
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Token name for Cross Site Request Forgery protection.
|
||||
*/
|
||||
public string $tokenName = 'csrf_test_name';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* CSRF Header Name
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Header name for Cross Site Request Forgery protection.
|
||||
*/
|
||||
public string $headerName = 'X-CSRF-TOKEN';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* CSRF Cookie Name
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Cookie name for Cross Site Request Forgery protection.
|
||||
*/
|
||||
public string $cookieName = 'csrf_cookie_name';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* CSRF Expires
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Expiration time for Cross Site Request Forgery protection cookie.
|
||||
*
|
||||
* Defaults to two hours (in seconds).
|
||||
*/
|
||||
public int $expires = 7200;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* CSRF Regenerate
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Regenerate CSRF Token on every submission.
|
||||
*/
|
||||
public bool $regenerate = false;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* CSRF Redirect
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Redirect to previous page with error on failure.
|
||||
*/
|
||||
public bool $redirect = true;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* CSRF SameSite
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Setting for CSRF SameSite cookie token.
|
||||
*
|
||||
* Allowed values are: None - Lax - Strict - ''.
|
||||
*
|
||||
* Defaults to `Lax` as recommended in this link:
|
||||
*
|
||||
* @see https://portswigger.net/web-security/csrf/samesite-cookies
|
||||
*
|
||||
* @deprecated `Config\Cookie` $samesite property is used.
|
||||
*/
|
||||
public string $samesite = 'Lax';
|
||||
}
|
||||
114
app/Config/Services.php
Executable file
114
app/Config/Services.php
Executable file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseService;
|
||||
use App\Libraries\Slug;
|
||||
use App\Libraries\MyLogger;
|
||||
use App\Libraries\GmailAPI;
|
||||
use App\Libraries\MyGoogleDrive;
|
||||
use App\Libraries\RuleImportService;
|
||||
use App\Libraries\DataServiceSqlite;
|
||||
use App\Libraries\S3Service;
|
||||
use App\Libraries\FileStorageService;
|
||||
use App\Controllers\Home;
|
||||
|
||||
/**
|
||||
* Services Configuration file.
|
||||
*
|
||||
* Services are simply other classes/libraries that the system uses
|
||||
* to do its job. This is used by CodeIgniter to allow the core of the
|
||||
* framework to be swapped out easily without affecting the usage within
|
||||
* the rest of your application.
|
||||
*
|
||||
* This file holds any application-specific services, or service overrides
|
||||
* that you might need. An example has been included with the general
|
||||
* method format you should use for your service methods. For more examples,
|
||||
* see the core Services file at system/Config/Services.php.
|
||||
*/
|
||||
class Services extends BaseService
|
||||
{
|
||||
|
||||
public static function slug($getShared = true)
|
||||
{
|
||||
if ($getShared) {
|
||||
return static::getSharedInstance('slug');
|
||||
}
|
||||
|
||||
return new Slug();
|
||||
}
|
||||
|
||||
public static function home($getShared = true)
|
||||
{
|
||||
if ($getShared) {
|
||||
return static::getSharedInstance('home');
|
||||
}
|
||||
|
||||
return new Home();
|
||||
}
|
||||
|
||||
public static function mylogger($getShared = true)
|
||||
{
|
||||
if ($getShared) {
|
||||
return static::getSharedInstance('mylogger');
|
||||
}
|
||||
|
||||
return new MyLogger();
|
||||
}
|
||||
|
||||
|
||||
public static function dataServiceSqlite($getShared = true)
|
||||
{
|
||||
if ($getShared) {
|
||||
return static::getSharedInstance('dataServiceSqlite');
|
||||
}
|
||||
|
||||
return new DataServiceSqlite();
|
||||
}
|
||||
|
||||
public static function gmailapi($getShared = true)
|
||||
{
|
||||
if ($getShared) {
|
||||
return static::getSharedInstance('gmailapi');
|
||||
}
|
||||
|
||||
return new GmailAPI();
|
||||
}
|
||||
|
||||
public static function myGoogleDrive($getShared = true)
|
||||
{
|
||||
if ($getShared) {
|
||||
return static::getSharedInstance('myGoogleDrive');
|
||||
}
|
||||
|
||||
return new MyGoogleDrive();
|
||||
}
|
||||
|
||||
public static function ruleImportService($getShared = true)
|
||||
{
|
||||
if ($getShared) {
|
||||
return static::getSharedInstance('ruleImportService');
|
||||
}
|
||||
|
||||
return new RuleImportService();
|
||||
}
|
||||
|
||||
public static function getS3Service($getShared = true)
|
||||
{
|
||||
if ($getShared) {
|
||||
return static::getSharedInstance('getS3Service');
|
||||
}
|
||||
|
||||
return new S3Service();
|
||||
}
|
||||
|
||||
public static function getFileStorageService($getShared = true)
|
||||
{
|
||||
if ($getShared) {
|
||||
return static::getSharedInstance('getFileStorageService');
|
||||
}
|
||||
|
||||
return new FileStorageService();
|
||||
}
|
||||
|
||||
}
|
||||
102
app/Config/Session.php
Executable file
102
app/Config/Session.php
Executable file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use CodeIgniter\Session\Handlers\BaseHandler;
|
||||
use CodeIgniter\Session\Handlers\FileHandler;
|
||||
|
||||
class Session extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Session Driver
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The session storage driver to use:
|
||||
* - `CodeIgniter\Session\Handlers\FileHandler`
|
||||
* - `CodeIgniter\Session\Handlers\DatabaseHandler`
|
||||
* - `CodeIgniter\Session\Handlers\MemcachedHandler`
|
||||
* - `CodeIgniter\Session\Handlers\RedisHandler`
|
||||
*
|
||||
* @var class-string<BaseHandler>
|
||||
*/
|
||||
public string $driver = FileHandler::class;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Session Cookie Name
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The session cookie name, must contain only [0-9a-z_-] characters
|
||||
*/
|
||||
public string $cookieName = 'ci_session';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Session Expiration
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The number of SECONDS you want the session to last.
|
||||
* Setting to 0 (zero) means expire when the browser is closed.
|
||||
*/
|
||||
public int $expiration = 7200;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Session Save Path
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The location to save sessions to and is driver dependent.
|
||||
*
|
||||
* For the 'files' driver, it's a path to a writable directory.
|
||||
* WARNING: Only absolute paths are supported!
|
||||
*
|
||||
* For the 'database' driver, it's a table name.
|
||||
* Please read up the manual for the format with other session drivers.
|
||||
*
|
||||
* IMPORTANT: You are REQUIRED to set a valid save path!
|
||||
*/
|
||||
public string $savePath = WRITEPATH . 'session';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Session Match IP
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Whether to match the user's IP address when reading the session data.
|
||||
*
|
||||
* WARNING: If you're using the database driver, don't forget to update
|
||||
* your session table's PRIMARY KEY when changing this setting.
|
||||
*/
|
||||
public bool $matchIP = false;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Session Time to Update
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* How many seconds between CI regenerating the session ID.
|
||||
*/
|
||||
public int $timeToUpdate = 300;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Session Regenerate Destroy
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Whether to destroy session data associated with the old session ID
|
||||
* when auto-regenerating the session ID. When set to FALSE, the data
|
||||
* will be later deleted by the garbage collector.
|
||||
*/
|
||||
public bool $regenerateDestroy = false;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Session Database Group
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* DB Group for the database session.
|
||||
*/
|
||||
public ?string $DBGroup = null;
|
||||
}
|
||||
118
app/Config/Toolbar.php
Executable file
118
app/Config/Toolbar.php
Executable file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use CodeIgniter\Debug\Toolbar\Collectors\Database;
|
||||
use CodeIgniter\Debug\Toolbar\Collectors\Events;
|
||||
use CodeIgniter\Debug\Toolbar\Collectors\Files;
|
||||
use CodeIgniter\Debug\Toolbar\Collectors\Logs;
|
||||
use CodeIgniter\Debug\Toolbar\Collectors\Routes;
|
||||
use CodeIgniter\Debug\Toolbar\Collectors\Timers;
|
||||
use CodeIgniter\Debug\Toolbar\Collectors\Views;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Debug Toolbar
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The Debug Toolbar provides a way to see information about the performance
|
||||
* and state of your application during that page display. By default it will
|
||||
* NOT be displayed under production environments, and will only display if
|
||||
* `CI_DEBUG` is true, since if it's not, there's not much to display anyway.
|
||||
*/
|
||||
class Toolbar extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Toolbar Collectors
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* List of toolbar collectors that will be called when Debug Toolbar
|
||||
* fires up and collects data from.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public array $collectors = [
|
||||
Timers::class,
|
||||
Database::class,
|
||||
Logs::class,
|
||||
Views::class,
|
||||
// \CodeIgniter\Debug\Toolbar\Collectors\Cache::class,
|
||||
Files::class,
|
||||
Routes::class,
|
||||
Events::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Collect Var Data
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* If set to false var data from the views will not be colleted. Useful to
|
||||
* avoid high memory usage when there are lots of data passed to the view.
|
||||
*/
|
||||
public bool $collectVarData = true;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Max History
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* `$maxHistory` sets a limit on the number of past requests that are stored,
|
||||
* helping to conserve file space used to store them. You can set it to
|
||||
* 0 (zero) to not have any history stored, or -1 for unlimited history.
|
||||
*/
|
||||
public int $maxHistory = 20;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Toolbar Views Path
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* The full path to the the views that are used by the toolbar.
|
||||
* This MUST have a trailing slash.
|
||||
*/
|
||||
public string $viewsPath = SYSTEMPATH . 'Debug/Toolbar/Views/';
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Max Queries
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* If the Database Collector is enabled, it will log every query that the
|
||||
* the system generates so they can be displayed on the toolbar's timeline
|
||||
* and in the query log. This can lead to memory issues in some instances
|
||||
* with hundreds of queries.
|
||||
*
|
||||
* `$maxQueries` defines the maximum amount of queries that will be stored.
|
||||
*/
|
||||
public int $maxQueries = 100;
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Watched Directories
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Contains an array of directories that will be watched for changes and
|
||||
* used to determine if the hot-reload feature should reload the page or not.
|
||||
* We restrict the values to keep performance as high as possible.
|
||||
*
|
||||
* NOTE: The ROOTPATH will be prepended to all values.
|
||||
*/
|
||||
public array $watchedDirectories = [
|
||||
'app',
|
||||
];
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Watched File Extensions
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* Contains an array of file extensions that will be watched for changes and
|
||||
* used to determine if the hot-reload feature should reload the page or not.
|
||||
*/
|
||||
public array $watchedExtensions = [
|
||||
'php', 'css', 'js', 'html', 'svg', 'json', 'env',
|
||||
];
|
||||
}
|
||||
252
app/Config/UserAgents.php
Executable file
252
app/Config/UserAgents.php
Executable file
@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------
|
||||
* User Agents
|
||||
* -------------------------------------------------------------------
|
||||
*
|
||||
* This file contains four arrays of user agent data. It is used by the
|
||||
* User Agent Class to help identify browser, platform, robot, and
|
||||
* mobile device data. The array keys are used to identify the device
|
||||
* and the array values are used to set the actual name of the item.
|
||||
*/
|
||||
class UserAgents extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* -------------------------------------------------------------------
|
||||
* OS Platforms
|
||||
* -------------------------------------------------------------------
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $platforms = [
|
||||
'windows nt 10.0' => 'Windows 10',
|
||||
'windows nt 6.3' => 'Windows 8.1',
|
||||
'windows nt 6.2' => 'Windows 8',
|
||||
'windows nt 6.1' => 'Windows 7',
|
||||
'windows nt 6.0' => 'Windows Vista',
|
||||
'windows nt 5.2' => 'Windows 2003',
|
||||
'windows nt 5.1' => 'Windows XP',
|
||||
'windows nt 5.0' => 'Windows 2000',
|
||||
'windows nt 4.0' => 'Windows NT 4.0',
|
||||
'winnt4.0' => 'Windows NT 4.0',
|
||||
'winnt 4.0' => 'Windows NT',
|
||||
'winnt' => 'Windows NT',
|
||||
'windows 98' => 'Windows 98',
|
||||
'win98' => 'Windows 98',
|
||||
'windows 95' => 'Windows 95',
|
||||
'win95' => 'Windows 95',
|
||||
'windows phone' => 'Windows Phone',
|
||||
'windows' => 'Unknown Windows OS',
|
||||
'android' => 'Android',
|
||||
'blackberry' => 'BlackBerry',
|
||||
'iphone' => 'iOS',
|
||||
'ipad' => 'iOS',
|
||||
'ipod' => 'iOS',
|
||||
'os x' => 'Mac OS X',
|
||||
'ppc mac' => 'Power PC Mac',
|
||||
'freebsd' => 'FreeBSD',
|
||||
'ppc' => 'Macintosh',
|
||||
'linux' => 'Linux',
|
||||
'debian' => 'Debian',
|
||||
'sunos' => 'Sun Solaris',
|
||||
'beos' => 'BeOS',
|
||||
'apachebench' => 'ApacheBench',
|
||||
'aix' => 'AIX',
|
||||
'irix' => 'Irix',
|
||||
'osf' => 'DEC OSF',
|
||||
'hp-ux' => 'HP-UX',
|
||||
'netbsd' => 'NetBSD',
|
||||
'bsdi' => 'BSDi',
|
||||
'openbsd' => 'OpenBSD',
|
||||
'gnu' => 'GNU/Linux',
|
||||
'unix' => 'Unknown Unix OS',
|
||||
'symbian' => 'Symbian OS',
|
||||
];
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------
|
||||
* Browsers
|
||||
* -------------------------------------------------------------------
|
||||
*
|
||||
* The order of this array should NOT be changed. Many browsers return
|
||||
* multiple browser types so we want to identify the subtype first.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $browsers = [
|
||||
'OPR' => 'Opera',
|
||||
'Flock' => 'Flock',
|
||||
'Edge' => 'Spartan',
|
||||
'Edg' => 'Edge',
|
||||
'Chrome' => 'Chrome',
|
||||
// Opera 10+ always reports Opera/9.80 and appends Version/<real version> to the user agent string
|
||||
'Opera.*?Version' => 'Opera',
|
||||
'Opera' => 'Opera',
|
||||
'MSIE' => 'Internet Explorer',
|
||||
'Internet Explorer' => 'Internet Explorer',
|
||||
'Trident.* rv' => 'Internet Explorer',
|
||||
'Shiira' => 'Shiira',
|
||||
'Firefox' => 'Firefox',
|
||||
'Chimera' => 'Chimera',
|
||||
'Phoenix' => 'Phoenix',
|
||||
'Firebird' => 'Firebird',
|
||||
'Camino' => 'Camino',
|
||||
'Netscape' => 'Netscape',
|
||||
'OmniWeb' => 'OmniWeb',
|
||||
'Safari' => 'Safari',
|
||||
'Mozilla' => 'Mozilla',
|
||||
'Konqueror' => 'Konqueror',
|
||||
'icab' => 'iCab',
|
||||
'Lynx' => 'Lynx',
|
||||
'Links' => 'Links',
|
||||
'hotjava' => 'HotJava',
|
||||
'amaya' => 'Amaya',
|
||||
'IBrowse' => 'IBrowse',
|
||||
'Maxthon' => 'Maxthon',
|
||||
'Ubuntu' => 'Ubuntu Web Browser',
|
||||
'Vivaldi' => 'Vivaldi',
|
||||
];
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------
|
||||
* Mobiles
|
||||
* -------------------------------------------------------------------
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $mobiles = [
|
||||
// legacy array, old values commented out
|
||||
'mobileexplorer' => 'Mobile Explorer',
|
||||
// 'openwave' => 'Open Wave',
|
||||
// 'opera mini' => 'Opera Mini',
|
||||
// 'operamini' => 'Opera Mini',
|
||||
// 'elaine' => 'Palm',
|
||||
'palmsource' => 'Palm',
|
||||
// 'digital paths' => 'Palm',
|
||||
// 'avantgo' => 'Avantgo',
|
||||
// 'xiino' => 'Xiino',
|
||||
'palmscape' => 'Palmscape',
|
||||
// 'nokia' => 'Nokia',
|
||||
// 'ericsson' => 'Ericsson',
|
||||
// 'blackberry' => 'BlackBerry',
|
||||
// 'motorola' => 'Motorola'
|
||||
|
||||
// Phones and Manufacturers
|
||||
'motorola' => 'Motorola',
|
||||
'nokia' => 'Nokia',
|
||||
'palm' => 'Palm',
|
||||
'iphone' => 'Apple iPhone',
|
||||
'ipad' => 'iPad',
|
||||
'ipod' => 'Apple iPod Touch',
|
||||
'sony' => 'Sony Ericsson',
|
||||
'ericsson' => 'Sony Ericsson',
|
||||
'blackberry' => 'BlackBerry',
|
||||
'cocoon' => 'O2 Cocoon',
|
||||
'blazer' => 'Treo',
|
||||
'lg' => 'LG',
|
||||
'amoi' => 'Amoi',
|
||||
'xda' => 'XDA',
|
||||
'mda' => 'MDA',
|
||||
'vario' => 'Vario',
|
||||
'htc' => 'HTC',
|
||||
'samsung' => 'Samsung',
|
||||
'sharp' => 'Sharp',
|
||||
'sie-' => 'Siemens',
|
||||
'alcatel' => 'Alcatel',
|
||||
'benq' => 'BenQ',
|
||||
'ipaq' => 'HP iPaq',
|
||||
'mot-' => 'Motorola',
|
||||
'playstation portable' => 'PlayStation Portable',
|
||||
'playstation 3' => 'PlayStation 3',
|
||||
'playstation vita' => 'PlayStation Vita',
|
||||
'hiptop' => 'Danger Hiptop',
|
||||
'nec-' => 'NEC',
|
||||
'panasonic' => 'Panasonic',
|
||||
'philips' => 'Philips',
|
||||
'sagem' => 'Sagem',
|
||||
'sanyo' => 'Sanyo',
|
||||
'spv' => 'SPV',
|
||||
'zte' => 'ZTE',
|
||||
'sendo' => 'Sendo',
|
||||
'nintendo dsi' => 'Nintendo DSi',
|
||||
'nintendo ds' => 'Nintendo DS',
|
||||
'nintendo 3ds' => 'Nintendo 3DS',
|
||||
'wii' => 'Nintendo Wii',
|
||||
'open web' => 'Open Web',
|
||||
'openweb' => 'OpenWeb',
|
||||
|
||||
// Operating Systems
|
||||
'android' => 'Android',
|
||||
'symbian' => 'Symbian',
|
||||
'SymbianOS' => 'SymbianOS',
|
||||
'elaine' => 'Palm',
|
||||
'series60' => 'Symbian S60',
|
||||
'windows ce' => 'Windows CE',
|
||||
|
||||
// Browsers
|
||||
'obigo' => 'Obigo',
|
||||
'netfront' => 'Netfront Browser',
|
||||
'openwave' => 'Openwave Browser',
|
||||
'mobilexplorer' => 'Mobile Explorer',
|
||||
'operamini' => 'Opera Mini',
|
||||
'opera mini' => 'Opera Mini',
|
||||
'opera mobi' => 'Opera Mobile',
|
||||
'fennec' => 'Firefox Mobile',
|
||||
|
||||
// Other
|
||||
'digital paths' => 'Digital Paths',
|
||||
'avantgo' => 'AvantGo',
|
||||
'xiino' => 'Xiino',
|
||||
'novarra' => 'Novarra Transcoder',
|
||||
'vodafone' => 'Vodafone',
|
||||
'docomo' => 'NTT DoCoMo',
|
||||
'o2' => 'O2',
|
||||
|
||||
// Fallback
|
||||
'mobile' => 'Generic Mobile',
|
||||
'wireless' => 'Generic Mobile',
|
||||
'j2me' => 'Generic Mobile',
|
||||
'midp' => 'Generic Mobile',
|
||||
'cldc' => 'Generic Mobile',
|
||||
'up.link' => 'Generic Mobile',
|
||||
'up.browser' => 'Generic Mobile',
|
||||
'smartphone' => 'Generic Mobile',
|
||||
'cellphone' => 'Generic Mobile',
|
||||
];
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------
|
||||
* Robots
|
||||
* -------------------------------------------------------------------
|
||||
*
|
||||
* There are hundred of bots but these are the most common.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $robots = [
|
||||
'googlebot' => 'Googlebot',
|
||||
'msnbot' => 'MSNBot',
|
||||
'baiduspider' => 'Baiduspider',
|
||||
'bingbot' => 'Bing',
|
||||
'slurp' => 'Inktomi Slurp',
|
||||
'yahoo' => 'Yahoo',
|
||||
'ask jeeves' => 'Ask Jeeves',
|
||||
'fastcrawler' => 'FastCrawler',
|
||||
'infoseek' => 'InfoSeek Robot 1.0',
|
||||
'lycos' => 'Lycos',
|
||||
'yandex' => 'YandexBot',
|
||||
'mediapartners-google' => 'MediaPartners Google',
|
||||
'CRAZYWEBCRAWLER' => 'Crazy Webcrawler',
|
||||
'adsbot-google' => 'AdsBot Google',
|
||||
'feedfetcher-google' => 'Feedfetcher Google',
|
||||
'curious george' => 'Curious George',
|
||||
'ia_archiver' => 'Alexa Crawler',
|
||||
'MJ12bot' => 'Majestic-12',
|
||||
'Uptimebot' => 'Uptimebot',
|
||||
];
|
||||
}
|
||||
44
app/Config/Validation.php
Executable file
44
app/Config/Validation.php
Executable file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
use CodeIgniter\Validation\StrictRules\CreditCardRules;
|
||||
use CodeIgniter\Validation\StrictRules\FileRules;
|
||||
use CodeIgniter\Validation\StrictRules\FormatRules;
|
||||
use CodeIgniter\Validation\StrictRules\Rules;
|
||||
|
||||
class Validation extends BaseConfig
|
||||
{
|
||||
// --------------------------------------------------------------------
|
||||
// Setup
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Stores the classes that contain the
|
||||
* rules that are available.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public array $ruleSets = [
|
||||
Rules::class,
|
||||
FormatRules::class,
|
||||
FileRules::class,
|
||||
CreditCardRules::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Specifies the views that are used to display the
|
||||
* errors.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public array $templates = [
|
||||
'list' => 'CodeIgniter\Validation\Views\list',
|
||||
'single' => 'CodeIgniter\Validation\Views\single',
|
||||
];
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Rules
|
||||
// --------------------------------------------------------------------
|
||||
}
|
||||
62
app/Config/View.php
Executable file
62
app/Config/View.php
Executable file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Config;
|
||||
|
||||
use CodeIgniter\Config\View as BaseView;
|
||||
use CodeIgniter\View\ViewDecoratorInterface;
|
||||
|
||||
/**
|
||||
* @phpstan-type ParserCallable (callable(mixed): mixed)
|
||||
* @phpstan-type ParserCallableString (callable(mixed): mixed)&string
|
||||
*/
|
||||
class View extends BaseView
|
||||
{
|
||||
/**
|
||||
* When false, the view method will clear the data between each
|
||||
* call. This keeps your data safe and ensures there is no accidental
|
||||
* leaking between calls, so you would need to explicitly pass the data
|
||||
* to each view. You might prefer to have the data stick around between
|
||||
* calls so that it is available to all views. If that is the case,
|
||||
* set $saveData to true.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $saveData = true;
|
||||
|
||||
/**
|
||||
* Parser Filters map a filter name with any PHP callable. When the
|
||||
* Parser prepares a variable for display, it will chain it
|
||||
* through the filters in the order defined, inserting any parameters.
|
||||
* To prevent potential abuse, all filters MUST be defined here
|
||||
* in order for them to be available for use within the Parser.
|
||||
*
|
||||
* Examples:
|
||||
* { title|esc(js) }
|
||||
* { created_on|date(Y-m-d)|esc(attr) }
|
||||
*
|
||||
* @var array<string, string>
|
||||
* @phpstan-var array<string, ParserCallableString>
|
||||
*/
|
||||
public $filters = [];
|
||||
|
||||
/**
|
||||
* Parser Plugins provide a way to extend the functionality provided
|
||||
* by the core Parser by creating aliases that will be replaced with
|
||||
* any callable. Can be single or tag pair.
|
||||
*
|
||||
* @var array<string, array<string>|callable|string>
|
||||
* @phpstan-var array<string, array<ParserCallableString>|ParserCallableString|ParserCallable>
|
||||
*/
|
||||
public $plugins = [];
|
||||
|
||||
/**
|
||||
* View Decorators are class methods that will be run in sequence to
|
||||
* have a chance to alter the generated output just prior to caching
|
||||
* the results.
|
||||
*
|
||||
* All classes must implement CodeIgniter\View\ViewDecoratorInterface
|
||||
*
|
||||
* @var class-string<ViewDecoratorInterface>[]
|
||||
*/
|
||||
public array $decorators = [];
|
||||
}
|
||||
13
app/Config/google-services.json
Executable file
13
app/Config/google-services.json
Executable file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "service_account",
|
||||
"project_id": "push-notification-enrollment",
|
||||
"private_key_id": "979d0fcd0ce95d31367f3150d34ee0044ca51b0d",
|
||||
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4Xsan6ESIzM12\nG15gRU361Yy6PVmoMzTToWtZHPEcDRd9weFNlCXjmX+WAqdgD/hQ42GcZQVCndse\nEQGJHbRu/ifMKGo53zxOpshNZnCSxojAGWIGrIHFE5kFumiKz9drW9vPWsFLtgx0\nEAW0T7su1MejjH33GftHG8k/q3uyWoU0+OFRD6emhYkFdVsucwR27hE7GsFwE+CF\nnRdSWQ5mu2e8J7q9Qbpgot5d7uk7iJgpiyNdChPvqUZ7XeknVrw1F4REZlbAkVjb\nFuwKg5qtG97LZ1NkyYqFmssA1rNkqSi4+T+2/ERegdwyA6W4/HLT9fPbSl4v1MvB\ntJlGYkMpAgMBAAECggEABhspdCUmo+s4gMleQkz5TK3m58IhZocvoDSv4/cn6xhp\n50HVDucxrAyI47R5y54ryK4HLRFRb8ffmmrQxLRFpgln0wShpAIHMsmmR531a38E\nx2vvya3L7HV+M2jdjn9csJMNwBvO3A2O8wcW0UZ0uhPU+s8r2tOy9UNv0lqBEcKM\nUxCtk7UxcLEM3btDP5I4oEHVo2f5jzPeb99KHbYQV/vc0BtA1wtuYTzGT2EhBpTG\nN1rbQhlj6iJ6vMwHETWrctNMohCQNQEv/QIC/NWYPNRqmtrJfNe29XGWN9IIhv0e\n/ZmhdzoO+uhJW4VaWpI93nB5ZAa6LHqJSg+OyrPoawKBgQDd45Aiq7d+4Z+kDZYu\n72bgW5CGhyck2hrqHpBdHA960AYIftw/wHgI9xb9Z8hi+Lh+W18xDywOLoaz7TJV\nElj2Gh3oj2ZLzDpeyqV9UOgz11dbm6yYUK8jl7zyajBt7RhGcdj+4x+YjUTT6O61\nuxm2ZbKsb/0er/W6DlKDVvpWSwKBgQDUtqowATEaGTrd+eutanRwtv+rdptVcadj\nD4H3XdY4PAsVSp1mT9knGgh7c5elI0lBH/VD/IbM5uPXOc7judnIK4RgKScrJT2P\nekyEJnXyFAZGS3WTFG7fzeXn/cNNx5MCZE2Yay7LqqON8Kg08IvvesiTushrDlMJ\nX8cunSyz2wKBgDIOwZigxq/QNNSs4AHMrpfU8GD5IqKUtde1d3oZ94AMaCAIhqW3\nRR04qS4X+MQjOnP/JxWJR7YXVvpGe8FndzxmHfM2Tqyw8UYrT3RbCVeQsDuRfjmK\nkkhkVhMWU8Co6X4S9xJhqOIglLN97ESBZkaY4Ns4FJGUvsnvqzvIJofLAoGBAKJO\ny2eb0TK/46ozJEELxNOo30efVgGJmpa844e0A1yffDl/2MCT1ve+JpDEcAbi+OeH\nkieRTe6Vk27LvnEHhAT4J6cUX73NSb7sK+x+SGsyGmOS+qEC62M8gdxWRqtXyHX/\nwTG3P1rK1sfcxQy4K57NSrVmxbzijjvN6HdKGS0XAoGATtsid5pypcZ0BsYUp/27\neS6z++ewo7QCVCBPa4Ag4nX56/0kdrECvy/ISMDjggNv+mR8hpyCjfm9ztJ88njB\nUBu1jzLBZ+u5gEjmtu43qvuDJNobyb+evDSReEJmify943vFZqnkIhFlwg1PcS6S\nCd+pr559KWURPIgfxb7S/JU=\n-----END PRIVATE KEY-----\n",
|
||||
"client_email": "firebase-adminsdk-gzzkw@push-notification-enrollment.iam.gserviceaccount.com",
|
||||
"client_id": "109128197176743587316",
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://oauth2.googleapis.com/token",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-gzzkw%40push-notification-enrollment.iam.gserviceaccount.com",
|
||||
"universe_domain": "googleapis.com"
|
||||
}
|
||||
67
app/ControllerCleaners/ThzControllerCleaner.php
Normal file
67
app/ControllerCleaners/ThzControllerCleaner.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\ControllerCleaners;
|
||||
|
||||
use App\Controllers\BaseController;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
|
||||
use App\Models\ThzMasterModel;
|
||||
use App\Models\ThzMasterNotesModel;
|
||||
|
||||
class ThzControllerCleaner extends BaseController
|
||||
{
|
||||
|
||||
protected $thzMasterModel;
|
||||
protected $thzMasterNotesModel;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->thzMasterModel = new ThzMasterModel();
|
||||
$this->thzMasterNotesModel = new ThzMasterNotesModel();
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function ticketCreation($data){
|
||||
|
||||
if(empty($data)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$cleanedData = $data;
|
||||
|
||||
return $cleanedData;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function ticketList($data){
|
||||
|
||||
if(empty($data)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$cleanedData = $data;
|
||||
|
||||
return $cleanedData;
|
||||
|
||||
}
|
||||
|
||||
public function ticketConversation($data){
|
||||
|
||||
if(empty($data)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$cleanedData = $data;
|
||||
|
||||
return $cleanedData;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
20
app/Controllers/AdminController.php
Executable file
20
app/Controllers/AdminController.php
Executable file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use CodeIgniter\HTTP\IncomingRequest;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
use App\Controllers\PublicController;
|
||||
|
||||
//This is a controller act like base controller for business logics which are required
|
||||
// authendication and authrizations also config the routes as per requirement in routes.php
|
||||
class AdminController extends BaseController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
829
app/Controllers/Api/NonEbClaimApiController.php
Normal file
829
app/Controllers/Api/NonEbClaimApiController.php
Normal file
@ -0,0 +1,829 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers\Api;
|
||||
|
||||
use App\Controllers\BaseController;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
|
||||
use App\Models\NonEbTicketMasterModel;
|
||||
use App\Models\NonEbClaimAssetModel;
|
||||
use App\Models\TicketHistoryModel;
|
||||
use App\Models\TicketClaimStatusModel;
|
||||
use App\Models\ClaimFilesModel;
|
||||
use App\Models\ClientPolicyModel;
|
||||
use App\Models\PolicyTypeModel;
|
||||
use App\Models\ClientRMModel;
|
||||
use App\Models\EmployeeModel;
|
||||
use App\Models\LevelContactModel;
|
||||
use App\Helpers\JWTToken;
|
||||
|
||||
class NonEbClaimApiController extends BaseController
|
||||
{
|
||||
use ResponseTrait;
|
||||
|
||||
protected $nonEbTicketModel;
|
||||
protected $assetModel;
|
||||
protected $ticketHistoryModel;
|
||||
protected $claimStatusModel;
|
||||
protected $claimFilesModel;
|
||||
protected $clientPolicyModel;
|
||||
protected $policyTypeModel;
|
||||
protected $clientRMModel;
|
||||
protected $myLogger;
|
||||
|
||||
// API system user ID — used as created_by for all inserts (no session)
|
||||
const API_SYSTEM_USER_ID = 0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->nonEbTicketModel = new NonEbTicketMasterModel();
|
||||
$this->assetModel = new NonEbClaimAssetModel();
|
||||
$this->ticketHistoryModel = new TicketHistoryModel();
|
||||
$this->claimStatusModel = new TicketClaimStatusModel();
|
||||
$this->claimFilesModel = new ClaimFilesModel();
|
||||
$this->clientPolicyModel = new ClientPolicyModel();
|
||||
$this->policyTypeModel = new PolicyTypeModel();
|
||||
$this->clientRMModel = new ClientRMModel();
|
||||
$this->myLogger = \Config\Services::mylogger();
|
||||
}
|
||||
|
||||
// ===================== AUTH HELPER =====================
|
||||
|
||||
/**
|
||||
* Decode the JWT from Authorization header and return the resolved user row.
|
||||
* Returns array with normalised keys: id, name, email, mobile.
|
||||
* Returns null if token is missing or invalid.
|
||||
*/
|
||||
protected function getAuthUser(): ?array
|
||||
{
|
||||
$authHeader = $this->request->getHeaderLine('Authorization');
|
||||
if (empty($authHeader)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = JWTToken::validateJWT($authHeader);
|
||||
if ($result['status'] !== true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$decoded = $result['decoded'];
|
||||
|
||||
if (isset($decoded['emp_code'])) {
|
||||
$model = new EmployeeModel();
|
||||
$user = $model->find($decoded['id'] ?? null);
|
||||
if (!$user) return null;
|
||||
return [
|
||||
'id' => $user['id'],
|
||||
'name' => $user['name'] ?? '',
|
||||
'email' => $user['email_corporate'] ?? $user['email'] ?? '',
|
||||
'mobile' => $user['mobile'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
$model = new LevelContactModel();
|
||||
$user = $model->find($decoded['post_hr_id'] ?? null);
|
||||
if (!$user) return null;
|
||||
return [
|
||||
'id' => $user['id'],
|
||||
'name' => $user['name'] ?? '',
|
||||
'email' => $user['email'] ?? '',
|
||||
'mobile' => $user['mobile'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
// ===================== SHARED HELPERS =====================
|
||||
|
||||
/**
|
||||
* Convert DD-MM-YYYY date fields to Y-m-d for DB storage.
|
||||
*/
|
||||
protected function formatDatesForClaim(array $data): array
|
||||
{
|
||||
$dateFields = ['loss_date', 'intimation_recd_date', 'intimated_to_insurer_date', 'eta_for_documents'];
|
||||
foreach ($dateFields as $field) {
|
||||
if (empty($data[$field])) {
|
||||
$data[$field] = null;
|
||||
} else {
|
||||
$data[$field] = change_date_format($data[$field], null, 'Y-m-d');
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate check: same client + loss_date + policy_no.
|
||||
*/
|
||||
protected function checkDuplicateNonEbClaim(array $data): bool
|
||||
{
|
||||
$client_id = $data['client_id'] ?? null;
|
||||
$loss_date = $data['loss_date'] ?? null;
|
||||
$policy_no = $data['policy_no'] ?? null;
|
||||
|
||||
if (empty($client_id) || empty($loss_date)) return false;
|
||||
|
||||
$query = $this->nonEbTicketModel
|
||||
->where('client_id', $client_id)
|
||||
->where('loss_date', $loss_date)
|
||||
->where('is_active', 1);
|
||||
|
||||
if (!empty($policy_no)) {
|
||||
$query->where('policy_no', $policy_no);
|
||||
}
|
||||
|
||||
return !empty($query->first());
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload the asset_file from the request. Returns filename or null.
|
||||
*/
|
||||
protected function handleAssetFileUpload(): ?string
|
||||
{
|
||||
$file = $this->request->getFile('asset_file');
|
||||
if ($file === null || !$file->isValid() || $file->hasMoved()) {
|
||||
return null;
|
||||
}
|
||||
$uploadPath = WRITEPATH . 'uploads/non_eb_asset_files/';
|
||||
$fileName = storage_file_Upload($file, $uploadPath, UPLOAD_EXT_ASSET_FILES);
|
||||
return !empty($fileName) ? $fileName : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save asset rows (asset_id_code[], serial_no[], etc.) linked to a claim.
|
||||
*/
|
||||
protected function saveAssets(int $claim_id, array $post_data): void
|
||||
{
|
||||
$this->assetModel->where('non_eb_ticket_id', $claim_id)->set(['is_active' => 0])->update();
|
||||
|
||||
$codes = $post_data['asset_id_code'] ?? [];
|
||||
$serials = $post_data['serial_no'] ?? [];
|
||||
$vehicles = $post_data['vehicle_no'] ?? [];
|
||||
$descriptions = $post_data['asset_description'] ?? [];
|
||||
|
||||
if (!is_array($codes)) return;
|
||||
|
||||
for ($i = 0; $i < count($codes); $i++) {
|
||||
$code = trim($codes[$i] ?? '');
|
||||
$serial = trim($serials[$i] ?? '');
|
||||
$vehicle = trim($vehicles[$i] ?? '');
|
||||
$desc = trim($descriptions[$i] ?? '');
|
||||
|
||||
if (empty($code) && empty($serial) && empty($vehicle) && empty($desc)) continue;
|
||||
|
||||
$this->assetModel->insert([
|
||||
'non_eb_ticket_id' => $claim_id,
|
||||
'asset_id_code' => $code,
|
||||
'serial_no' => $serial,
|
||||
'vehicle_no' => $vehicle,
|
||||
'asset_description' => $desc,
|
||||
'is_active' => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the initial history row after a new claim is created.
|
||||
*/
|
||||
protected function putHistoryAfterInsert(array $ticket_data, int $ticket_id): void
|
||||
{
|
||||
if (!empty($ticket_data)) {
|
||||
$this->ticketHistoryModel->insert([
|
||||
'ticket_id' => $ticket_id,
|
||||
'field_name' => 'claim_status_id',
|
||||
'display_name' => 'Claim Created',
|
||||
'old_value' => null,
|
||||
'new_value' => $ticket_data['claim_status_id'],
|
||||
'created_by' => self::API_SYSTEM_USER_ID,
|
||||
'is_active' => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== ENDPOINTS =====================
|
||||
|
||||
public function createClaim()
|
||||
{
|
||||
$authUser = $this->getAuthUser();
|
||||
if (!$authUser) {
|
||||
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
$rawBody = $this->request->getBody();
|
||||
$isJson = str_contains($this->request->getHeaderLine('Content-Type'), 'application/json');
|
||||
$body = ($isJson || ($rawBody !== '' && $rawBody !== null))
|
||||
? (json_decode($rawBody, true) ?? $this->request->getPost())
|
||||
: $this->request->getPost();
|
||||
|
||||
// Validate minimal user-facing fields only
|
||||
$rules = [
|
||||
'client_policy_id' => ['rules' => 'required|is_natural_no_zero', 'errors' => ['required' => 'Client Policy is required']],
|
||||
'nature_of_loss' => ['rules' => 'required|min_length[3]', 'errors' => ['required' => 'Nature of Loss is required']],
|
||||
'loss_location' => ['rules' => 'required', 'errors' => ['required' => 'Loss Location is required']],
|
||||
'loss_date' => ['rules' => 'required', 'errors' => ['required' => 'Loss Date is required']],
|
||||
'loss_description' => ['rules' => 'permit_empty'],
|
||||
'loss_estimate' => ['rules' => 'permit_empty|numeric'],
|
||||
'claim_number' => ['rules' => 'permit_empty|regex_match[/^[a-zA-Z0-9\/_-]+$/]'],
|
||||
];
|
||||
|
||||
if (!$this->validateData($body, $rules)) {
|
||||
return $this->respond([
|
||||
'status' => false, 'code' => 400,
|
||||
'message' => 'Input validation failed',
|
||||
'errors' => $this->validator->getErrors(),
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Fetch client_policy — derive all FK fields from it
|
||||
$cp = $this->clientPolicyModel
|
||||
->where('id', (int)$body['client_policy_id'])
|
||||
->where('is_active', 1)
|
||||
->first();
|
||||
|
||||
if (!$cp) {
|
||||
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Client policy not found or inactive'], 404);
|
||||
}
|
||||
|
||||
// Validate policy type is Non-EB or Marine
|
||||
$policyType = $this->policyTypeModel
|
||||
->select('allocg')
|
||||
->where('id', $cp['policy_type_id'])
|
||||
->where('is_active', 1)
|
||||
->first();
|
||||
|
||||
if (!$policyType || !in_array($policyType['allocg'], ['Non-EB', 'Marine'])) {
|
||||
return $this->respond(['status' => false, 'code' => 422, 'message' => 'Only Non-EB or Marine policy types are allowed'], 422);
|
||||
}
|
||||
|
||||
// Fetch ACM from client_rm (level 3)
|
||||
$acm = $this->clientRMModel
|
||||
->where('client_id', $cp['client_id'])
|
||||
->where('level', 3)
|
||||
->where('is_active', 1)
|
||||
->first();
|
||||
|
||||
if (!$acm) {
|
||||
$this->myLogger->logme('error', "[NON_EB_API][createClaim] No ACM found for client_id: {$cp['client_id']}");
|
||||
}
|
||||
|
||||
// Auto-set first claim_status_id for this policy type
|
||||
$firstStatus = $this->claimStatusModel
|
||||
->select('id')
|
||||
->where('ticket_type', $cp['policy_type_id'])
|
||||
->orderBy('id', 'ASC')
|
||||
->first();
|
||||
|
||||
if (!$firstStatus) {
|
||||
return $this->respond(['status' => false, 'code' => 422, 'message' => 'No claim status configured for this policy type'], 422);
|
||||
}
|
||||
|
||||
// Build ticket data — merge user input with server-derived values
|
||||
$ticket_data = [
|
||||
'client_policy_id' => (int)$cp['id'],
|
||||
'client_id' => (int)$cp['client_id'],
|
||||
'branch_id' => (int)$cp['client_branch_id'],
|
||||
'policy_type_id' => (int)$cp['policy_type_id'],
|
||||
'insurer_id' => (int)$cp['insurer_id'],
|
||||
'policy_no' => $cp['policy_no'] ?? null,
|
||||
'acm_id' => $acm ? (int)$acm['user_id'] : null,
|
||||
'claim_status_id' => (int)$firstStatus['id'],
|
||||
'insured_contact_name' => $authUser['name'],
|
||||
'insured_contact_number' => $authUser['mobile'],
|
||||
'insured_contact_email' => $authUser['email'],
|
||||
'nature_of_loss' => $body['nature_of_loss'],
|
||||
'loss_location' => $body['loss_location'],
|
||||
'loss_date' => $body['loss_date'],
|
||||
'loss_description' => $body['loss_description'] ?? null,
|
||||
'loss_estimate' => $body['loss_estimate'] ?? null,
|
||||
'claim_number' => $body['claim_number'] ?? null,
|
||||
'priority' => 1,
|
||||
'created_by' => self::API_SYSTEM_USER_ID,
|
||||
];
|
||||
|
||||
$ticket_data = $this->formatDatesForClaim($ticket_data);
|
||||
|
||||
// Handle optional asset file upload
|
||||
$assetFileName = null;
|
||||
$assetFile = $this->request->getFile('asset_file');
|
||||
|
||||
if ($assetFile !== null && $assetFile->isValid() && !$assetFile->hasMoved()) {
|
||||
$ext = strtolower($assetFile->getClientExtension());
|
||||
$allowed = UPLOAD_EXT_ASSET_FILES; // ['pdf', 'xls', 'xlsx', 'csv']
|
||||
|
||||
if (!in_array($ext, $allowed)) {
|
||||
return $this->respond([
|
||||
'status' => false, 'code' => 415,
|
||||
'message' => 'Unsupported file type: ' . $ext . '. Allowed: ' . implode(', ', $allowed),
|
||||
], 415);
|
||||
}
|
||||
|
||||
if (empty(trim($ticket_data['loss_description'] ?? ''))) {
|
||||
return $this->respond([
|
||||
'status' => false, 'code' => 400,
|
||||
'message' => 'Input validation failed',
|
||||
'errors' => ['loss_description' => 'Loss Description is required when uploading an asset file.'],
|
||||
], 400);
|
||||
}
|
||||
|
||||
$uploadPath = WRITEPATH . 'uploads/non_eb_asset_files/';
|
||||
$assetFileName = storage_file_Upload($assetFile, $uploadPath, $allowed);
|
||||
|
||||
if (empty($assetFileName)) {
|
||||
return $this->respond(['status' => false, 'code' => 500, 'message' => 'Asset file upload failed'], 500);
|
||||
}
|
||||
|
||||
$ticket_data['asset_file'] = $assetFileName;
|
||||
}
|
||||
|
||||
// Duplicate check (runs after derivation so client_id + policy_no are populated)
|
||||
if ($this->checkDuplicateNonEbClaim($ticket_data)) {
|
||||
return $this->respond([
|
||||
'status' => false, 'code' => 409,
|
||||
'message' => 'Duplicate claim found for Client + Loss Date + Policy No combination',
|
||||
], 409);
|
||||
}
|
||||
|
||||
$claim_id = $this->nonEbTicketModel->insert($ticket_data);
|
||||
|
||||
if (!$claim_id) {
|
||||
return $this->respond(['status' => false, 'code' => 500, 'message' => 'Failed to create claim'], 500);
|
||||
}
|
||||
|
||||
$this->saveAssets($claim_id, $body);
|
||||
$this->putHistoryAfterInsert($ticket_data, $claim_id);
|
||||
|
||||
$this->myLogger->logme('error', "[NON_EB_API][createClaim] Claim created. ID: $claim_id, user: {$authUser['id']}");
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'code' => 200,
|
||||
'claim_id' => $claim_id,
|
||||
'asset_file' => $assetFileName,
|
||||
'message' => 'Non-EB Claim created successfully',
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function listClaims()
|
||||
{
|
||||
$authUser = $this->getAuthUser();
|
||||
if (!$authUser) {
|
||||
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
try {
|
||||
$body = $this->request->getJSON(true) ?? [];
|
||||
} catch (\Throwable $e) {
|
||||
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Invalid JSON body'], 400);
|
||||
}
|
||||
$page = max(1, (int)($body['page'] ?? 1));
|
||||
$per_page = min(100, max(1, (int)($body['per_page'] ?? 20)));
|
||||
$offset = ($page - 1) * $per_page;
|
||||
|
||||
$db = db_connect();
|
||||
$builder = $db->table('non_eb_ticket_master tm');
|
||||
|
||||
$builder->select([
|
||||
'tm.id',
|
||||
'tm.claim_number',
|
||||
'tm.nhance_claim_ref_no',
|
||||
'tm.policy_no',
|
||||
'tm.policy_type_id',
|
||||
'tm.claim_status_id',
|
||||
'tcs.claim_status AS status',
|
||||
'tcs.display_name AS status_display',
|
||||
'pt.policy_type AS policy_type_name',
|
||||
'c.client_name',
|
||||
'i.name AS insurer_name',
|
||||
'tm.loss_date',
|
||||
'tm.loss_location',
|
||||
'tm.nature_of_loss',
|
||||
'tm.loss_estimate',
|
||||
'tm.insured_contact_name',
|
||||
'tm.insured_contact_number',
|
||||
'(SELECT first_name FROM user_profiles WHERE user_profiles.id = tm.acm_id) AS acm_name',
|
||||
'DATE_FORMAT(tm.created_at, "%d-%m-%Y") AS created_date',
|
||||
'DATE_FORMAT(tm.updated_at, "%d-%m-%Y") AS updated_date',
|
||||
]);
|
||||
|
||||
$builder->join('clients c', 'c.id = tm.client_id AND c.is_active = 1', 'left');
|
||||
$builder->join('insurers i', 'i.id = tm.insurer_id AND i.is_active = 1', 'left');
|
||||
$builder->join('ticket_claim_status tcs','tcs.id = tm.claim_status_id AND tcs.is_active = 1', 'left');
|
||||
$builder->join('policy_type pt', 'pt.id = tm.policy_type_id AND pt.is_active = 1', 'left');
|
||||
|
||||
$builder->where('tm.is_active', 1);
|
||||
|
||||
// Filters
|
||||
if (!empty($body['client_id'])) $builder->where('md5(tm.client_id)', (int)$body['client_id']);
|
||||
if (!empty($body['insurer_id'])) $builder->where('tm.insurer_id', (int)$body['insurer_id']);
|
||||
if (!empty($body['policy_type_id'])) $builder->where('tm.policy_type_id', (int)$body['policy_type_id']);
|
||||
if (!empty($body['claim_status_id'])) $builder->where('tm.claim_status_id', (int)$body['claim_status_id']);
|
||||
if (!empty($body['claim_number'])) $builder->like('tm.claim_number', $body['claim_number']);
|
||||
if (!empty($body['nhance_claim_ref_no'])) $builder->like('tm.nhance_claim_ref_no', $body['nhance_claim_ref_no']);
|
||||
|
||||
// Date range filter
|
||||
if (!empty($body['date_type']) && !empty($body['start_date']) && !empty($body['end_date'])) {
|
||||
$col = $body['date_type'] === 'updated_date' ? 'tm.updated_at' : 'tm.created_at';
|
||||
$start = date('Y-m-d 00:00:00', strtotime(str_replace('-', '/', $body['start_date'])));
|
||||
$end = date('Y-m-d 23:59:59', strtotime(str_replace('-', '/', $body['end_date'])));
|
||||
$builder->where("$col BETWEEN '$start' AND '$end'");
|
||||
}
|
||||
|
||||
// Exclude terminal statuses by default
|
||||
if (empty($body['show_closed'])) {
|
||||
$builder->whereNotIn('tcs.display_name', ['Claim Settled', 'Claim Closed', 'Claim Rejected', 'Claim Withdrawn']);
|
||||
}
|
||||
|
||||
// COUNT for pagination (clone before limit)
|
||||
$countBuilder = clone $builder;
|
||||
$total = $countBuilder->countAllResults(false);
|
||||
|
||||
$builder->orderBy('tm.id', 'DESC');
|
||||
$builder->limit($per_page, $offset);
|
||||
|
||||
$data = $builder->get()->getResultArray();
|
||||
|
||||
$this->myLogger->logme('error', "[NON_EB_API][listClaims] user: {$authUser['id']}, page: $page, total: $total");
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'code' => 200,
|
||||
'total' => (int)$total,
|
||||
'page' => $page,
|
||||
'per_page' => $per_page,
|
||||
'data' => $data,
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function claimHistory(int $claim_id)
|
||||
{
|
||||
$authUser = $this->getAuthUser();
|
||||
if (!$authUser) {
|
||||
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
// Fetch full claim row with joins (client, insurer, status, policy type, ACM, branch)
|
||||
$raw = $this->nonEbTicketModel->getTicketDataByTicketID($claim_id);
|
||||
|
||||
if (!$raw) {
|
||||
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Claim not found'], 404);
|
||||
}
|
||||
|
||||
// claims_data — major display fields only
|
||||
$claims_data = [
|
||||
'id' => $raw['id'],
|
||||
'nhance_claim_ref_no' => $raw['nhance_claim_ref_no'],
|
||||
'claim_number' => $raw['claim_number'],
|
||||
'policy_no' => $raw['policy_no'],
|
||||
'policy_type_name' => $raw['policy_type_name'],
|
||||
'client_name' => $raw['client_name'],
|
||||
'branch_name' => $raw['branch_name'],
|
||||
'insurer_name' => $raw['insurer_name'],
|
||||
'claim_status' => $raw['claim_status'],
|
||||
'status_display_name' => $raw['status_display_name'],
|
||||
'insured_contact_name' => $raw['insured_contact_name'],
|
||||
'insured_contact_number' => $raw['insured_contact_number'],
|
||||
'insured_contact_email' => $raw['insured_contact_email'],
|
||||
'nature_of_loss' => $raw['nature_of_loss'],
|
||||
'loss_location' => $raw['loss_location'],
|
||||
'loss_date' => $raw['loss_date'],
|
||||
'loss_description' => $raw['loss_description'],
|
||||
'loss_estimate' => $raw['loss_estimate'],
|
||||
'loss_assessed_value' => $raw['loss_assessed_value'],
|
||||
'settled_amount' => $raw['settled_amount'],
|
||||
'settlement_utr' => $raw['settlement_utr'],
|
||||
'intimation_recd_date' => $raw['intimation_recd_date'],
|
||||
'intimated_to_insurer_date'=> $raw['intimated_to_insurer_date'],
|
||||
'surveyor_name' => $raw['surveyor_name'],
|
||||
'surveyor_contact_person' => $raw['surveyor_contact_person'],
|
||||
'surveyor_contact_number' => $raw['surveyor_contact_number'],
|
||||
'acm' => $raw['acm'],
|
||||
'acm_mobile' => $raw['acm_mobile'],
|
||||
'asset_file' => $raw['asset_file'],
|
||||
'priority' => $raw['priority'],
|
||||
'created_at' => $raw['created_at'],
|
||||
'updated_at' => $raw['updated_at'],
|
||||
];
|
||||
|
||||
// required_docs — decoded separately
|
||||
$required_docs = json_decode($raw['required_docs'] ?? '{}', true) ?: [];
|
||||
|
||||
// claims_files
|
||||
$claims_files = $this->claimFilesModel
|
||||
->select('id, doc_name, file_name, mime_type, file_type, docs_for_ir, created_at')
|
||||
->where('ticket_id', $claim_id)
|
||||
->where('ticket_type', 2)
|
||||
->where('is_active', 1)
|
||||
->findAll();
|
||||
|
||||
// ticket_data — claim status history, oldest first
|
||||
$history_rows = $this->ticketHistoryModel
|
||||
->select('new_value, created_at')
|
||||
->where('ticket_id', $claim_id)
|
||||
->where('field_name', 'claim_status_id')
|
||||
->where('is_active', 1)
|
||||
->orderBy('created_at', 'ASC')
|
||||
->findAll();
|
||||
|
||||
$statuses = $this->claimStatusModel
|
||||
->select('id, claim_status, display_name')
|
||||
->where('ticket_type', $raw['policy_type_id'])
|
||||
->where('display_name IS NOT NULL')
|
||||
->where("display_name != ''")
|
||||
->where('is_active', 1)
|
||||
->findAll();
|
||||
|
||||
$display_map = array_column($statuses, 'display_name', 'id');
|
||||
|
||||
$ticket_data = [];
|
||||
foreach ($history_rows as $row) {
|
||||
$status_id = (int)$row['new_value'];
|
||||
$display_name = $display_map[$status_id] ?? null;
|
||||
if (!$display_name) continue;
|
||||
|
||||
$ticket_data[] = [
|
||||
'status' => $display_name,
|
||||
'changed_at' => date('d-m-Y h:i A', strtotime($row['created_at'])),
|
||||
];
|
||||
}
|
||||
|
||||
$this->myLogger->logme('error', "[NON_EB_API][claimHistory] claim_id: $claim_id, steps: " . count($ticket_data) . ", user: {$authUser['id']}");
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'code' => 200,
|
||||
'claim_id' => $claim_id,
|
||||
'claims_data' => $claims_data,
|
||||
'required_docs' => $required_docs,
|
||||
'claims_files' => $claims_files,
|
||||
'ticket_data' => $ticket_data,
|
||||
], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v1/non-eb-claim/statuses
|
||||
* Returns Non-EB claim statuses (ticket_type = 50).
|
||||
*/
|
||||
public function listClaimStatuses()
|
||||
{
|
||||
$authUser = $this->getAuthUser();
|
||||
if (!$authUser) {
|
||||
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
$statuses = $this->claimStatusModel
|
||||
->select('id, claim_status, display_name')
|
||||
->where('ticket_type', 50)
|
||||
->where('is_active', 1)
|
||||
->orderBy('id', 'ASC')
|
||||
->findAll();
|
||||
|
||||
$this->myLogger->logme('error', "[NON_EB_API][listClaimStatuses] user: {$authUser['id']}, count: " . count($statuses));
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'code' => 200,
|
||||
'data' => $statuses,
|
||||
], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/v1/non-eb-claim/policies
|
||||
* Returns Non-EB / Marine policies for a given client (md5) + branch.
|
||||
*
|
||||
* Body:
|
||||
* client_id string MD5 hash of the client's numeric id (required)
|
||||
* client_branch_id int client branch id (required)
|
||||
*/
|
||||
public function listPolicies()
|
||||
{
|
||||
$authUser = $this->getAuthUser();
|
||||
if (!$authUser) {
|
||||
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
try {
|
||||
$body = $this->request->getJSON(true) ?? $this->request->getPost();
|
||||
} catch (\Throwable $e) {
|
||||
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Invalid JSON body'], 400);
|
||||
}
|
||||
$client_id_md5 = trim($body['client_id'] ?? '');
|
||||
$client_branch_id = (int)($body['client_branch_id'] ?? 0);
|
||||
|
||||
$errors = [];
|
||||
if (empty($client_id_md5)) {
|
||||
$errors['client_id'] = 'client_id is required';
|
||||
} elseif (!preg_match('/^[a-f0-9]{32}$/i', $client_id_md5)) {
|
||||
$errors['client_id'] = 'client_id must be a valid MD5 hash';
|
||||
}
|
||||
if ($client_branch_id <= 0) {
|
||||
$errors['client_branch_id'] = 'client_branch_id is required';
|
||||
}
|
||||
if (!empty($errors)) {
|
||||
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Input validation failed', 'errors' => $errors], 400);
|
||||
}
|
||||
|
||||
$db = db_connect();
|
||||
$builder = $db->table('client_policy cp');
|
||||
|
||||
$builder->select([
|
||||
'cp.id',
|
||||
'cp.policy_no',
|
||||
'cp.policy_type_id',
|
||||
'pt.policy_type AS policy_type_name',
|
||||
'cp.insurer_id',
|
||||
'i.name AS insurer_name',
|
||||
'i.short_name AS insurer_short_name',
|
||||
'DATE_FORMAT(cp.policy_start_date, "%d-%m-%Y") AS policy_start_date',
|
||||
'DATE_FORMAT(cp.policy_end_date, "%d-%m-%Y") AS policy_end_date',
|
||||
]);
|
||||
$builder->join('policy_type pt', 'pt.id = cp.policy_type_id', 'left');
|
||||
$builder->join('insurers i', 'i.id = cp.insurer_id AND i.is_active = 1', 'left');
|
||||
$builder->where('MD5(cp.client_id)', $client_id_md5);
|
||||
$builder->where('cp.client_branch_id', $client_branch_id);
|
||||
$builder->where('cp.is_active', 1);
|
||||
$builder->whereIn('pt.allocg', ['Non-EB', 'Marine']);
|
||||
$builder->orderBy('cp.id', 'DESC');
|
||||
|
||||
$policies = $builder->get()->getResultArray();
|
||||
|
||||
$this->myLogger->logme('error', "[NON_EB_API][listPolicies] user: {$authUser['id']}, client_md5: $client_id_md5, branch: $client_branch_id, count: " . count($policies));
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'code' => 200,
|
||||
'total' => count($policies),
|
||||
'data' => $policies,
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function uploadRequiredDoc()
|
||||
{
|
||||
$authUser = $this->getAuthUser();
|
||||
if (!$authUser) {
|
||||
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
$ticket_id = $this->request->getPost('ticket_id') ?? null;
|
||||
$get_file_data = $this->request->getFiles('claim_docs') ?? null;
|
||||
$get_docs_name = $this->request->getPost('claim_doc_names') ?? [];
|
||||
$required_docs = $this->request->getPost('required_docs') ?? null;
|
||||
|
||||
if (is_string($get_docs_name)) {
|
||||
$decoded = json_decode($get_docs_name, true);
|
||||
$get_docs_name = json_last_error() === JSON_ERROR_NONE ? $decoded : [];
|
||||
} elseif (!is_array($get_docs_name)) {
|
||||
$get_docs_name = [];
|
||||
}
|
||||
|
||||
$file_data = [];
|
||||
if (!empty($get_file_data)) {
|
||||
$file_path = WRITEPATH . 'uploads/claim_files/';
|
||||
$file_data = storage_multi_file_Upload($get_file_data, $file_path, $get_docs_name, UPLOAD_EXT_CLAIM_DOCS);
|
||||
}
|
||||
|
||||
if (!empty($file_data) && !empty($ticket_id)) {
|
||||
foreach ($file_data as $value) {
|
||||
$this->claimFilesModel->insert([
|
||||
'ticket_id' => $ticket_id,
|
||||
'ticket_type' => 2,
|
||||
'doc_name' => $value['doc_name'],
|
||||
'file_name' => $value['file_name'],
|
||||
'url' => $value['file_path'],
|
||||
'file_type' => 2,
|
||||
'mime_type' => getMimeTypeByFileName($value['file_name']),
|
||||
'docs_for_ir' => 1,
|
||||
'is_active' => 1,
|
||||
'created_by' => self::API_SYSTEM_USER_ID,
|
||||
]);
|
||||
}
|
||||
|
||||
db_connect()->query(
|
||||
'UPDATE non_eb_ticket_master SET required_docs = ? WHERE id = ?',
|
||||
[$required_docs, $ticket_id]
|
||||
);
|
||||
|
||||
$this->myLogger->logme('error', "[NON_EB_API][uploadRequiredDoc] Docs uploaded. ticket_id: $ticket_id, count: " . count($file_data) . ", user: {$authUser['id']}");
|
||||
|
||||
return $this->respond(['status' => true, 'code' => 200, 'message' => 'Files uploaded successfully'], 200);
|
||||
}
|
||||
|
||||
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Failed to upload the file'], 400);
|
||||
}
|
||||
|
||||
public function uploadRequiredDoc_v1(int $claim_id)
|
||||
{
|
||||
$authUser = $this->getAuthUser();
|
||||
if (!$authUser) {
|
||||
return $this->respond(['status' => false, 'code' => 401, 'message' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
// Verify claim exists
|
||||
$claim = $this->nonEbTicketModel
|
||||
->select('id, client_id, required_docs')
|
||||
->where('id', $claim_id)
|
||||
->where('is_active', 1)
|
||||
->first();
|
||||
|
||||
if (!$claim) {
|
||||
return $this->respond(['status' => false, 'code' => 404, 'message' => 'Claim not found'], 404);
|
||||
}
|
||||
|
||||
// Validate required_docs checklist is configured
|
||||
$required_docs = json_decode($claim['required_docs'] ?? '{}', true);
|
||||
if (empty($required_docs) || empty($required_docs['docs'])) {
|
||||
return $this->respond(['status' => false, 'code' => 422, 'message' => 'No required documents checklist configured for this claim'], 422);
|
||||
}
|
||||
|
||||
// Check if checklist is locked
|
||||
if (!empty($required_docs['is_action_freeze'])) {
|
||||
return $this->respond(['status' => false, 'code' => 423, 'message' => 'Document checklist is locked for this claim'], 423);
|
||||
}
|
||||
|
||||
// Validate inputs
|
||||
$document_name = trim($this->request->getPost('document_name') ?? '');
|
||||
if (empty($document_name)) {
|
||||
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Input validation failed', 'errors' => ['document_name' => 'document_name is required']], 400);
|
||||
}
|
||||
|
||||
$file = $this->request->getFile('file');
|
||||
if (!$file || !$file->isValid() || $file->hasMoved()) {
|
||||
return $this->respond(['status' => false, 'code' => 400, 'message' => 'Input validation failed', 'errors' => ['file' => 'A valid file is required']], 400);
|
||||
}
|
||||
|
||||
// Validate file extension
|
||||
$allowed = defined('UPLOAD_EXT_CLAIM_DOCS') ? UPLOAD_EXT_CLAIM_DOCS : ['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx', 'xls', 'xlsx'];
|
||||
$ext = strtolower($file->getClientExtension());
|
||||
if (is_array($allowed) && !in_array($ext, $allowed)) {
|
||||
return $this->respond(['status' => false, 'code' => 415, 'message' => 'Unsupported file type: ' . $ext], 415);
|
||||
}
|
||||
|
||||
// Find matching doc in checklist (exact case-sensitive match)
|
||||
$matched_index = null;
|
||||
foreach ($required_docs['docs'] as $i => $doc) {
|
||||
if (($doc['document_name'] ?? '') === $document_name) {
|
||||
$matched_index = $i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($matched_index === null) {
|
||||
return $this->respond([
|
||||
'status' => false, 'code' => 404,
|
||||
'message' => "Document '{$document_name}' not found in required documents list",
|
||||
], 404);
|
||||
}
|
||||
|
||||
// Upload file and update required_docs atomically
|
||||
$db = db_connect();
|
||||
$db->transStart();
|
||||
|
||||
$upload_path = WRITEPATH . 'uploads/claim_files/';
|
||||
$uploaded = storage_claim_file_Upload($file, $upload_path, UPLOAD_EXT_CLAIM_DOCS);
|
||||
|
||||
if ($uploaded === '') {
|
||||
$db->transRollback();
|
||||
return $this->respond(['status' => false, 'code' => 500, 'message' => 'File upload failed'], 500);
|
||||
}
|
||||
|
||||
$disk_name = $uploaded['disk_name'];
|
||||
$display_name = $uploaded['display_name'];
|
||||
|
||||
// Insert into claim_files
|
||||
$this->claimFilesModel->insert([
|
||||
'ticket_id' => $claim_id,
|
||||
'ticket_type' => 2,
|
||||
'doc_name' => $document_name,
|
||||
'file_name' => $display_name,
|
||||
'url' => $disk_name,
|
||||
'file_type' => 2,
|
||||
'mime_type' => getMimeTypeByFileName($display_name),
|
||||
'is_active' => 1,
|
||||
'created_by' => self::API_SYSTEM_USER_ID,
|
||||
]);
|
||||
|
||||
// Mark document as received in required_docs JSON
|
||||
$required_docs['docs'][$matched_index]['document_received'] = true;
|
||||
$updated_json = json_encode($required_docs);
|
||||
|
||||
$db->query('UPDATE non_eb_ticket_master SET required_docs = ? WHERE id = ?', [$updated_json, $claim_id]);
|
||||
|
||||
$db->transComplete();
|
||||
|
||||
if (!$db->transStatus()) {
|
||||
$this->myLogger->logme('error', "[NON_EB_API][uploadRequiredDoc] Transaction failed. claim_id: $claim_id, doc: $document_name, user: {$authUser['id']}");
|
||||
return $this->respond(['status' => false, 'code' => 500, 'message' => 'Failed to save document. Please try again.'], 500);
|
||||
}
|
||||
|
||||
$file_id = $this->claimFilesModel->insertID();
|
||||
$download_url = base_url('downloadClaimFile/') . $file_id;
|
||||
|
||||
$this->myLogger->logme('error', "[NON_EB_API][uploadRequiredDoc] Doc uploaded. claim_id: $claim_id, doc: $document_name, file: $disk_name, user: {$authUser['id']}");
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'code' => 200,
|
||||
'message' => 'Document uploaded successfully',
|
||||
'claim_id' => $claim_id,
|
||||
'document_name' => $document_name,
|
||||
'download_url' => $download_url,
|
||||
'required_docs' => $required_docs,
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
1002
app/Controllers/ApiServiceController.php
Normal file
1002
app/Controllers/ApiServiceController.php
Normal file
File diff suppressed because it is too large
Load Diff
814
app/Controllers/AppContentManagementController.php
Executable file
814
app/Controllers/AppContentManagementController.php
Executable file
@ -0,0 +1,814 @@
|
||||
<?php
|
||||
|
||||
// declare(strict_types=1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
|
||||
use App\Models\AddImgModel;
|
||||
use App\Models\FEContentModel;
|
||||
use App\Models\ClientModel;
|
||||
use App\Models\FAQModel;
|
||||
|
||||
class AppContentManagementController extends AdminController
|
||||
{
|
||||
use ResponseTrait;
|
||||
protected $myLogger;
|
||||
protected $addImgModel;
|
||||
protected $feContentModel;
|
||||
protected $clientModel;
|
||||
protected $faqModel;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->myLogger = \Config\Services::mylogger();
|
||||
$this->addImgModel = new AddImgModel();
|
||||
$this->feContentModel = new FEContentModel();
|
||||
$this->clientModel = new ClientModel();
|
||||
$this->faqModel = new FAQModel();
|
||||
}
|
||||
//listing
|
||||
public function add_image_index()
|
||||
{
|
||||
$headerData['tab_name'] = 'Advertisement Images';
|
||||
$headerData['page_name'] = 'Advertisement Images';
|
||||
// $data['addImageList'] = $this->addImgModel->findAll();
|
||||
$data['addImageList'] = $this->addImgModel->select('advertisement_images.*,
|
||||
clients.client_name,
|
||||
clients.short_name,
|
||||
CASE WHEN advertisement_images.is_active = 1 THEN "Active" ELSE "Inactive" END AS status', false)
|
||||
->join('clients', 'advertisement_images.client_id = clients.id', 'left')
|
||||
->where('advertisement_images.is_active', 1)
|
||||
->findAll();
|
||||
|
||||
$data['client'] = $this->clientModel->where('is_active', 1)->where('client_type', 1)->findAll();
|
||||
|
||||
// dd($data);
|
||||
|
||||
echo view('layout/header', $headerData);
|
||||
echo view('add_image_list', $data);
|
||||
echo view('layout/footer');
|
||||
|
||||
// $this->loadLayout('client_onboarding', $data);
|
||||
}
|
||||
|
||||
// add and edit
|
||||
public function add_advertise_image() {
|
||||
try {
|
||||
|
||||
$data = $this->request->getPost();
|
||||
$sanitized_post_data = sanitizeInputArrayAdvanced($data);
|
||||
$id = (int)($sanitized_post_data['add_image_id'] ?? 0);
|
||||
|
||||
$rules = [
|
||||
'add_image_id' => [
|
||||
'rules' => 'permit_empty|integer|is_natural',
|
||||
'errors' => [
|
||||
'integer' => 'Image ID must be a valid number',
|
||||
'is_natural' => 'Image ID must be a non-negative number'
|
||||
]
|
||||
],
|
||||
'client_id' => [
|
||||
'rules' => 'required|integer|is_natural',
|
||||
'errors' => [
|
||||
'required' => 'Client is required',
|
||||
'integer' => 'Invalid client selected',
|
||||
'is_natural' => 'Invalid client selected',
|
||||
]
|
||||
],
|
||||
'advertise_image' => [
|
||||
'rules' => ($id === 0 ? 'uploaded[advertise_image]|' : '')
|
||||
. 'is_image[advertise_image]'
|
||||
. '|mime_in[advertise_image,image/jpg,image/jpeg,image/png]'
|
||||
. '|max_size[advertise_image,200]'
|
||||
. '|max_dims[advertise_image,1640,664]',
|
||||
'errors' => [
|
||||
'uploaded' => 'Image is required',
|
||||
'is_image' => 'File must be an image',
|
||||
'mime_in' => 'Only JPG, JPEG, PNG allowed',
|
||||
'max_size' => 'Image size must not exceed 200 KB',
|
||||
'max_dims' => 'Image dimensions must not exceed 1640x664 pixels',
|
||||
]
|
||||
],
|
||||
];
|
||||
if (!$this->validate($rules)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => false,
|
||||
'message' => 'Input validation failed',
|
||||
'code' => 400,
|
||||
'errors' => $this->validator->getErrors()
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
$file = $this->request->getFile('advertise_image');
|
||||
$client_id = (int)($sanitized_post_data['client_id'] ?? 0);
|
||||
|
||||
if ($client_id !== 0) {
|
||||
$clientExists = $this->clientModel->where('id', $client_id)->where('is_active', 1)->first();
|
||||
if (!$clientExists) {
|
||||
return $this->respond(['status' => false, 'message' => 'Invalid client selected.'], 400);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$file || !$file->isValid()) {
|
||||
return $this->respond(['status' => false, 'message' => 'No file uploaded or invalid file.'], 400);
|
||||
}
|
||||
|
||||
if (!validate_upload_extension($file, UPLOAD_EXT_IMAGES)) {
|
||||
return $this->respond(['status' => false, 'message' => 'Only JPG, JPEG, PNG files are allowed.'], 400);
|
||||
}
|
||||
|
||||
$dimInfo = @getimagesize($file->getTempName());
|
||||
if ($dimInfo === false || (int) $dimInfo[0] !== 1640 || (int) $dimInfo[1] !== 664) {
|
||||
return $this->respond(['status' => false, 'message' => 'Image dimensions must be exactly 1640x664 pixels.'], 400);
|
||||
}
|
||||
|
||||
$fileName = sanitize_upload_filename($file->getClientName());
|
||||
$existing = $this->addImgModel->where('name', $fileName)->where('client_id', $client_id)->where('is_active', 1)->first();
|
||||
if($existing){ return $this->respond(['status' => false, 'message' => 'This file has already been uploaded in active state.'], 400); }
|
||||
|
||||
$uploadPath = ROOTPATH . 'public/uploads/add_image_upload/';
|
||||
if (!is_dir($uploadPath)) mkdir($uploadPath, 0755, true);
|
||||
|
||||
$file->move($uploadPath, $fileName);
|
||||
|
||||
$details = ['name' => $fileName,'client_id'=>$client_id];
|
||||
|
||||
if ($id === 0) {
|
||||
$this->addImgModel->insert($details);
|
||||
} else {
|
||||
$existingRecord = $this->addImgModel->find($id);
|
||||
if (!$existingRecord) {
|
||||
return $this->respond(['status' => false, 'message' => 'Record not found for update.'], 404);
|
||||
}
|
||||
$this->addImgModel->update($id, $details);
|
||||
}
|
||||
|
||||
return $this->respond(['status' => true, 'message' => 'Image saved successfully.']);
|
||||
} catch (\Exception $e) {
|
||||
return $this->respond(['status' => false, 'message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
// soft delete
|
||||
public function remove_advertise_image()
|
||||
{
|
||||
try {
|
||||
$rules = [
|
||||
'add_image_id' => [
|
||||
'rules' => 'required|integer|is_natural_no_zero',
|
||||
'errors' => [
|
||||
'required' => 'Image ID is required',
|
||||
'integer' => 'Image ID must be a valid number',
|
||||
'is_natural_no_zero' => 'Image ID must be a positive number'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
if (!$this->validate($rules)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => false,
|
||||
'message' => 'Input validation failed',
|
||||
'code' => 400,
|
||||
'errors' => $this->validator->getErrors()
|
||||
]);
|
||||
}
|
||||
|
||||
$id = (int)$this->request->getPost('add_image_id');
|
||||
|
||||
$existing = $this->addImgModel->find($id);
|
||||
if (!$existing) {
|
||||
return $this->respond(['status' => false, 'message' => 'Record not found'], 404);
|
||||
}
|
||||
|
||||
$this->addImgModel->update($id, ['is_active' => 0]);
|
||||
|
||||
return $this->respond(['status' => true, 'message' => 'Deleted successfully']);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return $this->respond(['status' => false, 'message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Preview image Went Edit.
|
||||
public function showAdvertiseImage($filename)
|
||||
{
|
||||
$filename = basename($filename);
|
||||
|
||||
if (!preg_match('/^[a-zA-Z0-9_\-]+\.(jpg|jpeg|png|gif|webp)$/i', $filename)) {
|
||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
|
||||
$path = ROOTPATH . 'public/uploads/add_image_upload/' . $filename;
|
||||
$realPath = realpath($path);
|
||||
$allowedDir = realpath(ROOTPATH . 'public/uploads/add_image_upload');
|
||||
|
||||
if (!$realPath || strpos($realPath, $allowedDir) !== 0 || !file_exists($realPath)) {
|
||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
|
||||
$mime = mime_content_type($realPath);
|
||||
$allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||||
if (!in_array($mime, $allowedMimes, true)) {
|
||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
|
||||
return $this->response->setHeader('Content-Type', $mime)->setBody(file_get_contents($realPath));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function frontend_content()
|
||||
{
|
||||
|
||||
$method = $this->request->getMethod();
|
||||
|
||||
if ($this->request->getMethod() === 'post') {
|
||||
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* STEP 1: INITIAL VALIDATION
|
||||
* --------------------------------------------------------------------------
|
||||
* These are the basic validation rules. For 'content' and 'notes', we only
|
||||
* check if they are provided and within the allowed length.
|
||||
* The more advanced security check for script tags happens next.
|
||||
*/
|
||||
$rules = [
|
||||
'fe_id' => [
|
||||
'rules' => 'permit_empty|integer|is_natural',
|
||||
'errors' => [
|
||||
'integer' => 'ID must be a valid number',
|
||||
'is_natural' => 'ID must be a non-negative number'
|
||||
]
|
||||
],
|
||||
'type' => [
|
||||
'rules' => 'required|max_length[255]|regex_match[/^[a-zA-Z0-9_ \-]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Type is required',
|
||||
'max_length' => 'Type cannot exceed 255 characters',
|
||||
'regex_match' => 'Type can contain only letters, numbers, spaces, _ and -'
|
||||
]
|
||||
],
|
||||
'content_section' => [
|
||||
'rules' => 'required|max_length[255]|regex_match[/^[a-zA-Z0-9_ \-]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Content Section is required',
|
||||
'max_length' => 'Content Section cannot exceed 255 characters',
|
||||
'regex_match' => 'Content Section can contain only letters, numbers, spaces, _ and -'
|
||||
]
|
||||
],
|
||||
'heading' => [
|
||||
'rules' => 'required|max_length[255]|regex_match[/^[a-zA-Z0-9 _\-.,;:!?()&\/]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Heading is required',
|
||||
'max_length' => 'Heading cannot exceed 255 characters',
|
||||
'regex_match' => 'Heading can contain only letters, numbers, spaces, and these characters: . , ; : ! ? ( ) & / -'
|
||||
]
|
||||
],
|
||||
'content' => [
|
||||
'rules' => 'required|max_length[5000]',
|
||||
'errors' => [
|
||||
'required' => 'Content is required',
|
||||
'max_length' => 'Content cannot exceed 5000 characters',
|
||||
]
|
||||
],
|
||||
'notes' => [
|
||||
'rules' => 'required|max_length[1500]',
|
||||
'errors' => [
|
||||
'required' => 'Notes are required',
|
||||
'max_length' => 'Notes cannot exceed 1500 characters',
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
if (!$this->validate($rules)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => false,
|
||||
'message' => 'Input validation failed',
|
||||
'code' => 400,
|
||||
'errors' => $this->validator->getErrors()
|
||||
]);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* REFACTORED SANITIZATION LOGIC (XSS Protection)
|
||||
**************************************************************************
|
||||
*
|
||||
* Per the user's request, we are avoiding the generic `sanitizeInputArrayAdvanced`
|
||||
* on the `content` and `notes` fields, as they require special HTML
|
||||
* handling.
|
||||
*
|
||||
* The new process is:
|
||||
* 1. Get the raw `content` and `notes` directly from the POST request.
|
||||
* 2. Perform the critical XSS validation on this raw content using `hasXssTags()`.
|
||||
* If it fails, the request is rejected immediately. This satisfies all
|
||||
* the failure test cases (Tests 4-9).
|
||||
* 3. Take all *other* POST data and sanitize it using the generic
|
||||
* `sanitizeInputArrayAdvanced` function.
|
||||
* 4. Sanitize the now-validated `content` and `notes` using our specific
|
||||
* `sanitizeHtml()` function, which allows safe HTML.
|
||||
* 5. Combine the sanitized data into a final array for database insertion.
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
// Step 1: Get raw `content` and `notes`.
|
||||
$rawContent = $this->request->getPost('content');
|
||||
$rawNotes = $this->request->getPost('notes');
|
||||
|
||||
// Step 2: Perform critical XSS validation on raw input.
|
||||
$xssErrors = [];
|
||||
if ($this->hasXssTags($rawContent)) {
|
||||
$xssErrors['content'] = 'Content contains restricted tags. Script, iframe and event handlers are not allowed';
|
||||
}
|
||||
if ($this->hasXssTags($rawNotes)) {
|
||||
$xssErrors['notes'] = 'Notes contains restricted tags. Script, iframe and event handlers are not allowed';
|
||||
}
|
||||
|
||||
if (!empty($xssErrors)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => false,
|
||||
'message' => 'Input validation failed',
|
||||
'code' => 400,
|
||||
'errors' => $xssErrors
|
||||
]);
|
||||
}
|
||||
|
||||
// Step 3: Sanitize all *other* POST data.
|
||||
$otherPostData = $this->request->getPost();
|
||||
unset($otherPostData['content'], $otherPostData['notes']);
|
||||
$data = sanitizeInputArrayAdvanced($otherPostData);
|
||||
|
||||
// Step 4 & 5: Sanitize and re-combine `content` and `notes`.
|
||||
$data['content'] = $this->sanitizeHtml($rawContent);
|
||||
$data['notes'] = $this->sanitizeHtml($rawNotes);
|
||||
|
||||
$id = $data['fe_id'] ?? null;
|
||||
|
||||
if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => false, 'message' => 'Invalid ID format', 'code' => 400
|
||||
]);
|
||||
}
|
||||
$id = !empty($id) ? (int)$id : null;
|
||||
|
||||
unset($data['fe_id']);
|
||||
|
||||
foreach ($data as $k => $v) {
|
||||
if ($v === '' || $v === null) {unset($data[$k]);}
|
||||
}
|
||||
|
||||
// INSERT / UPDATE
|
||||
if (empty($id)) {
|
||||
$status = $this->feContentModel->insert($data);
|
||||
$text = "Created";
|
||||
} else {
|
||||
$existing = $this->feContentModel->find($id);
|
||||
if (!$existing) {
|
||||
return $this->response->setStatusCode(404)->setJSON([
|
||||
'status' => false, 'message' => 'Record not found', 'code' => 404
|
||||
]);
|
||||
}
|
||||
$status = $this->feContentModel->update($id, $data);
|
||||
$text = "Updated";
|
||||
}
|
||||
|
||||
return $this->respond([
|
||||
'status' => $status ? true : false,
|
||||
'message' => "Frontend Content $text " . ($status ? 'successfully' : 'failed'),
|
||||
'code' => $status ? 200 : 400,
|
||||
'data' => $data,
|
||||
] , $status ? 200 : 400 );
|
||||
}
|
||||
elseif ($method === 'get') {
|
||||
|
||||
$id = $this->request->getGet('fe_id') ?? null;
|
||||
|
||||
if (!empty($id)) {
|
||||
|
||||
if (!ctype_digit((string)$id) || (int)$id <= 0) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => false, 'code' => 400, 'message' => 'Invalid ID format'
|
||||
]);
|
||||
}
|
||||
$id = (int)$id;
|
||||
|
||||
$data['fe_list'] = $this->feContentModel->where('id', $id)->orderBy('id', 'DESC')->findAll();
|
||||
|
||||
if (!empty($data)) {
|
||||
return $this->respond(['status' => true, 'code' => 200, 'data' => $data], 200);
|
||||
} else {
|
||||
return $this->respond(['status' => false, 'code' => 400, 'message' => 'No data found'], 200);
|
||||
}
|
||||
}
|
||||
|
||||
$data['fe_list'] = $this->feContentModel->orderBy('id', 'DESC')->findAll();
|
||||
|
||||
return $this->loadLayout('frontend_content_list', ['data' => $data,'tab_name' => 'Front-End Content','page_name' => 'Front-End Content']);
|
||||
|
||||
} elseif ($method === 'delete') {
|
||||
|
||||
$id = $this->request->getGet('fe_id') ?? null;
|
||||
|
||||
if (empty($id) || !ctype_digit((string)$id) || (int)$id <= 0) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 400,
|
||||
'message' => 'Valid numeric ID is required for deletion'
|
||||
], 400);
|
||||
}
|
||||
$id = (int)$id;
|
||||
|
||||
$existing = $this->feContentModel->find($id);
|
||||
if (!$existing) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 404,
|
||||
'message' => 'Record not found'
|
||||
], 404);
|
||||
}
|
||||
|
||||
$update_status = $this->feContentModel->where('id', $id)->set(['is_active' => 0])->update();
|
||||
|
||||
if ($update_status) {
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'code' => 200,
|
||||
'message' => 'Data removed successfully',
|
||||
'fe_id' => $id
|
||||
], 200);
|
||||
} else {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'code' => 400,
|
||||
'message' => 'Failed to remove data',
|
||||
'fe_id' => $id
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private const ALLOWED_RETURN_TYPES = ['api', 'web'];
|
||||
|
||||
public function FAQ()
|
||||
{
|
||||
$method = strtolower($this->request->getMethod());
|
||||
$returnType = strtolower($this->request->getGet('return_type') ?? 'api');
|
||||
if (!in_array($returnType, self::ALLOWED_RETURN_TYPES, true)) {
|
||||
$returnType = 'api';
|
||||
}
|
||||
$ref = ['timestamp' => date('Y-m-d H:i:s')];
|
||||
|
||||
try {
|
||||
// --- 1. POST: CREATE OR UPDATE ---
|
||||
if ($method === 'post') {
|
||||
$rules = [
|
||||
'category' => [
|
||||
// Allow letters, numbers, spaces and / - . , " '
|
||||
'rules' => 'required|max_length[100]|regex_match[/^[a-zA-Z0-9 \\/\\-\\.\,\"\\\']+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Category is required',
|
||||
'max_length' => 'Category cannot exceed 100 characters',
|
||||
'regex_match' => 'Category can contain only letters, numbers, spaces, and these characters: / - . , " \'',
|
||||
|
||||
]
|
||||
],
|
||||
'question' => [
|
||||
// Allow only letters, numbers, spaces and basic punctuation . , ; : ! ? ( ) & / -
|
||||
'rules' => 'required|max_length[1000]|regex_match[/^[a-zA-Z0-9 _\\-.,;:!?()&\\/]+$/]',
|
||||
'errors' => [
|
||||
'required' => 'Question is required',
|
||||
'max_length' => 'Question cannot exceed 1000 characters',
|
||||
'regex_match' => 'Question can contain only letters, numbers, spaces, and these characters: . , ; : ! ? ( ) & / -',
|
||||
]
|
||||
],
|
||||
'answer' => [
|
||||
'rules' => 'required|max_length[5000]',
|
||||
'errors' => [
|
||||
'required' => 'Answer is required',
|
||||
'max_length' => 'Answer cannot exceed 5000 characters',
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
if (!$this->validate($rules)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => 'error',
|
||||
'message' => 'Input validation failed',
|
||||
'code' => 400,
|
||||
'errors' => $this->validator->getErrors(),
|
||||
'ref' => $ref
|
||||
]);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* XSS PROTECTION FOR 'question' and 'answer'
|
||||
**************************************************************************
|
||||
*
|
||||
* Applying the same security model as `frontend_content`.
|
||||
*
|
||||
* 1. Validate raw `question` and `answer` for malicious tags using `hasXssTags()`.
|
||||
* If found, reject the request immediately.
|
||||
* 2. Sanitize all *other* fields using the generic `sanitizeInputArrayAdvanced`.
|
||||
* 3. Sanitize the `question` and `answer` using the HTML-aware `sanitizeHtml()`
|
||||
* function to allow safe tags before saving.
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
// Step 1: Validate raw input for XSS threats.
|
||||
$rawQuestion = $this->request->getPost('question');
|
||||
$rawAnswer = $this->request->getPost('answer');
|
||||
$xssErrors = [];
|
||||
|
||||
if ($this->hasXssTags($rawQuestion)) {
|
||||
$xssErrors['question'] = 'Question contains restricted tags. Script, iframe and event handlers are not allowed';
|
||||
}
|
||||
if ($this->hasXssTags($rawAnswer)) {
|
||||
$xssErrors['answer'] = 'Answer contains restricted tags. Script, iframe and event handlers are not allowed';
|
||||
}
|
||||
|
||||
if (!empty($xssErrors)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => 'error',
|
||||
'message' => 'Input validation failed',
|
||||
'code' => 400,
|
||||
'errors' => $xssErrors,
|
||||
'ref' => $ref
|
||||
]);
|
||||
}
|
||||
|
||||
// Step 2 & 3: Sanitize and combine data.
|
||||
$otherPostData = $this->request->getPost();
|
||||
unset($otherPostData['question'], $otherPostData['answer']);
|
||||
$data = sanitizeInputArrayAdvanced($otherPostData);
|
||||
|
||||
$data['question'] = $this->sanitizeHtml($rawQuestion);
|
||||
$data['answer'] = $this->sanitizeHtml($rawAnswer);
|
||||
|
||||
$id = $data['faq_id'] ?? null;
|
||||
|
||||
if (!empty($id) && (!ctype_digit((string)$id) || (int)$id <= 0)) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => 'error', 'message' => 'Invalid FAQ ID format', 'code' => 400, 'ref' => $ref
|
||||
]);
|
||||
}
|
||||
$id = !empty($id) ? (int)$id : null;
|
||||
unset($data['faq_id']);
|
||||
|
||||
if (empty($id)) {
|
||||
$status = $this->faqModel->insert($data);
|
||||
$msg = "Created";
|
||||
} else {
|
||||
$existing = $this->faqModel->find($id);
|
||||
if (!$existing) {
|
||||
return $this->response->setStatusCode(404)->setJSON([
|
||||
'status' => 'error', 'message' => 'FAQ not found', 'code' => 404, 'ref' => $ref
|
||||
]);
|
||||
}
|
||||
$status = $this->faqModel->update($id, $data);
|
||||
$msg = "Updated";
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => $status ? 'success' : 'error',
|
||||
'message' => "FAQ $msg " . ($status ? 'successfully' : 'failed'),
|
||||
'code' => $status ? 200 : 400,
|
||||
'data' => $data,
|
||||
'ref' => $ref
|
||||
])->setStatusCode($status ? 200 : 400);
|
||||
}
|
||||
|
||||
// --- 2. GET: FETCH LIST OR SINGLE ---
|
||||
elseif ($method === 'get') {
|
||||
|
||||
$id = $this->request->getGet('faq_id');
|
||||
|
||||
if (!empty($id)) {
|
||||
if (!ctype_digit((string)$id) || (int)$id <= 0) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'status' => 'error', 'message' => 'Invalid FAQ ID format', 'code' => 400, 'ref' => $ref
|
||||
]);
|
||||
}
|
||||
$id = (int)$id;
|
||||
|
||||
if($returnType === 'web'){
|
||||
$row = $this->faqModel->find($id);
|
||||
}else{
|
||||
$row = $this->faqModel->where('is_active', 1)->find($id);
|
||||
}
|
||||
|
||||
$data['faq_list'] = $row ? [$row] : [];
|
||||
} else {
|
||||
if($returnType === 'web'){
|
||||
$data['faq_list'] = $this->faqModel->orderBy('id', 'desc')->findAll();
|
||||
}else{
|
||||
$data['faq_list'] = $this->faqModel->where('is_active', 1)->orderBy('id', 'desc')->findAll();
|
||||
}
|
||||
}
|
||||
|
||||
if ($returnType === 'web') {
|
||||
$data['tab_name'] = "FAQ";
|
||||
$data['page_name'] = "FAQ";
|
||||
// print_r($data);die;
|
||||
return $this->loadLayout('faq_list', $data);
|
||||
}
|
||||
|
||||
// API Response Logic
|
||||
if (empty($data['faq_list'])) {
|
||||
return $this->response->setJSON([
|
||||
'status' => $returnType === 'web' ? false : 'error',
|
||||
'message' => 'No data found',
|
||||
'code' => 404,
|
||||
'data' => [],
|
||||
'ref' => $ref
|
||||
])->setStatusCode(200); // Using 200 with error status is common for mobile apps to prevent crashes
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => $returnType === 'web' ? true : 'success',
|
||||
'message' => 'Data retrieved',
|
||||
'code' => 200,
|
||||
'data' => $data,
|
||||
'ref' => $ref
|
||||
])->setStatusCode(200);
|
||||
}
|
||||
|
||||
// --- 3. DELETE: SOFT DELETE ---
|
||||
elseif ($method === 'delete') {
|
||||
$id = $this->request->getGet('faq_id');
|
||||
|
||||
if (empty($id) || !ctype_digit((string)$id) || (int)$id <= 0) {
|
||||
return $this->response->setJSON([
|
||||
'status' => 'error', 'message' => 'Valid numeric FAQ ID is required', 'code' => 400, 'ref' => $ref
|
||||
])->setStatusCode(400);
|
||||
}
|
||||
$id = (int)$id;
|
||||
|
||||
$status = $this->faqModel->find($id) ? $this->faqModel->update($id, ['is_active' => 0]) : false;
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => $status ? ($returnType === 'web' ? true : 'success') : ($returnType === 'web' ? false : 'error'),
|
||||
'message' => $status ? 'Data removed successfully' : 'Failed to remove or ID missing',
|
||||
'code' => $status ? 200 : 400,
|
||||
'data' => [],
|
||||
'ref' => $ref
|
||||
])->setStatusCode( 200 );
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'FAQ error: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
|
||||
return $this->response->setJSON([
|
||||
'status' => $returnType === 'web' ? false : 'error',
|
||||
'code' => 500,
|
||||
'message' => 'An internal error occurred. Please try again later.',
|
||||
'ref' => $ref
|
||||
])->setStatusCode(500);
|
||||
}
|
||||
}
|
||||
|
||||
// public function frontend_content_files()
|
||||
// {
|
||||
|
||||
// // 1. Define your physical system path
|
||||
// define('UPLOAD_PATH', ROOTPATH . 'public/uploads/add_image_upload/');
|
||||
|
||||
// // 2. Define the public URL so the editor can display the image later
|
||||
// // This is what the browser uses to see the image (e.g., https://site.com/uploads/...)
|
||||
// // $publicUrl = "https://yourdomain.com/public/uploads/add_image_upload/";
|
||||
// $publicUrl = ROOTPATH . 'public/uploads/add_image_upload/';
|
||||
// // if (!is_dir($uploadPath)) mkdir($uploadPath, 0755, true);
|
||||
|
||||
// header('Content-Type: application/json');
|
||||
|
||||
// if ($_FILES['files']) {
|
||||
// // Ensure directory exists
|
||||
// if (!is_dir(UPLOAD_PATH)) {
|
||||
// mkdir(UPLOAD_PATH, 0775, true);
|
||||
// }
|
||||
|
||||
// $fileName = time() . '_' . basename($_FILES['files']['name'][0]);
|
||||
// $fullPath = UPLOAD_PATH . $fileName;
|
||||
|
||||
// if (move_uploaded_file($_FILES['files']['tmp_name'][0], $fullPath)) {
|
||||
// echo json_encode([
|
||||
// "success" => true,
|
||||
// "data" => [
|
||||
// "baseurl" => $publicUrl,
|
||||
// "files" => [$fileName]
|
||||
// ]
|
||||
// ]);
|
||||
// } else {
|
||||
// echo json_encode(["success" => false, "error" => "Failed to move file to " . UPLOAD_PATH]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
|
||||
// Add these two private methods inside AppContentManagementController
|
||||
|
||||
|
||||
/**
|
||||
* =================================================================================
|
||||
* HTML SANITIZATION & VALIDATION HELPER METHODS
|
||||
* =================================================================================
|
||||
* The following two methods are the core of the XSS protection logic.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* sanitizeHtml()
|
||||
*
|
||||
* This function cleans a string of HTML, ensuring it is safe to display in a browser.
|
||||
* It allows a specific set of safe HTML tags and removes any dangerous attributes
|
||||
* from those tags.
|
||||
*
|
||||
* @param string $input The raw HTML string from user input.
|
||||
* @return string The cleaned, safe HTML string.
|
||||
*/
|
||||
private function sanitizeHtml(string $input): string
|
||||
{
|
||||
/**
|
||||
* Define a whitelist of allowed HTML tags. Any tag not in this list will be
|
||||
* completely removed. We are allowing basic formatting, lists, tables, etc.
|
||||
*/
|
||||
// ✅ Added <s>, <u>, <h1>-<h6>, <blockquote>, <pre>, <code>, <hr> for Jodit support
|
||||
$allowed_tags = '<p><b><i><s><u><strong><em><ul><ol><li><br><a><img><table><thead><tbody><tr><th><td><span><div><h1><h2><h3><h4><h5><h6><blockquote><pre><code><hr><sub><sup>';
|
||||
|
||||
// Use strip_tags() to remove all tags that are not in our whitelist.
|
||||
$clean = strip_tags($input, $allowed_tags);
|
||||
|
||||
/**
|
||||
* Define a blacklist of dangerous attributes. These are often used for XSS
|
||||
* attacks (e.g., `onclick`, `onmouseover`). We search for and remove these
|
||||
* attributes from any remaining tags.
|
||||
*/
|
||||
$dangerous_attrs = [
|
||||
'/\s*on\w+\s*=\s*["\'][^"\']*["\']/i', // e.g., onclick="..."
|
||||
'/\s*on\w+\s*=\s*[^\s>]*/i', // e.g., onclick=...
|
||||
'/\s*javascript\s*:[^"\'"]*/i', // e.g., href="javascript:..."
|
||||
'/\s*vbscript\s*:[^"\'"]*/i', // e.g., href="vbscript:..."
|
||||
];
|
||||
|
||||
// Use preg_replace to find and remove the dangerous attributes.
|
||||
foreach ($dangerous_attrs as $pattern) {
|
||||
$clean = preg_replace($pattern, '', $clean);
|
||||
}
|
||||
|
||||
return $clean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hasXssTags()
|
||||
*
|
||||
* This function scans a string for common XSS-related tags, protocols, and event
|
||||
* handlers. It is used as a primary check to quickly reject any input that is
|
||||
* clearly malicious.
|
||||
*
|
||||
* @param string $str The raw string from user input.
|
||||
* @return bool Returns `true` if a dangerous pattern is found, `false` otherwise.
|
||||
*/
|
||||
private function hasXssTags(string $str): bool
|
||||
{
|
||||
// Decode the string to handle entities (e.g., `%3Cscript%3E`) and prevent evasion.
|
||||
$decoded = html_entity_decode($str, ENT_QUOTES, 'UTF-8');
|
||||
$decoded = urldecode($decoded);
|
||||
$decoded = str_replace(["\0", "\x00"], '', $decoded); // Remove null bytes
|
||||
|
||||
/**
|
||||
* Define a blacklist of dangerous patterns. This includes tags like `<script>`
|
||||
* and `<iframe>`, as well as patterns like `javascript:` and `onclick=`.
|
||||
* The `/i` flag makes the search case-insensitive.
|
||||
*/
|
||||
$dangerous_patterns = [
|
||||
'/<\s*script/i', // <script
|
||||
'/<\s*\/\s*script/i', // </script
|
||||
'/javascript\s*:/i', // javascript:
|
||||
'/vbscript\s*:/i', // vbscript:
|
||||
'/<\s*iframe/i', // <iframe>
|
||||
'/<\s*object/i', // <object>
|
||||
'/<\s*embed/i', // <embed>
|
||||
'/<\s*applet/i', // <applet>
|
||||
'/on\w+\s*=/i', // on...= (e.g., onclick=, onmouseover=, onerror=)
|
||||
'/data\s*:\s*text\/html/i', // data:text/html
|
||||
'/expression\s*\(/i', // CSS expression()
|
||||
'/\balert\s*\(/i', // alert(...)
|
||||
];
|
||||
|
||||
// Loop through the patterns and check if any of them exist in the decoded string.
|
||||
foreach ($dangerous_patterns as $pattern) {
|
||||
if (preg_match($pattern, $decoded)) return true; // Found a threat
|
||||
}
|
||||
|
||||
// If we get here, no threats were found.
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
1192
app/Controllers/BDSReportController.php
Normal file
1192
app/Controllers/BDSReportController.php
Normal file
File diff suppressed because it is too large
Load Diff
68
app/Controllers/BaseController.php
Executable file
68
app/Controllers/BaseController.php
Executable file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use CodeIgniter\Controller;
|
||||
use CodeIgniter\HTTP\CLIRequest;
|
||||
use CodeIgniter\HTTP\IncomingRequest;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Class BaseController
|
||||
*
|
||||
* BaseController provides a convenient place for loading components
|
||||
* and performing functions that are needed by all your controllers.
|
||||
* Extend this class in any new controllers:
|
||||
* class Home extends BaseController
|
||||
*
|
||||
* For security be sure to declare any new methods as protected or private.
|
||||
*/
|
||||
abstract class BaseController extends Controller
|
||||
{
|
||||
/**
|
||||
* Instance of the main Request object.
|
||||
*
|
||||
* @var CLIRequest|IncomingRequest
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* An array of helpers to be loaded automatically upon
|
||||
* class instantiation. These helpers will be available
|
||||
* to all other controllers that extend BaseController.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $helpers = [];
|
||||
|
||||
/**
|
||||
* Be sure to declare properties for any property fetch you initialized.
|
||||
* The creation of dynamic property is deprecated in PHP 8.2.
|
||||
*/
|
||||
// protected $session;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected $session;
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
|
||||
{
|
||||
// Do Not Edit This Line
|
||||
// echo 'initController';
|
||||
parent::initController($request, $response, $logger);
|
||||
|
||||
// Preload any models, libraries, etc, here.
|
||||
|
||||
$this->session = \Config\Services::session();
|
||||
}
|
||||
|
||||
|
||||
public function loadLayout($view_name,$data = [])
|
||||
{
|
||||
echo view('layout/header',$data);
|
||||
echo view($view_name, $data);
|
||||
echo view('layout/footer',$data);
|
||||
}
|
||||
}
|
||||
452
app/Controllers/ChatBotController.php
Executable file
452
app/Controllers/ChatBotController.php
Executable file
@ -0,0 +1,452 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Helpers\MailHelper;
|
||||
use App\Helpers\sendMailNotification;
|
||||
|
||||
use App\Models\EmployeeModel;
|
||||
use App\Models\EmployeePolicyModel;
|
||||
use App\Models\ClientModel;
|
||||
use App\Models\ClientRMModel;
|
||||
use App\Models\PolicesModel;
|
||||
use App\Models\RelationshipModel;
|
||||
use App\Models\FileModel;
|
||||
use App\Models\ClientPolicyModel;
|
||||
use App\Models\PolicyPremium1Model;
|
||||
use App\Models\PolicyPremium2Model;
|
||||
use App\Models\PolicyTypeModel;
|
||||
use App\Models\NotificationModel;
|
||||
use App\Models\UserModel;
|
||||
use App\Models\FEContentModel;
|
||||
use App\Models\AddImgModel;
|
||||
use App\Models\ChatBotModel;
|
||||
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
|
||||
class ChatBotController extends AdminController
|
||||
{
|
||||
|
||||
use ResponseTrait;
|
||||
|
||||
protected $myLogger;
|
||||
protected $employeeModel;
|
||||
protected $employeePolicyModel;
|
||||
protected $clientModel;
|
||||
protected $clientRMModel;
|
||||
protected $fileModel;
|
||||
protected $policesModel;
|
||||
protected $relationshipModel;
|
||||
protected $clientPolicyModel;
|
||||
protected $policyPremium1Model;
|
||||
protected $policyPremium2Model;
|
||||
protected $policyTypeModel;
|
||||
protected $notificationModel;
|
||||
protected $userModel;
|
||||
protected $feContentModel;
|
||||
protected $addImgModel;
|
||||
protected $ChatBotModel;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
set_session_context('Employee');
|
||||
$this->myLogger = \Config\Services::mylogger();
|
||||
$this->employeeModel = new EmployeeModel();
|
||||
$this->employeePolicyModel = new EmployeePolicyModel();
|
||||
$this->clientModel = new ClientModel();
|
||||
$this->clientRMModel = new ClientRMModel();
|
||||
$this->policesModel = new PolicesModel();
|
||||
$this->relationshipModel = new RelationshipModel();
|
||||
$this->fileModel= new FileModel();
|
||||
$this->clientPolicyModel = new ClientPolicyModel();
|
||||
$this->policyPremium1Model = new PolicyPremium1Model();
|
||||
$this->policyPremium2Model = new PolicyPremium2Model();
|
||||
$this->policyTypeModel = new PolicyTypeModel();
|
||||
$this->notificationModel = new NotificationModel();
|
||||
$this->userModel = new UserModel();
|
||||
$this->feContentModel = new FEContentModel();
|
||||
$this->addImgModel = new AddImgModel();
|
||||
$this->ChatBotModel = new ChatBotModel();
|
||||
}
|
||||
|
||||
|
||||
public function getChatResponse()
|
||||
{
|
||||
try {
|
||||
$emp_code = $this->request->getVar('emp_code');
|
||||
$client_id = $this->request->getVar('client_id');
|
||||
$client_branch_id = $this->request->getVar('client_branch_id');
|
||||
$request_for = $this->request->getVar('request_for');
|
||||
$option = $this->request->getVar('option');
|
||||
$is_option = $this->request->getVar('is_option');
|
||||
|
||||
$responseData = $this->ChatBotModel->where('request', $request_for)
|
||||
->where('is_active', 1 )
|
||||
->first();
|
||||
|
||||
|
||||
if ($responseData) {
|
||||
|
||||
|
||||
if(isset($option) && $option != 0)
|
||||
{
|
||||
|
||||
$decodedData = json_decode($responseData['options'],true);
|
||||
|
||||
if (isset($decodedData[$option]) && $decodedData[$option] !== null) {
|
||||
|
||||
|
||||
$requestFor = $decodedData[$option];
|
||||
$responseData = $this->ChatBotModel->where('request', $requestFor)
|
||||
->where('is_active', 1 )
|
||||
->first();
|
||||
|
||||
if($responseData['request'] == 'Call' || $responseData['request'] == 'Mail'){
|
||||
|
||||
$AccountManagerDetails = $this->clientRMModel->select('client_rm.* , user_profiles.*')
|
||||
->join('user_profiles', 'client_rm.user_id = user_profiles.id', 'left')
|
||||
->where('client_rm.client_id', $client_id )
|
||||
->where('client_rm.level', 3 )
|
||||
->findAll();
|
||||
|
||||
if(isset($AccountManagerDetails) && $responseData['request'] == 'Call'){
|
||||
$responseData['options'] = $AccountManagerDetails[0]['mobile'];
|
||||
}else if(isset($AccountManagerDetails) && $responseData['request'] == 'Mail'){
|
||||
$responseData['options'] = $AccountManagerDetails[0]['email'];
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
if( $request_for == 'Initiate claim'){
|
||||
$result = [
|
||||
'request_for' => $request_for,
|
||||
'options' => json_decode($responseData['options'], true),
|
||||
'text' => "Enter Message",
|
||||
'is_option' => "0",
|
||||
'policy_id' => $option,
|
||||
'mobile_no' => $this->request->getVar('mobile_no'),
|
||||
'navigation' => $responseData['navigation'],
|
||||
'navigation_type' => $responseData['navigation_type']
|
||||
];
|
||||
|
||||
return $this->respond(['status' => 'success','code' => 200,'data' => $result ],200);
|
||||
}else{
|
||||
|
||||
$result = $this->sendDefaultMessage();
|
||||
return $this->respond(['status' => 'success','code' => 200,'data' => $result ],200);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(isset($is_option) && $is_option == 0){
|
||||
|
||||
$text = $this->request->getVar('text');
|
||||
$value = $this->request->getVar('value');
|
||||
|
||||
$Data = $this->ChatBotModel->where('options', $text)->where('is_active', 1 )->first();
|
||||
|
||||
if($Data){
|
||||
|
||||
if($Data['options'] == 'Please Enter Your Mobile Number To View Your Policies')
|
||||
{
|
||||
|
||||
$result = $this->getAllPolicyByMobileNumber($value,'text');
|
||||
return $this->respond(['status' => 'success','code' => 200,'data' => $result ],200);
|
||||
|
||||
}else if($Data['options'] == 'Please Enter Your Mobile Number To Raise Claim'){
|
||||
|
||||
$result = $this->getAllPolicyByMobileNumber($value,'options');
|
||||
return $this->respond(['status' => 'success','code' => 200,'data' => $result ],200);
|
||||
|
||||
}else if( $Data['options'] == 'Enter Your Claim Number'){
|
||||
|
||||
$result = $this->getTicket($value,$emp_code,$client_id);
|
||||
return $this->respond(['status' => 'success','code' => 200,'data' => $result ],200);
|
||||
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
if( $request_for == 'Create claim' && $text == 'Enter Message'){
|
||||
|
||||
|
||||
$ticket = $this->claimTicket();
|
||||
|
||||
$ticket = json_decode($ticket,true);
|
||||
|
||||
if (isset($ticket['success']) && $ticket['success'] == 1) {
|
||||
|
||||
$result = [
|
||||
'request_for' => $request_for,
|
||||
'options' => null,
|
||||
'text' => $ticket['message'].', Your Claim id is '.$ticket['ticket_id'],
|
||||
'is_option' => "0",
|
||||
'navigation' => "0",
|
||||
'navigation_type' => null
|
||||
];
|
||||
}else{
|
||||
|
||||
$result = [
|
||||
'request_for' => $request_for,
|
||||
'options' => null,
|
||||
'text' => $ticket['message'],
|
||||
'is_option' => "0",
|
||||
'navigation' => "0",
|
||||
'navigation_type' => null
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
return $this->respond(['status' => 'success','code' => 200,'data' => $result ],200);
|
||||
|
||||
}else{
|
||||
|
||||
$result = $this->sendDefaultMessage();
|
||||
return $this->respond(['status' => 'success','code' => 200,'data' => $result ],200);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$result = [
|
||||
'request_for' => $responseData['request'],
|
||||
'options' => json_decode($responseData['options'], true),
|
||||
'text' => $responseData['is_option'] != 1 ? $responseData['options'] : null,
|
||||
'is_option' => $responseData['is_option'],
|
||||
'navigation' => $responseData['navigation'],
|
||||
'navigation_type' => $responseData['navigation_type']
|
||||
];
|
||||
|
||||
return $this->respond(['status' => 'success','code' => 200,'data' => $result ],200);
|
||||
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
return $this->respond(['status' => 'failed','code' => 404,'data' => 'No Data'],404);
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
return $this->respond(['status' => 'failed','code' => 500,'data' => $th],500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function sendDefaultMessage()
|
||||
{
|
||||
$responseData = $this->ChatBotModel->where('id', 1)->where('is_active', 1 )->first();
|
||||
return [
|
||||
'request_for' => $responseData['request'],
|
||||
'options' => json_decode($responseData['options'], true),
|
||||
'text' => $responseData['is_option'] != 1 ? $responseData['options'] : null,
|
||||
'is_option' => $responseData['is_option'],
|
||||
'navigation' => $responseData['navigation'],
|
||||
'navigation_type' => $responseData['navigation_type']
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function getAllPolicyByMobileNumber($mobileNo,$action)
|
||||
{
|
||||
$employee = $this->employeeModel->where('mobile', $mobileNo)->where('is_active', 1 )->first();
|
||||
if($employee){
|
||||
|
||||
$employeeData = $this->employeeModel->where('emp_code', $employee['emp_code'])
|
||||
->where('client_id',$employee['client_id'])
|
||||
->where('client_branch_id',$employee['client_branch_id'])
|
||||
->where('is_active', 1 )->findAll();
|
||||
$whereArrayForId = [];
|
||||
foreach ( $employeeData as $key => $value) { array_push($whereArrayForId, $value['id']); }
|
||||
|
||||
$ClientPolicyData = $this->clientPolicyModel->select('client_policy.* , policy_type.long_name as policy_name , policy_type.policy_type as policy_type')
|
||||
->join('policy_type', 'client_policy.policy_type_id = policy_type.id', 'left')
|
||||
->where('client_policy.client_id', $employee['client_id'] )
|
||||
->where('client_policy.client_branch_id', $employee['client_branch_id'] )
|
||||
->where('client_policy.is_active', 1 )
|
||||
->where('client_policy.policy_status', 1)
|
||||
->orderby('client_policy.id' , 'ASC')
|
||||
->findAll();
|
||||
$options = [];
|
||||
$text = '';
|
||||
if(count($ClientPolicyData) > 0 && count($employeeData) > 0)
|
||||
{
|
||||
|
||||
foreach ($ClientPolicyData as $key => $ClientPolicyValue) {
|
||||
|
||||
$data['policy_name'] = $ClientPolicyValue['policy_name'];
|
||||
$data['policy_type'] = $ClientPolicyValue['policy_type'];
|
||||
$data['policy_no'] = $ClientPolicyValue['policy_no'];
|
||||
|
||||
|
||||
|
||||
$employee_policy = $this->employeePolicyModel->select('employees.*,employee_polices.employee_id , employee_polices.basic_cover_si , employee_polices.premium , employee_polices.gst , employee_polices.tpa_id , employee_polices.rand_string , employee_polices.uhid as uhid')
|
||||
->join('employees', 'employee_polices.employee_id = employees.id', 'left')
|
||||
->whereIn('employee_polices.employee_id',$whereArrayForId)
|
||||
->where('employee_polices.client_policy_id',$ClientPolicyValue['id'])
|
||||
->where('employee_polices.is_active', 1 )->findAll();
|
||||
if(count($employee_policy) > 0)
|
||||
{
|
||||
$policy[$ClientPolicyValue['id']] = $ClientPolicyValue['policy_name']."(".$ClientPolicyValue['policy_type'].")";
|
||||
array_push($options, $policy);
|
||||
$text .= $ClientPolicyValue['policy_name']." % ";
|
||||
}else{
|
||||
$text .= "There is no any active policies ";
|
||||
}
|
||||
|
||||
}
|
||||
if($action == 'options'){
|
||||
return [
|
||||
'request_for' => $this->request->getVar('request_for'),
|
||||
'options' => $options[count($options) - 1],
|
||||
'text' => null,
|
||||
'is_option' => "1",
|
||||
'mobile_no' => $mobileNo,
|
||||
];
|
||||
}else{
|
||||
return [
|
||||
'request_for' => $this->request->getVar('request_for'),
|
||||
'options' => null,
|
||||
'text' => $text,
|
||||
'is_option' => "0",
|
||||
'mobile_no' => $mobileNo,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
}else{
|
||||
return [
|
||||
'request_for' => $this->request->getVar('request_for'),
|
||||
'options' => null,
|
||||
'text' => "Dear ".$employee['name']." Policy not yet bound for you.",
|
||||
'is_option' => "0"
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}else{
|
||||
return [
|
||||
'request_for' => $this->request->getVar('request_for'),
|
||||
'options' => null,
|
||||
'text' => "Invalid Mobile Number",
|
||||
'is_option' => "0"
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function claimTicket()
|
||||
{
|
||||
|
||||
|
||||
$employee = $this->employeeModel->where('mobile', $this->request->getVar('mobile_no'))->where('is_active', 1 )->first();
|
||||
if($employee){
|
||||
|
||||
$url = 'https://venbait.in/nhance/helpdesk/dev/api/tickets/create';
|
||||
|
||||
|
||||
$token = 'uncp8FvG310bEyYdV9MmStlo7KDRZ65fLWTeXCI2JzwPrNHjBqQhUiAgxsaO';
|
||||
$headers = [
|
||||
'Token: ' . $token,
|
||||
];
|
||||
|
||||
// Form data
|
||||
$data = [
|
||||
'opener' => 'user',
|
||||
'department_id' => 2,
|
||||
'subject' => 'Claim Raised by Chat Bot',
|
||||
'body' => $this->request->getVar('value'),
|
||||
'policy_id' => $this->request->getVar('policy_id'),
|
||||
'emp_code' => $employee['emp_code'],
|
||||
'mobile_number' => $this->request->getVar('mobile_no'),
|
||||
'email' => 'adhavanvalli@gmail.com',
|
||||
'fullname' => $employee['name'],
|
||||
];
|
||||
|
||||
// Initialize cURL session
|
||||
$ch = curl_init();
|
||||
|
||||
// Set cURL options
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
return $response;
|
||||
// Check for errors
|
||||
// if (curl_errno($ch)) {
|
||||
// $error = curl_error($ch);
|
||||
// curl_close($ch);
|
||||
// return ['status' => 'failed', 'response' => $error];
|
||||
// } else {
|
||||
// // Close cURL session
|
||||
// curl_close($ch);
|
||||
// return ['status' => 'success', 'response' => $response];
|
||||
// }
|
||||
|
||||
|
||||
}else{
|
||||
return ['status' => 'failed', 'response' => 'Employee Not Found'];
|
||||
}
|
||||
}
|
||||
|
||||
public function getTicket($value, $emp_code, $client_id)
|
||||
{
|
||||
// Construct the URL with query parameters
|
||||
$url = 'https://venbait.in/nhance/helpdesk/dev/api/tickets/show/' . $value;
|
||||
|
||||
$token = 'uncp8FvG310bEyYdV9MmStlo7KDRZ65fLWTeXCI2JzwPrNHjBqQhUiAgxsaO';
|
||||
$headers = [
|
||||
'Token: ' . $token,
|
||||
];
|
||||
|
||||
// Initialize cURL session
|
||||
$ch = curl_init();
|
||||
|
||||
// Set cURL options
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
// Execute cURL request
|
||||
$response = curl_exec($ch);
|
||||
|
||||
// Check for errors
|
||||
if (curl_errno($ch)) {
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
return ['status' => 'failed', 'response' => $error];
|
||||
} else {
|
||||
// Close cURL session
|
||||
curl_close($ch);
|
||||
return ['status' => 'success', 'response' => $response];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
129
app/Controllers/Chatbot/EcardDownloadConversation.php
Normal file
129
app/Controllers/Chatbot/EcardDownloadConversation.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
// namespace App\Conversations;
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
use App\Helpers\ChatbotHelper;
|
||||
|
||||
class EcardDownloadConversation extends Conversation
|
||||
{
|
||||
protected $buttonsData = [];
|
||||
public function run()
|
||||
{
|
||||
$this->bot->types();
|
||||
// sleep(0.5);
|
||||
$this->showEcardMenu();
|
||||
}
|
||||
|
||||
protected function showEcardMenu()
|
||||
{
|
||||
// Log entry for debugging
|
||||
log_message('error', 'showEcardMenu function called');
|
||||
|
||||
|
||||
// Get chat session and policies
|
||||
$chat_session_info = get_chatbot_session_info();
|
||||
$policy_list = ChatbotHelper::getListOfPolicies($chat_session_info);
|
||||
|
||||
// If policies found, build an HTML list of links (and raw URLs as fallback)
|
||||
if (is_array($policy_list) && count($policy_list)) {
|
||||
$parts = [];
|
||||
foreach ($policy_list as $policy) {
|
||||
// Resolve rand string (defensive)
|
||||
$randString = '';
|
||||
if (isset($policy['rand_string']) && $policy['rand_string'] !== '') {
|
||||
$randString = $policy['rand_string'];
|
||||
} elseif (isset($policy['rand']) && $policy['rand'] !== '') {
|
||||
$randString = $policy['rand'];
|
||||
} elseif (isset($policy['emp_policy_id'])) {
|
||||
// As a last resort, use emp_policy_id (not ideal but prevents broken links)
|
||||
$randString = $policy['emp_policy_id'];
|
||||
}
|
||||
|
||||
// Build link; make sure base_url produces correct path
|
||||
$link = base_url('/download-e-card/' . $randString . '/1');
|
||||
|
||||
// Escape policy name to avoid HTML injection
|
||||
$safeName = isset($policy['policy_name']) ? htmlspecialchars($policy['policy_name'], ENT_QUOTES, 'UTF-8') : 'Policy';
|
||||
|
||||
// Add to parts; include anchor and raw URL fallback
|
||||
$parts[] = "🔹 <a href=\"{$link}\" target=\"_blank\">{$safeName}</a><br>";
|
||||
}
|
||||
|
||||
$message = "Choose a policy to download (click the policy name below):<br><br>" . implode('<br><br>', $parts);
|
||||
log_message('error', 'Ecard links displayed: ' . json_encode(array_column($policy_list, 'policy_name')));
|
||||
$this->say($message);
|
||||
} else {
|
||||
log_message('error', 'No policies found for ecard download');
|
||||
$this->say('No policy found.');
|
||||
}
|
||||
|
||||
// After showing links, move to confirmation conversation (Do you want to continue?)
|
||||
// This keeps the UX identical to previous flow where we asked the user if they want to continue.
|
||||
$this->bot->startConversation(new doYouWantToContinueConversation());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected function showEcardMenuOLD()
|
||||
{
|
||||
log_message('error', ('showEcardMenu function called'));
|
||||
|
||||
$chat_session_info = get_chatbot_session_info();
|
||||
$policy_list = ChatbotHelper::getListOfPolicies($chat_session_info);
|
||||
$buttons = [];
|
||||
|
||||
$question = 'Choose Policy to Download Ecard:';
|
||||
log_message('error', ('policy_list : ' . json_encode($policy_list)));
|
||||
|
||||
if(is_array($policy_list) && count($policy_list))
|
||||
{
|
||||
foreach($policy_list as $policy)
|
||||
{
|
||||
|
||||
$this->buttonsData[$policy['emp_policy_id'].'#'.$policy['rand_string']] = ['response_text' => "🔹 {$policy['policy_name']}"] ;
|
||||
$buttons[] = Button::create("{$policy['policy_name']}")->value($policy['emp_policy_id'].'#'.$policy['rand_string']);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$question = 'No policy found';
|
||||
}
|
||||
$this->buttonsData['go_back'] = ['response_text' => '◀️ Go Back'] ;
|
||||
$buttons = array_merge($buttons,[Button::create("◀️ Go Back")->value("go_back")]);
|
||||
$question = Question::create($question)
|
||||
->addButtons($buttons);
|
||||
$temp = $this->buttonsData;
|
||||
$this->bot->ask($question, function ($answer) use ($temp) {
|
||||
log_message('error', 'showEcardMenu answer received');
|
||||
log_message('error', ($answer));
|
||||
$selectedOption = $answer->getText();
|
||||
$this->say("You have selected {$temp[$selectedOption]['response_text']}");
|
||||
switch ($answer->getValue()) {
|
||||
case "go_back":
|
||||
$this->bot->startConversation(new MainMenuConversation());
|
||||
break;
|
||||
case is_string($answer->getValue()) && is_array(explode('#',$answer->getValue())) && count((explode('#',$answer->getValue()))) == 2:
|
||||
|
||||
$link = base_url().'/download-e-card/' . explode('#',$answer->getValue())[1].'/1';
|
||||
log_message('error', $link);
|
||||
$this->say('Click here to downlad: <a href="'.$link.'" target="_blank">GMC</a><br>Click here to downlad: <a href="'.$link.'" target="_blank">GMC Parent</a>');
|
||||
$this->bot->startConversation(new doYouWantToContinueConversation()); // ✅ Restart the
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->bot->startConversation(new EcardDownloadConversation()); // ✅ Restart the conversation
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
26
app/Controllers/Chatbot/LoginToContiueConversation.php
Normal file
26
app/Controllers/Chatbot/LoginToContiueConversation.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
use BotMan\Drivers\Web\WebDriver;
|
||||
|
||||
class LoginToContiueConversation extends Conversation
|
||||
{
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->bot->userStorage()->delete();
|
||||
$this->bot->types(); // Typing indicator for the first message
|
||||
sleep(0.5); // Delay
|
||||
$this->showLoginMessage();
|
||||
}
|
||||
|
||||
protected function showLoginMessage()
|
||||
{
|
||||
$this->say("Kindly Login to use the chatbot");
|
||||
}
|
||||
}
|
||||
107
app/Controllers/Chatbot/MainMenuConversation.php
Normal file
107
app/Controllers/Chatbot/MainMenuConversation.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
use BotMan\Drivers\Web\WebDriver;
|
||||
|
||||
class MainMenuConversation extends Conversation
|
||||
{
|
||||
protected $buttonsData = [
|
||||
"ecard_download" => ["response_text" => "📄 Ecard Download"],
|
||||
"network_hospital" => ["response_text" => "🏥 Network Hospital"],
|
||||
"reimbursement_claim" => ["response_text" => "💰 Reimbursement Claim Process"],
|
||||
"reimbursement_status" => ["response_text" => "📑 Reimbursement Claim Status"],
|
||||
"new_policy" => ["response_text" => "🆕 New Policy"],
|
||||
"renew_policy" => ["response_text" => "🔄 Renew Policy"],
|
||||
];
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->bot->userStorage()->delete();
|
||||
$this->bot->types(); // Typing indicator for the first message
|
||||
// sleep(0.5); // Delay
|
||||
$this->showMainMenu();
|
||||
}
|
||||
|
||||
protected function showMainMenu()
|
||||
{
|
||||
$buttons = [];
|
||||
foreach ($this->buttonsData as $key => $data) {
|
||||
$buttons[] = Button::create($data['response_text'])->value($key);
|
||||
}
|
||||
$this->bot->userStorage()->save([
|
||||
'path' => []
|
||||
]);
|
||||
$question = Question::create("Please choose an option:")->addButtons($buttons);
|
||||
$temp = $this->buttonsData;
|
||||
$this->bot->ask($question, function ($answer) use( $temp ){
|
||||
$user_reponse = $answer->getValue();
|
||||
if (isset($temp[$user_reponse])) {
|
||||
$message = "You've chosen " . $temp[$user_reponse]['response_text'];
|
||||
// $this->bot->say($message,,WebDriver::class);
|
||||
$this->bot->reply($message);
|
||||
}
|
||||
|
||||
log_message('error', ('user_reponse before: ' . $user_reponse));
|
||||
|
||||
|
||||
if (!$answer->isInteractiveMessageReply()) {
|
||||
$user_reponse = null;
|
||||
}
|
||||
|
||||
log_message('error', ('user_reponse is interactive: ' . $answer->isInteractiveMessageReply()));
|
||||
|
||||
|
||||
// Get just the existing path array
|
||||
$path = $this->bot->userStorage()->get('path') ?? [];
|
||||
|
||||
// Add new value
|
||||
array_push($path, $answer->getValue());
|
||||
|
||||
// Save ONLY the path back
|
||||
$this->bot->userStorage()->save([
|
||||
'path' => $path
|
||||
]);
|
||||
|
||||
log_message('error', ('user_reponse after: ' . $user_reponse));
|
||||
|
||||
|
||||
switch ($user_reponse) {
|
||||
|
||||
case "ecard_download":
|
||||
$this->bot->startConversation(new EcardDownloadConversation());
|
||||
log_message('error', 'ecard_download clicked ');
|
||||
|
||||
break;
|
||||
case "network_hospital":
|
||||
$this->bot->startConversation(new NetworkHospitalConversation());
|
||||
log_message('error', 'network_hospital clicked ');
|
||||
break;
|
||||
case "reimbursement_claim":
|
||||
$this->bot->startConversation(new ReimbursementClaimProcessConversation());
|
||||
log_message('error', 'reimbursement_claim clicked ');
|
||||
break;
|
||||
case "reimbursement_status":
|
||||
$this->bot->startConversation(new ReimbursementClaimStatusConversation());
|
||||
log_message('error', 'reimbursement_status clicked ');
|
||||
break;
|
||||
case "new_policy":
|
||||
case "renew_policy":
|
||||
$this->bot->startConversation(new policyConversation());
|
||||
log_message('error', 'renew_policy || new_policy clicked ');
|
||||
break;
|
||||
default:
|
||||
log_message('error', 'default shown ');
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->bot->startConversation(new MainMenuConversation());
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
145
app/Controllers/Chatbot/NetworkHospitalConversation.php
Normal file
145
app/Controllers/Chatbot/NetworkHospitalConversation.php
Normal file
@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
// namespace App\Conversations;
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
|
||||
class NetworkHospitalConversation extends Conversation
|
||||
{
|
||||
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->bot->types(); // Typing indicator for the first message
|
||||
// sleep(0.5); // Delay
|
||||
$this->showHospitalMenu();
|
||||
}
|
||||
|
||||
protected function showHospitalMenuOLD()
|
||||
{
|
||||
$chat_session_info = get_chatbot_session_info();
|
||||
$policy_list = ChatbotHelper::getListOfPolicies($chat_session_info);
|
||||
$buttons = [];
|
||||
$question = 'Choose Policy:';
|
||||
if(is_array($policy_list) && count($policy_list))
|
||||
{
|
||||
foreach($policy_list as $policy)
|
||||
{
|
||||
// $question = Question::create("Choose Policy to Download Ecard:")
|
||||
// ->addButtons([
|
||||
// Button::create("🔹 $policy['policy_name']")->value("$policy['emp_policy_id']"),
|
||||
// Button::create("◀️ Go Back")->value("go_back"),
|
||||
// ]);
|
||||
$buttons[] = Button::create("🔹 {$policy['policy_name']}")->value($policy['emp_policy_id'].'#'.$policy['rand_string']);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$question = 'No policy found';
|
||||
}
|
||||
$buttons = array_merge($buttons,[Button::create("◀️ Go Back")->value("go_back")]);
|
||||
$question = Question::create($question)
|
||||
->addButtons($buttons);
|
||||
|
||||
// $this->bot->hears('.*', function ($bot) {
|
||||
// $this->bot->types(); // Typing indicator for the first message
|
||||
// sleep(0.5); // Delay
|
||||
// });
|
||||
|
||||
$this->bot->ask($question, function ($answer) {
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
array_push($path,
|
||||
$answer->getValue()
|
||||
);
|
||||
$this->bot->userStorage()->save([
|
||||
'path' => $path
|
||||
]);
|
||||
// log_message('error', ('user_reponse : ' . $answer->getValue()));
|
||||
// log_message('error', ('user_reponse : ' . json_encode(explode('#',$answer->getValue()))));
|
||||
switch ($answer->getValue()) {
|
||||
|
||||
case is_string($answer->getValue()) && is_array(explode('#',$answer->getValue())) && count((explode('#',$answer->getValue()))) == 2:
|
||||
|
||||
$emp_poilicy_id = explode('#',$answer->getValue());
|
||||
$emp_poilicy_id = array_shift($emp_poilicy_id);
|
||||
log_message('error','EMP POL ID FOR HOSPITAL' . $emp_poilicy_id);
|
||||
$hospital_link = ChatbotHelper::getHospitalLink($emp_poilicy_id);
|
||||
|
||||
if($hospital_link)
|
||||
{
|
||||
$this->say('<a href="' . $hospital_link . '" target="_blank">Click here for hospital details</a>');
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->say('No Data found, please contact support team');
|
||||
}
|
||||
|
||||
$this->bot->startConversation(new doYouWantToContinueConversation());
|
||||
break;
|
||||
case "go_back":
|
||||
$this->bot->startConversation(new MainMenuConversation());
|
||||
break;
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->bot->startConversation(new NetworkHospitalConversation());
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected function showHospitalMenu()
|
||||
{
|
||||
log_message('error', 'showHospitalMenu function called');
|
||||
|
||||
|
||||
// Get chat session and policies
|
||||
$chat_session_info = get_chatbot_session_info();
|
||||
$policy_list = ChatbotHelper::getListOfPolicies($chat_session_info);
|
||||
|
||||
if (is_array($policy_list) && count($policy_list)) {
|
||||
$parts = [];
|
||||
foreach ($policy_list as $policy) {
|
||||
// Resolve rand string (defensive)
|
||||
$randString = '';
|
||||
if (isset($policy['rand_string']) && $policy['rand_string'] !== '') {
|
||||
$randString = $policy['rand_string'];
|
||||
} elseif (isset($policy['rand']) && $policy['rand'] !== '') {
|
||||
$randString = $policy['rand'];
|
||||
} elseif (isset($policy['emp_policy_id'])) {
|
||||
$randString = $policy['emp_policy_id'];
|
||||
}
|
||||
|
||||
$emp_policy_id = isset($policy['emp_policy_id']) ? $policy['emp_policy_id'] : '';
|
||||
$hospital_link = ChatbotHelper::getHospitalLink($emp_policy_id);
|
||||
|
||||
$safeName = isset($policy['policy_name']) ? htmlspecialchars($policy['policy_name'], ENT_QUOTES, 'UTF-8') : 'Policy';
|
||||
|
||||
if ($hospital_link) {
|
||||
$parts[] = "🔹 <a href=\"{$hospital_link}\" target=\"_blank\">{$safeName}</a><br>";
|
||||
} else {
|
||||
$parts[] = "🔹 {$safeName} — No Data found, please contact support team";
|
||||
}
|
||||
|
||||
log_message('error', 'Hospital link for policy ' . $emp_policy_id . ': ' . $hospital_link);
|
||||
}
|
||||
|
||||
$message = "Access hospital details for your policies below:<br><br>" . implode('<br><br>', $parts);
|
||||
$this->say($message);
|
||||
} else {
|
||||
log_message('error', 'No policies found for hospital menu');
|
||||
$this->say('No policy found.');
|
||||
}
|
||||
|
||||
// After showing links, move to confirmation conversation
|
||||
$this->bot->startConversation(new doYouWantToContinueConversation());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
// namespace App\Conversations;
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
|
||||
class ReimbursementClaimProcessConversation extends Conversation
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$this->bot->types(); // Typing indicator for the first message
|
||||
sleep(0.5); // Delay
|
||||
$this->claimProcess();
|
||||
}
|
||||
|
||||
protected function claimProcess()
|
||||
{
|
||||
$chat_session_info = get_chatbot_session_info();
|
||||
$res = ChatbotHelper::sendReimbursementProcessOverMail($chat_session_info);
|
||||
if($res == true)
|
||||
{
|
||||
$this->say("Reimbursement process shared over your registered mail...!");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->say("No email found in your profile / Please contact support team");
|
||||
}
|
||||
|
||||
$this->bot->startConversation(new doYouWantToContinueConversation());
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
// namespace App\Conversations;
|
||||
namespace App\Controllers\Chatbot;
|
||||
use App\Helpers\ChatbotHelper;
|
||||
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
|
||||
|
||||
class ReimbursementClaimStatusConversation extends Conversation
|
||||
{
|
||||
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->claimStatus();
|
||||
}
|
||||
|
||||
protected function claimStatus()
|
||||
{
|
||||
$this->say("Fetching Status please wait ...");
|
||||
|
||||
$chat_session_info = get_chatbot_session_info();
|
||||
$data = ChatbotHelper::getReimbursementClaimStatus($chat_session_info);
|
||||
log_message('error','Claim Number - ' . json_encode($data['claim_number']));
|
||||
$this->bot->types();
|
||||
if ($data){
|
||||
if(isset($data['claim_number']) && $data['claim_number'] !== null)
|
||||
{
|
||||
log_message('error','Claim Number YES- ' . json_encode($data['claim_number']));
|
||||
$this->say("The claim status for the claim Number {$data['claim_number']} is currently in <strong>{$data['client_status']}</strong> status. <br> More Detailed Information is sent to your mail");
|
||||
}
|
||||
else
|
||||
{
|
||||
log_message('error','Claim Number NO- ' . json_encode($data['claim_number']));
|
||||
$this->say("The claim status for your latest claim (ID not generated) is currently in <strong>{$data['client_status']}</strong> status. <br> More Detailed Information is sent to your mail");
|
||||
}
|
||||
$this->bot->startConversation(new doYouWantToContinueConversation());
|
||||
}else{
|
||||
$this->say("No Claim Raised against the user.");
|
||||
$this->bot->startConversation(new doYouWantToContinueConversation());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
77
app/Controllers/Chatbot/commonPolicyOptionConversations.php
Normal file
77
app/Controllers/Chatbot/commonPolicyOptionConversations.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
// namespace App\Conversations;
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
|
||||
class commonPolicyOptionConversations extends Conversation
|
||||
{
|
||||
protected $buttonsData = [
|
||||
"upload_vehicle_details" => ["response_text" => "🔹 Upload Vehicle Details"],
|
||||
"service_executive" => ["response_text" => "🔹 Talk to our Service Executive"],
|
||||
"go_back" => ["response_text" => "◀️ Go Back"],
|
||||
];
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->policyOptions();
|
||||
}
|
||||
|
||||
protected function policyOptions()
|
||||
{
|
||||
$buttons = [];
|
||||
foreach ($this->buttonsData as $key => $data) {
|
||||
$buttons[] = Button::create($data['response_text'])->value($key);
|
||||
}
|
||||
// $this->bot->userStorage()->save([
|
||||
// 'path' => []
|
||||
// ]);
|
||||
$question = Question::create("Please choose an option:")->addButtons($buttons);
|
||||
$temp = $this->buttonsData;
|
||||
$this->bot->ask($question, function ($answer) use( $temp ){
|
||||
$user_reponse = $answer->getValue();
|
||||
if (isset($temp[$user_reponse])) {
|
||||
$message = "You've chosen " . $temp[$user_reponse]['response_text'];
|
||||
// $this->bot->say($message,,WebDriver::class);
|
||||
$this->bot->reply($message);
|
||||
}
|
||||
|
||||
if (!$answer->isInteractiveMessageReply()) {
|
||||
$user_reponse = null;
|
||||
}
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
array_push($path,
|
||||
$answer->getValue()
|
||||
);
|
||||
$this->bot->userStorage()->save([
|
||||
'path' => $path
|
||||
]);
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
// $this->say(json_encode($path));
|
||||
switch ($user_reponse) {
|
||||
case "upload_vehicle_details":
|
||||
// $cityName = $response->getText();
|
||||
$this->bot->startConversation(new vehicleFormConversation());
|
||||
break;
|
||||
case "service_executive":
|
||||
$this->bot->startConversation(new serviceExecutiveConversation());
|
||||
break;
|
||||
case "go_back":
|
||||
$this->bot->startConversation(new MainMenuConversation());
|
||||
break;
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->policyOptions();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
90
app/Controllers/Chatbot/doYouWantToContinueConversation.php
Normal file
90
app/Controllers/Chatbot/doYouWantToContinueConversation.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
// namespace App\Conversations;
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
|
||||
class doYouWantToContinueConversation extends Conversation
|
||||
{
|
||||
protected $buttonsData = [
|
||||
"yes" => ["response_text" => "🔹 Yes I need some more Help"],
|
||||
"no" => ["response_text" => "🔹 No, Thank You"]
|
||||
];
|
||||
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->bot->types(); // Typing indicator for the first message
|
||||
sleep(3); // Delay
|
||||
$this->askQuestion();
|
||||
}
|
||||
|
||||
protected function askQuestion()
|
||||
{
|
||||
$buttons = [];
|
||||
foreach ($this->buttonsData as $key => $data) {
|
||||
$buttons[] = Button::create($data['response_text'])->value($key);
|
||||
}
|
||||
// $this->bot->userStorage()->save([
|
||||
// 'path' => []
|
||||
// ]);
|
||||
$question = Question::create("Please choose an option:")->addButtons($buttons);
|
||||
$temp = $this->buttonsData;
|
||||
$this->bot->ask($question, function ($answer) use( $temp ){
|
||||
$user_reponse = $answer->getValue();
|
||||
if (isset($temp[$user_reponse])) {
|
||||
$message = "You've chosen " . $temp[$user_reponse]['response_text'];
|
||||
// $this->bot->say($message,,WebDriver::class);
|
||||
$this->bot->reply($message);
|
||||
}
|
||||
|
||||
if (!$answer->isInteractiveMessageReply()) {
|
||||
$user_reponse = null;
|
||||
}
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
array_push($path,
|
||||
$answer->getValue()
|
||||
);
|
||||
$this->bot->userStorage()->save([
|
||||
'path' => $path
|
||||
]);
|
||||
|
||||
switch ($user_reponse) {
|
||||
case "yes":
|
||||
|
||||
// $this->say("Sure, How can I help you ");
|
||||
$this->bot->startConversation(new MainMenuConversation());
|
||||
break;
|
||||
case "no":
|
||||
// $user = $this->bot->getUser();
|
||||
// $userID = $user->getId();
|
||||
$dataBefore = $this->bot->userStorage()->all();
|
||||
// log_message('info','Before Deletion: ' . json_encode($dataBefore));
|
||||
|
||||
$this->bot->userStorage()->delete();
|
||||
$this->bot->userStorage()->save([]);
|
||||
|
||||
|
||||
// Cache::flush();
|
||||
|
||||
$dataAfter = $this->bot->userStorage()->all();
|
||||
// log_message('info','After Deletion: ' . json_encode($dataAfter));
|
||||
|
||||
|
||||
$this->say("Thank you for your time! If you need anything else, feel free to reach out. Goodbye!");
|
||||
$this->say("Say Hi or Help or Hello to start conversation again!");
|
||||
break;
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->askQuestion();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
75
app/Controllers/Chatbot/fourWheelerOptionsConversations.php
Normal file
75
app/Controllers/Chatbot/fourWheelerOptionsConversations.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
// namespace App\Conversations;
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
|
||||
class fourWheelerOptionsConversations extends Conversation
|
||||
{
|
||||
protected $buttonsData = [
|
||||
"comp" => ["response_text" => "🔹 Comprehensive"],
|
||||
"3p" => ["response_text" => "🔹 Third-Party Only"],
|
||||
"go_back" => ["response_text" => "◀️ Go Back"],
|
||||
];
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->show4WOptions();
|
||||
}
|
||||
|
||||
protected function show4WOptions()
|
||||
{
|
||||
$buttons = [];
|
||||
foreach ($this->buttonsData as $key => $data) {
|
||||
$buttons[] = Button::create($data['response_text'])->value($key);
|
||||
}
|
||||
// $this->bot->userStorage()->save([
|
||||
// 'path' => []
|
||||
// ]);
|
||||
$question = Question::create("Please choose an option:")->addButtons($buttons);
|
||||
$temp = $this->buttonsData;
|
||||
$this->bot->ask($question, function ($answer) use( $temp ){
|
||||
$user_reponse = $answer->getValue();
|
||||
if (isset($temp[$user_reponse])) {
|
||||
$message = "You've chosen " . $temp[$user_reponse]['response_text'];
|
||||
// $this->bot->say($message,,WebDriver::class);
|
||||
$this->bot->reply($message);
|
||||
}
|
||||
|
||||
if (!$answer->isInteractiveMessageReply()) {
|
||||
$user_reponse = null;
|
||||
}
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
array_push($path,
|
||||
$answer->getValue()
|
||||
);
|
||||
$this->bot->userStorage()->save([
|
||||
'path' => $path
|
||||
]);
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
// $this->say(json_encode($path));
|
||||
switch ($user_reponse) {
|
||||
case "3p":
|
||||
// $cityName = $response->getText();
|
||||
$this->bot->startConversation(new commonPolicyOptionConversations());
|
||||
break;
|
||||
case "comp":
|
||||
$this->bot->startConversation(new commonPolicyOptionConversations());
|
||||
break;
|
||||
case "go_back":
|
||||
$this->bot->startConversation(new MainMenuConversation());
|
||||
break;
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->showHospitalMenu();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
72
app/Controllers/Chatbot/hospitalPolicyConversation.php
Normal file
72
app/Controllers/Chatbot/hospitalPolicyConversation.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
// namespace App\Conversations;
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
|
||||
class hospitalPolicyConversation extends Conversation
|
||||
{
|
||||
protected $buttonsData = [
|
||||
"individual" => ["response_text" => "🔹 Individual"],
|
||||
"floater" => ["response_text" => "🔹 Floater"],
|
||||
"go_back" => ["response_text" => "◀️ Go Back"],
|
||||
];
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->showMediclaimPolicyOptions();
|
||||
}
|
||||
|
||||
protected function showMediclaimPolicyOptions()
|
||||
{
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
|
||||
if (in_array('individual',$path)){
|
||||
$text = 'Upload Policy Copy and Individual Details';
|
||||
$value = 'individual_upload';
|
||||
}else{
|
||||
$text = 'Upload Policy Copy and Family Details';
|
||||
$value = 'family_upload';
|
||||
}
|
||||
$question = Question::create("Choose Policy Type:")
|
||||
->addButtons([
|
||||
Button::create("🔹 ".$text)->value($value),
|
||||
Button::create("🔹 Talk to our Service Executive")->value("service_executive"),
|
||||
Button::create("◀️ Go Back")->value("go_back"),
|
||||
]);
|
||||
|
||||
$this->bot->ask($question, function ($answer) use ($value, $text){
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
array_push($path,
|
||||
$answer->getValue()
|
||||
);
|
||||
$this->bot->userStorage()->save([
|
||||
'path' => $path
|
||||
]);
|
||||
switch ($answer->getValue()) {
|
||||
case $value:
|
||||
$this->say("you have chosen ".$text);
|
||||
|
||||
$this->bot->startConversation(new medicalClaimFormConversations());
|
||||
break;
|
||||
case "service_executive":
|
||||
$this->say('You have chosen to speak with our Service Executive');
|
||||
$this->bot->startConversation(new serviceExecutiveConversation());
|
||||
break;
|
||||
case "go_back":
|
||||
$this->bot->startConversation(new medicalClaimOptionConversations());
|
||||
break;
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->showHospitalMenu();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
317
app/Controllers/Chatbot/medicalClaimFormConversations.php
Normal file
317
app/Controllers/Chatbot/medicalClaimFormConversations.php
Normal file
@ -0,0 +1,317 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
|
||||
class medicalClaimFormConversations extends Conversation
|
||||
{
|
||||
private $currentMemberData = [
|
||||
'name' => null,
|
||||
'dob' => null,
|
||||
'si' => null,
|
||||
'pre_existing_disease' => null
|
||||
];
|
||||
|
||||
public function run()
|
||||
{
|
||||
// Initialize state if not exists
|
||||
if (!$this->bot->userStorage()->get('claim_form_state')) {
|
||||
$this->bot->userStorage()->save([
|
||||
'claim_form_state' => [
|
||||
'dependency_count' => 0,
|
||||
'current_member' => 'primary',
|
||||
'dependencies' => [],
|
||||
'primary_insurer' => null
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
$state = $this->bot->userStorage()->get('claim_form_state');
|
||||
$memberType = ($state['current_member'] === 'primary') ? 'primary insured person' : 'dependency';
|
||||
|
||||
$this->askName($memberType);
|
||||
}
|
||||
|
||||
private function askName($memberType)
|
||||
{
|
||||
$this->ask("Enter the Name of the $memberType?", function($answer) {
|
||||
$name = $answer->getText();
|
||||
|
||||
if (empty($name)) {
|
||||
$this->say('Name Cannot be Empty');
|
||||
return $this->run();
|
||||
}
|
||||
|
||||
// Get existing state and update only the name
|
||||
$state = $this->bot->userStorage()->get('claim_form_state');
|
||||
if ($state['current_member'] === 'primary') {
|
||||
if (!isset($state['primary_insurer'])) {
|
||||
$state['primary_insurer'] = [];
|
||||
}
|
||||
$state['primary_insurer']['name'] = $name;
|
||||
} else {
|
||||
if (!isset($state['temp_dependency'])) {
|
||||
$state['temp_dependency'] = [];
|
||||
}
|
||||
$state['temp_dependency']['name'] = $name;
|
||||
}
|
||||
|
||||
$this->bot->userStorage()->save(['claim_form_state' => $state]);
|
||||
$this->askDOB();
|
||||
});
|
||||
}
|
||||
|
||||
private function askDOB()
|
||||
{
|
||||
$this->ask('Enter Date of Birth (DD/MM/YYYY)?', function($answer) {
|
||||
$dob = $answer->getText();
|
||||
|
||||
if (empty($dob)) {
|
||||
$this->say('Date of Birth Cannot be Empty');
|
||||
return $this->askDOB();
|
||||
}
|
||||
|
||||
// Get existing state and update only the DOB
|
||||
$state = $this->bot->userStorage()->get('claim_form_state');
|
||||
if ($state['current_member'] === 'primary') {
|
||||
$state['primary_insurer']['dob'] = $dob;
|
||||
} else {
|
||||
$state['temp_dependency']['dob'] = $dob;
|
||||
}
|
||||
|
||||
$this->bot->userStorage()->save(['claim_form_state' => $state]);
|
||||
$this->askSumInsured();
|
||||
});
|
||||
}
|
||||
|
||||
private function askSumInsured()
|
||||
{
|
||||
$this->ask('Enter Sum Insured Amount?', function($answer) {
|
||||
$sum_insured = $answer->getText();
|
||||
|
||||
if (empty($sum_insured)) {
|
||||
$this->say('Sum Insured Cannot be Empty');
|
||||
return $this->askSumInsured();
|
||||
}
|
||||
|
||||
// Get existing state and update only the sum insured
|
||||
$state = $this->bot->userStorage()->get('claim_form_state');
|
||||
if ($state['current_member'] === 'primary') {
|
||||
$state['primary_insurer']['si'] = $sum_insured;
|
||||
} else {
|
||||
$state['temp_dependency']['si'] = $sum_insured;
|
||||
}
|
||||
|
||||
$this->bot->userStorage()->save(['claim_form_state' => $state]);
|
||||
$this->askPreExistingDisease();
|
||||
});
|
||||
}
|
||||
|
||||
private function askPreExistingDisease()
|
||||
{
|
||||
$this->ask('Enter Pre-Existing Disease if any (type "none" if none)?', function($answer) {
|
||||
$pre_existing_disease = $answer->getText();
|
||||
|
||||
if (empty($pre_existing_disease)) {
|
||||
$this->say('Please enter none if no pre-existing conditions');
|
||||
return $this->askPreExistingDisease();
|
||||
}
|
||||
|
||||
// Get existing state and update the disease info
|
||||
$state = $this->bot->userStorage()->get('claim_form_state');
|
||||
if ($state['current_member'] === 'primary') {
|
||||
$state['primary_insurer']['pre_existing_disease'] = $pre_existing_disease;
|
||||
$state['primary_insurer']['type'] = 'primary';
|
||||
} else {
|
||||
$state['temp_dependency']['pre_existing_disease'] = $pre_existing_disease;
|
||||
$state['temp_dependency']['type'] = 'dependency';
|
||||
|
||||
// Move completed dependency to dependencies array
|
||||
if (!isset($state['dependencies'])) {
|
||||
$state['dependencies'] = [];
|
||||
}
|
||||
$state['dependencies'][] = $state['temp_dependency'];
|
||||
$state['temp_dependency'] = null; // Clear temporary dependency
|
||||
}
|
||||
|
||||
$this->bot->userStorage()->save(['claim_form_state' => $state]);
|
||||
|
||||
// For debugging
|
||||
log_message('debug', 'Final state after all data collection: ' . json_encode($state));
|
||||
|
||||
$this->saveMemberAndContinue();
|
||||
});
|
||||
}
|
||||
|
||||
private function saveMemberAndContinue()
|
||||
{
|
||||
$state = $this->bot->userStorage()->get('claim_form_state');
|
||||
$memberData = $state['current_member'] === 'primary'
|
||||
? $state['primary_insurer']
|
||||
: end($state['dependencies']);
|
||||
|
||||
// Show current member info
|
||||
$memberInfo = "👤 **" . ucfirst($memberData['type']) . " Member Details:**\n";
|
||||
$memberInfo .= "🔹 Name: " . $memberData['name'] . "\n";
|
||||
$memberInfo .= "🔹 Date of Birth: " . $memberData['dob'] . "\n";
|
||||
$memberInfo .= "🔹 Sum Insured: " . $memberData['si'] . "\n";
|
||||
$memberInfo .= "🔹 Pre Existing Disease: " . $memberData['pre_existing_disease'] . "\n";
|
||||
|
||||
$this->say($memberInfo);
|
||||
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
|
||||
if (in_array('floater', $path)) {
|
||||
$this->askAboutDependencies();
|
||||
} else {
|
||||
$this->showSummary();
|
||||
}
|
||||
}
|
||||
|
||||
private function askAboutDependencies()
|
||||
{
|
||||
$question = Question::create("Do you want to add a dependency? ")
|
||||
->addButtons([
|
||||
Button::create("✅ Yes")->value("yes"),
|
||||
Button::create("❌ No")->value("no"),
|
||||
Button::create("◀️ Go Back")->value("go_back"),
|
||||
]);
|
||||
|
||||
$this->ask($question, function($answer) {
|
||||
$state = $this->bot->userStorage()->get('claim_form_state');
|
||||
|
||||
switch ($answer->getValue()) {
|
||||
case "yes":
|
||||
$state['dependency_count']++;
|
||||
$state['current_member'] = 'dependency';
|
||||
$this->bot->userStorage()->save(['claim_form_state' => $state]);
|
||||
|
||||
// Reset currentMemberData for new dependency
|
||||
$this->currentMemberData = [
|
||||
'name' => null,
|
||||
'dob' => null,
|
||||
'si' => null,
|
||||
'pre_existing_disease' => null
|
||||
];
|
||||
|
||||
$this->run();
|
||||
break;
|
||||
|
||||
case "no":
|
||||
$this->showSummary();
|
||||
break;
|
||||
|
||||
case "go_back":
|
||||
$this->bot->startConversation(new hospitalPolicyConversation());
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->askAboutDependencies();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function showSummary()
|
||||
{
|
||||
$state = $this->bot->userStorage()->get('claim_form_state');
|
||||
|
||||
$finalSummary = '<div style="font-family: Arial, sans-serif; font-size: 15px; margin: 0; padding: 2px;">';
|
||||
|
||||
// Primary Insurer
|
||||
if ($state['primary_insurer']) {
|
||||
$finalSummary .= '<div style="margin: 2px 0; padding-left: 5px;">Primary:<br>' .
|
||||
$this->formatSingleLineMemberInfo($state['primary_insurer']) .
|
||||
'</div>';
|
||||
}
|
||||
|
||||
// Dependencies
|
||||
if (!empty($state['dependencies'])) {
|
||||
foreach ($state['dependencies'] as $index => $dependency) {
|
||||
$finalSummary .= '<div style="margin: 2px 0; padding-left: 5px;">Dep ' . ($index + 1) . ':<br>' .
|
||||
$this->formatSingleLineMemberInfo($dependency) .
|
||||
'</div>';
|
||||
}
|
||||
}
|
||||
|
||||
$finalSummary .= '</div>';
|
||||
|
||||
$this->say($finalSummary);
|
||||
|
||||
$question = Question::create("Confirm all details:")
|
||||
->addButtons([
|
||||
Button::create("✅ Confirm")->value("confirm"),
|
||||
Button::create("🔁 Start Over")->value("restart"),
|
||||
Button::create("❌ Cancel")->value('cancel')
|
||||
]);
|
||||
|
||||
$this->ask($question, function ($answer) use ($finalSummary) {
|
||||
switch ($answer->getValue()) {
|
||||
case "confirm":
|
||||
$sent_status = ChatbotHelper::sendQuotationRequestMail($finalSummary);
|
||||
if ($sent_status) {
|
||||
$this->say('Our Executive will contact you with the quotation soon.');
|
||||
$this->bot->startConversation(new doYouWantToContinueConversation());
|
||||
} else {
|
||||
$this->say("There was a problem while requesting for a quotation. Please try again later.");
|
||||
}
|
||||
break;
|
||||
case "restart":
|
||||
$this->bot->userStorage()->save([
|
||||
'claim_form_state' => [
|
||||
'dependency_count' => 0,
|
||||
'current_member' => 'primary',
|
||||
'dependencies' => [],
|
||||
'primary_insurer' => null
|
||||
]
|
||||
]);
|
||||
$this->run();
|
||||
break;
|
||||
case "cancel":
|
||||
$this->bot->startConversation(new MainMenuConversation());
|
||||
break;
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->showSummary();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function formatSingleLineMemberInfo($member)
|
||||
{
|
||||
return '🔹 Name: ' . htmlspecialchars($member['name']) . '<br>' .
|
||||
'🔹 DOB: ' . htmlspecialchars($member['dob']) . '<br>' .
|
||||
'🔹 SI: ' . htmlspecialchars($member['si']) . '<br>' .
|
||||
'🔹 PED: ' . htmlspecialchars($member['pre_existing_disease']);
|
||||
}
|
||||
|
||||
|
||||
// private function formatMemberInfo($member)
|
||||
// {
|
||||
// // Format each piece of information with proper HTML
|
||||
// return '<div style="line-height: 1.6;">
|
||||
// <div style="margin-bottom: 5px;">
|
||||
// <span style="color: #2c3e50;">🔹 Name:</span>
|
||||
// <span style="color: #34495e; font-weight: 500;">' . htmlspecialchars($member['name']) . '</span>
|
||||
// </div>
|
||||
// <div style="margin-bottom: 5px;">
|
||||
// <span style="color: #2c3e50;">🔹 DOB:</span>
|
||||
// <span style="color: #34495e; font-weight: 500;">' . htmlspecialchars($member['dob']) . '</span>
|
||||
// </div>
|
||||
// <div style="margin-bottom: 5px;">
|
||||
// <span style="color: #2c3e50;">🔹 Sum Insured:</span>
|
||||
// <span style="color: #34495e; font-weight: 500;">' . htmlspecialchars($member['si']) . '</span>
|
||||
// </div>
|
||||
// <div style="margin-bottom: 5px;">
|
||||
// <span style="color: #2c3e50;">🔹 Pre-Existing Conditions:</span>
|
||||
// <span style="color: #34495e; font-weight: 500;">' . htmlspecialchars($member['pre_existing_disease']) . '</span>
|
||||
// </div>
|
||||
// </div>';
|
||||
// }
|
||||
}
|
||||
75
app/Controllers/Chatbot/medicalClaimOptionConversations.php
Normal file
75
app/Controllers/Chatbot/medicalClaimOptionConversations.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
// namespace App\Conversations;
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
|
||||
class medicalClaimOptionConversations extends Conversation
|
||||
{
|
||||
|
||||
protected $buttonsData = [
|
||||
"individual" => ["response_text" => "🔹 Individual"],
|
||||
"floater" => ["response_text" => "🔹 Floater"],
|
||||
"go_back" => ["response_text" => "◀️ Go Back"],
|
||||
];
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->showMediclaimOptions();
|
||||
}
|
||||
|
||||
protected function showMediclaimOptions()
|
||||
{
|
||||
$buttons = [];
|
||||
foreach ($this->buttonsData as $key => $data) {
|
||||
$buttons[] = Button::create($data['response_text'])->value($key);
|
||||
}
|
||||
// $this->bot->userStorage()->save([
|
||||
// 'path' => []
|
||||
// ]);
|
||||
$question = Question::create("Please choose an option:")->addButtons($buttons);
|
||||
$temp = $this->buttonsData;
|
||||
$this->bot->ask($question, function ($answer) use( $temp ){
|
||||
$user_reponse = $answer->getValue();
|
||||
if (isset($temp[$user_reponse])) {
|
||||
$message = "You've chosen " . $temp[$user_reponse]['response_text'];
|
||||
// $this->bot->say($message,,WebDriver::class);
|
||||
$this->bot->reply($message);
|
||||
}
|
||||
|
||||
if (!$answer->isInteractiveMessageReply()) {
|
||||
$user_reponse = null;
|
||||
}
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
array_push($path,
|
||||
$answer->getValue()
|
||||
);
|
||||
$this->bot->userStorage()->save([
|
||||
'path' => $path
|
||||
]);
|
||||
|
||||
switch ($user_reponse) {
|
||||
case "individual":
|
||||
// $cityName = $response->getText();
|
||||
$this->bot->startConversation(new hospitalPolicyConversation());
|
||||
break;
|
||||
case "floater":
|
||||
$this->bot->startConversation(new hospitalPolicyConversation());
|
||||
break;
|
||||
case "go_back":
|
||||
$this->bot->startConversation(new MainMenuConversation());
|
||||
break;
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->showHospitalMenu();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
82
app/Controllers/Chatbot/policyConversation.php
Normal file
82
app/Controllers/Chatbot/policyConversation.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
// namespace App\Conversations;
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
|
||||
class policyConversation extends Conversation
|
||||
{
|
||||
protected $buttonsData = [
|
||||
"2_wheeler" => ["response_text" => "🏍️ 2 Wheeler"],
|
||||
"4_wheeler" => ["response_text" => "🚗 4 Wheeler"],
|
||||
"mediclaim" => ["response_text" => "🩺 Medical Claim"],
|
||||
"go_back" => ["response_text" => "◀️ Go Back"],
|
||||
];
|
||||
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->showNewPolicy();
|
||||
}
|
||||
|
||||
protected function showNewPolicy()
|
||||
{
|
||||
$buttons = [];
|
||||
foreach ($this->buttonsData as $key => $data) {
|
||||
$buttons[] = Button::create($data['response_text'])->value($key);
|
||||
}
|
||||
// $this->bot->userStorage()->save([
|
||||
// 'path' => []
|
||||
// ]);
|
||||
$question = Question::create("Please choose an option:")->addButtons($buttons);
|
||||
$temp = $this->buttonsData;
|
||||
$this->bot->ask($question, function ($answer) use( $temp ){
|
||||
$user_reponse = $answer->getValue();
|
||||
if (isset($temp[$user_reponse])) {
|
||||
$message = "You've chosen " . $temp[$user_reponse]['response_text'];
|
||||
// $this->bot->say($message,,WebDriver::class);
|
||||
$this->bot->reply($message);
|
||||
}
|
||||
|
||||
if (!$answer->isInteractiveMessageReply()) {
|
||||
$user_reponse = null;
|
||||
}
|
||||
|
||||
log_message('error', ('user_reponse : ' . $answer->getValue()));
|
||||
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
array_push($path,
|
||||
$answer->getValue()
|
||||
);
|
||||
$this->bot->userStorage()->save([
|
||||
'path' => $path
|
||||
]);
|
||||
switch ($user_reponse) {
|
||||
case "2_wheeler":
|
||||
// $cityName = $response->getText();
|
||||
$this->bot->startConversation(new twoWheelerOptionsConversations());
|
||||
break;
|
||||
case "4_wheeler":
|
||||
$this->bot->startConversation(new fourWheelerOptionsConversations());
|
||||
break;
|
||||
case "mediclaim":
|
||||
$this->bot->startConversation(new medicalClaimOptionConversations());
|
||||
|
||||
break;
|
||||
case "go_back":
|
||||
$this->bot->startConversation(new MainMenuConversation());
|
||||
break;
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->showHospitalMenu();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
93
app/Controllers/Chatbot/selectInsurerConversations.php
Normal file
93
app/Controllers/Chatbot/selectInsurerConversations.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
// namespace App\Conversations;
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
|
||||
class selectInsurerConversations extends Conversation
|
||||
{
|
||||
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->insurerOptions();
|
||||
}
|
||||
|
||||
protected function insurerOptions()
|
||||
{
|
||||
$question = Question::create("Choose Your Options:")
|
||||
->addButtons([
|
||||
Button::create("🔹 Select Insurers")->value("insurers"),
|
||||
Button::create("🔹 Talk to our Service Executive")->value("service_executive"),
|
||||
Button::create("◀️ Go Back")->value("go_back"),
|
||||
]);
|
||||
|
||||
$this->bot->ask($question, function ($answer) {
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
array_push($path,
|
||||
$answer->getValue()
|
||||
);
|
||||
$this->bot->userStorage()->save([
|
||||
'path' => $path
|
||||
]);
|
||||
switch ($answer->getValue()) {
|
||||
case "insurers":
|
||||
$data = ChatbotHelper::getInsurersList();
|
||||
log_message("error",'data: '.json_encode($data));
|
||||
$question = Question::create("Select an Insurer:");
|
||||
$page = $this->bot->userStorage()->get('insurer_page') ?? 0;
|
||||
$perPage = 5;
|
||||
$totalPages = ceil(count($data) / $perPage);
|
||||
$insurersToShow = array_slice($data, $page * $perPage, $perPage);
|
||||
|
||||
$buttons = [];
|
||||
foreach ($insurersToShow as $insurer) {
|
||||
$buttons[] = Button::create("🔹 " . $insurer['short_name'])->value($insurer['short_name']);
|
||||
}
|
||||
|
||||
// Add pagination buttons
|
||||
if ($page > 0) {
|
||||
$buttons[] = Button::create("⬅️ Previous")->value("previous_page");
|
||||
}
|
||||
if (($page + 1) < $totalPages) {
|
||||
$buttons[] = Button::create("➡️ Next")->value("next_page");
|
||||
}
|
||||
|
||||
$question = Question::create("Choose an Insurer:")
|
||||
->addButtons($buttons);
|
||||
|
||||
$this->bot->ask($question, function ($response) {
|
||||
$selected = $response->getValue();
|
||||
|
||||
if ($selected == "previous_page") {
|
||||
$this->bot->userStorage()->save(["insurer_page" => max(0, $this->bot->userStorage()->get('insurer_page') - 1)]);
|
||||
$this->insurerOptions();
|
||||
} elseif ($selected == "next_page") {
|
||||
$this->bot->userStorage()->save(["insurer_page" => $this->bot->userStorage()->get('insurer_page') + 1]);
|
||||
$this->insurerOptions();
|
||||
} else {
|
||||
$this->say("You selected: " . $selected);
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
case "service_executive":
|
||||
$this->bot->startConversation(new serviceExecutiveConversation());
|
||||
break;
|
||||
case "go_back":
|
||||
$this->bot->startConversation(new MainMenuConversation());
|
||||
break;
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->policyOptions();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
68
app/Controllers/Chatbot/serviceExecutiveConversation.php
Normal file
68
app/Controllers/Chatbot/serviceExecutiveConversation.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
|
||||
class serviceExecutiveConversation extends Conversation
|
||||
{
|
||||
protected $mobile_number;
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->askServiceExecutive();
|
||||
}
|
||||
|
||||
protected function askServiceExecutive()
|
||||
{
|
||||
$chat_session_info = get_chatbot_session_info();
|
||||
$mobile = ChatbotHelper::getPhoneNumber($chat_session_info); // Assuming this fetches the user's phone number
|
||||
$question = Question::create("Is this Your Mobile Number: $mobile")
|
||||
->addButtons([
|
||||
Button::create("✅ Yes")->value("yes"),
|
||||
Button::create("❌ No")->value("no"),
|
||||
Button::create("◀️ Go Back")->value("go_back"),
|
||||
]);
|
||||
|
||||
$this->bot->ask($question, function ($answer) {
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
array_push($path,
|
||||
$answer->getValue()
|
||||
);
|
||||
$this->bot->userStorage()->save([
|
||||
'path' => $path
|
||||
]);
|
||||
switch ($answer->getValue()) {
|
||||
case "yes":
|
||||
$this->say("Our executive will call you at the earliest.");
|
||||
$this->bot->startConversation(new doYouWantToContinueConversation());
|
||||
break;
|
||||
|
||||
case "no":
|
||||
$this->ask('Enter Your Mobile Number?', function ($response) {
|
||||
$this->mobile_number = $response->getText();
|
||||
$this->bot->userStorage()->save([
|
||||
'mobile_number' => $this->mobile_number
|
||||
]);
|
||||
// Use 'use' to bring the class context into the closure
|
||||
$this->say("Our executive will call you at the earliest.");
|
||||
$this->bot->startConversation(new doYouWantToContinueConversation());
|
||||
});
|
||||
break;
|
||||
|
||||
case "go_back":
|
||||
$this->bot->startConversation(new MainMenuConversation());
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->askServiceExecutive();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
75
app/Controllers/Chatbot/twoWheelerOptionsConversations.php
Normal file
75
app/Controllers/Chatbot/twoWheelerOptionsConversations.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
// namespace App\Conversations;
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
|
||||
class twoWheelerOptionsConversations extends Conversation
|
||||
{
|
||||
protected $buttonsData = [
|
||||
"3p" => ["response_text" => "🔹 Third-Party"],
|
||||
"comp" => ["response_text" => "🔹 Comprehensive"],
|
||||
"go_back" => ["response_text" => "◀️ Go Back"]
|
||||
];
|
||||
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->show2WOptions();
|
||||
}
|
||||
|
||||
protected function show2WOptions()
|
||||
{
|
||||
$buttons = [];
|
||||
foreach ($this->buttonsData as $key => $data) {
|
||||
$buttons[] = Button::create($data['response_text'])->value($key);
|
||||
}
|
||||
// $this->bot->userStorage()->save([
|
||||
// 'path' => []
|
||||
// ]);
|
||||
$question = Question::create("Please choose an option:")->addButtons($buttons);
|
||||
$temp = $this->buttonsData;
|
||||
$this->bot->ask($question, function ($answer) use( $temp ){
|
||||
$user_reponse = $answer->getValue();
|
||||
if (isset($temp[$user_reponse])) {
|
||||
$message = "You've chosen " . $temp[$user_reponse]['response_text'];
|
||||
// $this->bot->say($message,,WebDriver::class);
|
||||
$this->bot->reply($message);
|
||||
}
|
||||
|
||||
if (!$answer->isInteractiveMessageReply()) {
|
||||
$user_reponse = null;
|
||||
}
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
array_push($path,
|
||||
$answer->getValue()
|
||||
);
|
||||
$this->bot->userStorage()->save([
|
||||
'path' => $path
|
||||
]);
|
||||
|
||||
switch ($user_reponse) {
|
||||
case "3p":
|
||||
// $cityName = $response->getText();
|
||||
$this->bot->startConversation(new commonPolicyOptionConversations());
|
||||
break;
|
||||
case "comp":
|
||||
$this->bot->startConversation(new commonPolicyOptionConversations());
|
||||
break;
|
||||
case "go_back":
|
||||
$this->bot->startConversation(new MainMenuConversation());
|
||||
break;
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->showHospitalMenu();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
219
app/Controllers/Chatbot/vehicleFormConversation.php
Normal file
219
app/Controllers/Chatbot/vehicleFormConversation.php
Normal file
@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers\Chatbot;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
use BotMan\BotMan\Messages\Outgoing\Question;
|
||||
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
|
||||
use BotMan\BotMan\Messages\Conversations\Conversation;
|
||||
|
||||
class vehicleFormConversation extends Conversation
|
||||
{
|
||||
protected $vehicleName;
|
||||
protected $vehicleYOM;
|
||||
protected $vehicleEngineNo;
|
||||
protected $vehicleInvoiceNo;
|
||||
protected $userId;
|
||||
protected $complied_information;
|
||||
protected $fuelType;
|
||||
protected $seater;
|
||||
protected $chassisNumber;
|
||||
|
||||
public function run()
|
||||
{
|
||||
// Ensure $this->bot is properly initialized
|
||||
// $user = $this->bot->getUser();
|
||||
// $this->userId = $user->getId(); // Correctly store user ID
|
||||
|
||||
$this->askVehicleName(); // Start asking vehicle questions
|
||||
}
|
||||
|
||||
protected function askVehicleName()
|
||||
{
|
||||
$this->ask('Enter Vehicle Name ?', function ($response) {
|
||||
$this->vehicleName = $response->getText();
|
||||
if (empty($this->vehicleName)) {
|
||||
$this->say('Vehicle Name Cannot be Empty');
|
||||
return $this->askVehicleName(); // Re-ask if empty
|
||||
}
|
||||
// $this->askVehicleYOM(); // Ask next question
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
|
||||
if (in_array('4_wheeler', $path)) {
|
||||
$this->askFuelType();
|
||||
} else {
|
||||
$this->askVehicleYOM(); // Ask next question
|
||||
}
|
||||
});
|
||||
}
|
||||
protected function askFuelType()
|
||||
{
|
||||
$this->ask('Enter Fuel Type of the Vehicle ?', function ($response) {
|
||||
$this->fuelType = $response->getText();
|
||||
if (empty($this->fuelType)) {
|
||||
$this->say('Vehicle Fuel Type Cannot be Empty');
|
||||
return $this->askFuelType(); // Re-ask if empty
|
||||
}
|
||||
$this->askSeating(); // Ask next question
|
||||
});
|
||||
}
|
||||
protected function askSeating()
|
||||
{
|
||||
$this->ask('Enter the seat count of the Vehicle ?', function ($response) {
|
||||
$this->seater = $response->getText();
|
||||
if (empty($this->seater)) {
|
||||
$this->say('Vehicle seat Cannot be Empty');
|
||||
return $this->askSeating(); // Re-ask if empty
|
||||
}
|
||||
$this->askVehicleYOM(); // Ask next question
|
||||
});
|
||||
}
|
||||
|
||||
protected function askVehicleYOM()
|
||||
{
|
||||
$this->ask('Enter Vehicle Year of Manufacture ?', function ($response) {
|
||||
$this->vehicleYOM = $response->getText();
|
||||
if (empty($this->vehicleYOM)) {
|
||||
$this->say('Vehicle Year of Manufacture Cannot be Empty');
|
||||
return $this->askVehicleYOM(); // Re-ask if empty
|
||||
}
|
||||
$this->askVehicleEngineNo(); // Ask next question
|
||||
});
|
||||
}
|
||||
|
||||
protected function askVehicleEngineNo()
|
||||
{
|
||||
$this->ask('Enter Vehicle Engine Number ?', function ($response) {
|
||||
$this->vehicleEngineNo = $response->getText();
|
||||
if (empty($this->vehicleEngineNo)) {
|
||||
$this->say('Vehicle Engine Number Cannot be Empty');
|
||||
return $this->askVehicleEngineNo(); // Re-ask if empty
|
||||
}
|
||||
|
||||
// Retrieve 'path' from storage
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
|
||||
if (in_array('4_wheeler', $path)) {
|
||||
$this->askChassisNumber();
|
||||
} else {
|
||||
$this->askVehicleInvoiceNo(); // Ask next question
|
||||
}
|
||||
// $this->askVehicleInvoiceNo();
|
||||
});
|
||||
}
|
||||
protected function askChassisNumber()
|
||||
{
|
||||
$this->ask('Enter Chassis Number of the Vehicle ?', function ($response) {
|
||||
$this->chassisNumber = $response->getText();
|
||||
if (empty($this->chassisNumber)) {
|
||||
$this->say('Vehicle Chassis Number Cannot be Empty');
|
||||
return $this->askChassisNumber(); // Re-ask if empty
|
||||
}
|
||||
$this->askVehicleInvoiceNo(); // Ask next question
|
||||
});
|
||||
}
|
||||
|
||||
protected function askVehicleInvoiceNo()
|
||||
{
|
||||
$this->ask('Enter Vehicle Invoice Number ?', function ($response) {
|
||||
$this->vehicleInvoiceNo = $response->getText();
|
||||
if (empty($this->vehicleInvoiceNo)) {
|
||||
$this->say('Vehicle Invoice Number Cannot be Empty');
|
||||
return $this->askVehicleInvoiceNo(); // Re-ask if empty
|
||||
}
|
||||
$this->storeVehicleData(); // Proceed to store the data
|
||||
});
|
||||
}
|
||||
|
||||
protected function storeVehicleData()
|
||||
{
|
||||
// Retrieve 'path' from storage
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
// $this->say(json_encode($path));
|
||||
// Prepare vehicle details
|
||||
$vehicleDetails = [
|
||||
'vehicle_name' => $this->vehicleName,
|
||||
'vehicle_yom' => $this->vehicleYOM,
|
||||
'vehicle_engine_no' => $this->vehicleEngineNo,
|
||||
$vehicleDetails['vehicle_invoice_no'] = $this->vehicleInvoiceNo
|
||||
|
||||
];
|
||||
|
||||
// Only store 'vehicle_invoice_no' if 'new_policy' is NOT in path
|
||||
if (in_array('4_wheeler', $path)) {
|
||||
$vehicleDetails['vehicle_fuelType'] = $this->fuelType;
|
||||
$vehicleDetails['vehicle_seater'] = $this->seater;
|
||||
$vehicleDetails['vehicle_chassisNo'] = $this->chassisNumber;
|
||||
}
|
||||
|
||||
// Save vehicle details
|
||||
$this->bot->userStorage()->save(['vehicle_details' => $vehicleDetails]);
|
||||
$alldata = $this->bot->userStorage()->all();
|
||||
$vehicle_details = $this->bot->userStorage()->get('vehicle_details');
|
||||
// Log stored data for debugging
|
||||
// log_message('error', 'All Details: ' . json_encode($alldata));
|
||||
|
||||
// log_message('error', 'Updated Vehicle Details: ' . json_encode($vehicle_details));
|
||||
|
||||
$this->say('Vehicle Information Saved!');
|
||||
|
||||
$vehicleInfo = "🚗 **Vehicle Details:**<br>";
|
||||
$vehicleInfo .= "🔹 Name: " . $vehicle_details['vehicle_name'] . "<br>";
|
||||
$vehicleInfo .= "🔹 Year of Manufacture: " . $vehicle_details['vehicle_yom'] . "<br>";
|
||||
$vehicleInfo .= "🔹 Engine No: " . $vehicle_details['vehicle_engine_no'] . "<br>";
|
||||
|
||||
if (isset($vehicle_details['vehicle_fuelType'])) {
|
||||
$vehicleInfo .= "🔹 Fuel Type: " . $vehicle_details['vehicle_fuelType'] . "<br>";
|
||||
$vehicleInfo .= "🔹 Seater: " . $vehicle_details['vehicle_seater'] . "<br>";
|
||||
$vehicleInfo .= "🔹 Chassis Number: " . $vehicle_details['vehicle_chassisNo'] . "<br>";
|
||||
}
|
||||
$this->complied_information = $vehicleInfo;
|
||||
// Send the message with vehicle details
|
||||
$this->say($vehicleInfo);
|
||||
$this->confirmVehicleDate($vehicleInfo);
|
||||
|
||||
|
||||
// $this->bot->startConversation(new selectInsurerConversations());
|
||||
}
|
||||
|
||||
protected function confirmVehicleDate($vehicle_info){
|
||||
$question = Question::create("Confim your Details:")
|
||||
->addButtons([
|
||||
Button::create("✅ Confirm")->value("confirm"),
|
||||
Button::create("🔁 Start Over")->value("restart"),
|
||||
Button::create("❌ Cancel")->value('cancel')
|
||||
]);
|
||||
$this->bot->ask($question, function ($answer) use($vehicle_info) {
|
||||
$path = $this->bot->userStorage()->get('path');
|
||||
array_push($path,
|
||||
$answer->getValue()
|
||||
);
|
||||
$this->bot->userStorage()->save([
|
||||
'path' => $path
|
||||
]);
|
||||
switch ($answer->getValue()) {
|
||||
case "confirm":
|
||||
// $cityName = $response->getText();
|
||||
$sent_staus = ChatbotHelper::sendQuotationRequestMail($vehicle_info);
|
||||
if ($sent_staus){
|
||||
$this->say('Thanks.Our Executive will contact you with the quotation soon.');
|
||||
$this->bot->startConversation(new doYouWantToContinueConversation());
|
||||
}else{
|
||||
$this->say("There was a problem while requesting for a quotation please try again later.");
|
||||
}
|
||||
break;
|
||||
case "restart":
|
||||
$this->bot->startConversation(new vehicleFormConversation());
|
||||
break;
|
||||
case "cancel":
|
||||
$this->say("Redirecting you to main menu...");
|
||||
$this->bot->startConversation(new MainMenuConversation());
|
||||
break;
|
||||
default:
|
||||
$this->say("Invalid selection. Please choose an option.");
|
||||
$this->showHospitalMenu();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
163
app/Controllers/ChatbotControllerNew.php
Normal file
163
app/Controllers/ChatbotControllerNew.php
Normal file
@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Controllers\Chatbot\LoginToContiueConversation;
|
||||
use BotMan\BotMan\BotMan;
|
||||
use BotMan\BotMan\BotManFactory;
|
||||
use BotMan\BotMan\Drivers\DriverManager;
|
||||
use BotMan\BotMan\Cache\SymfonyCache;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use BotMan\Drivers\Web\WebDriver;
|
||||
|
||||
use App\Controllers\Chatbot\MainMenuConversation;
|
||||
use App\Controllers\Chatbot\TypingMiddleware;
|
||||
|
||||
use App\Helpers\ChatbotHelper;
|
||||
|
||||
class ChatbotControllerNew extends BaseController
|
||||
{
|
||||
protected $myLogger;
|
||||
protected $session;
|
||||
protected $botman;
|
||||
protected $isMemberLoggedIn;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
set_session_context('CHATBOT CON');
|
||||
|
||||
$this->myLogger = \Config\Services::mylogger();
|
||||
$this->session = \Config\Services::session();
|
||||
|
||||
$sessionID = session_id();
|
||||
$this->myLogger->logme('error', 'COOKIE - ' . $sessionID);
|
||||
|
||||
// $this->myLogger->logme('error', 'CALLED');
|
||||
|
||||
// Load BotMan driver
|
||||
DriverManager::loadDriver(WebDriver::class);
|
||||
|
||||
// Setup BotMan with cache
|
||||
$cache = new SymfonyCache(new FilesystemAdapter());
|
||||
$this->botman = BotManFactory::create([], $cache);
|
||||
// Create a new instance of BotMan
|
||||
// $botman = resolve('botman');
|
||||
|
||||
// Attach the TypingMiddleware
|
||||
// $this->botman->middleware->received(new TypingMiddleware());
|
||||
|
||||
|
||||
// Retrieve user ID from session or request
|
||||
$extras = request()->getGet('conf');
|
||||
$extras = json_decode($extras);
|
||||
$router = service('router');
|
||||
$method = $router->methodName();
|
||||
// $this->myLogger->logme('error', $method);
|
||||
|
||||
if (isset($extras) && $method == 'widget') {
|
||||
$request = \Config\Services::request();
|
||||
$cookieHeader = $request->getHeaderLine('Cookie');
|
||||
|
||||
$extras = $extras->parameters;
|
||||
$this->session->set('CHATBOT_EMP_ID', $extras->employee_id);
|
||||
$this->session->set('CHATBOT_ORIGIN', $extras->origin);
|
||||
$this->session->set('CHATBOT_EMP_CODE', $extras->emp_code);
|
||||
$this->session->set('CHATBOT_CLIENT_ID', $extras->client_id);
|
||||
$this->session->set('CHATBOT_CLIENT_BRANCH_ID', $extras->client_branch_id);
|
||||
if($this->session->get('CHATBOT_RANDOM_USER_ID') == '')
|
||||
{
|
||||
$this->session->set('CHATBOT_RANDOM_USER_ID', '-');
|
||||
}
|
||||
}
|
||||
$log_message = [
|
||||
'CHATBOT_EMP_ID' => $this->session->get('CHATBOT_EMP_ID'),
|
||||
'CHATBOT_ORIGIN' => $this->session->get('CHATBOT_ORIGIN'),
|
||||
'CHATBOT_EMP_CODE' => $this->session->get('CHATBOT_EMP_CODE'),
|
||||
'CHATBOT_CLIENT_ID' => $this->session->get('CHATBOT_CLIENT_ID'),
|
||||
'CHATBOT_CLIENT_BRANCH_ID' => $this->session->get('CHATBOT_CLIENT_BRANCH_ID'),
|
||||
'CHATBOT_RANDOM_USER_ID' => $this->session->get('CHATBOT_RANDOM_USER_ID')
|
||||
];
|
||||
|
||||
$log_message = implode(' | ', array_map(
|
||||
fn($key, $value) => "{$key}: {$value}",
|
||||
array_keys($log_message),
|
||||
$log_message
|
||||
));
|
||||
|
||||
$this->myLogger->logme('error', $log_message);
|
||||
|
||||
// print_rr(ChatbotHelper::getListOfPolicies());die();
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
|
||||
if($this->session->get('CHATBOT_RANDOM_USER_ID') == '-' || $this->session->get('CHATBOT_RANDOM_USER_ID') == '')
|
||||
{
|
||||
|
||||
$user = $this->botman->getUser();
|
||||
$id = $user->getId();
|
||||
$this->myLogger->logme('error', ('TEST' . $id));
|
||||
$this->session->set('CHATBOT_RANDOM_USER_ID', $id);
|
||||
}
|
||||
|
||||
$this->isMemberLoggedIn = false;
|
||||
$chat_session_info = get_chatbot_session_info();
|
||||
if (!empty($chat_session_info['emp_id']) && !empty($chat_session_info['emp_code'] )){
|
||||
$this->isMemberLoggedIn = true;
|
||||
}
|
||||
|
||||
$this->botman->hears('.*', function ($bot) {
|
||||
log_message("error","Inside Bot Type Function");
|
||||
|
||||
$bot->types(); // Typing indicator for the first message
|
||||
// sleep(0.5); // Delay
|
||||
});
|
||||
// Start the Main Menu when user says "hi" or "start"
|
||||
$this->botman->hears('start|hi|hello|help|help me', function (BotMan $bot) {
|
||||
if ($this->isMemberLoggedIn){
|
||||
// $bot->reply('Hi how can i assist?');
|
||||
$bot->startConversation(new MainMenuConversation());
|
||||
}else {
|
||||
$bot->startConversation(new LoginToContiueConversation());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
// $this->botman->hears('main_menu', function (BotMan $bot) {
|
||||
// BotService::sendMainOptions($bot);
|
||||
// });
|
||||
|
||||
// // Register Option Handlers
|
||||
// $this->registerHandlers();
|
||||
|
||||
// Listen for Messages
|
||||
$this->botman->listen();
|
||||
}
|
||||
|
||||
// private function registerHandlers()
|
||||
// {
|
||||
// // Handling Policy and Claims
|
||||
// $this->botman->hears('group:policy:{option}', [\App\Controllers\Chatbot\PolicyHandler::class, 'handle']);
|
||||
// $this->botman->hears('group:claim:{option}', [\App\Libraries\Chatbot\ClaimHandler::class, 'handle']);
|
||||
|
||||
|
||||
// }
|
||||
|
||||
public function chatbotest()
|
||||
{
|
||||
$this->loadLayout('chatbottest.php');
|
||||
}
|
||||
|
||||
public function chatbot()
|
||||
{
|
||||
echo view('chatbot.php');
|
||||
}
|
||||
|
||||
public function widget()
|
||||
{
|
||||
echo view('widget.php');
|
||||
}
|
||||
}
|
||||
1016
app/Controllers/ClaimReportDashboardController.php
Normal file
1016
app/Controllers/ClaimReportDashboardController.php
Normal file
File diff suppressed because it is too large
Load Diff
176
app/Controllers/ClaimsCollectionV2DashboardController.php
Normal file
176
app/Controllers/ClaimsCollectionV2DashboardController.php
Normal file
@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Models\ClaimsCollectionV2DashboardModel;
|
||||
use App\Models\ClaimDumpFileModel;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
|
||||
/**
|
||||
* Claims Collection V2 KPI API (Metabase SQL as model methods).
|
||||
*/
|
||||
class ClaimsCollectionV2DashboardController extends BaseController
|
||||
{
|
||||
use ResponseTrait;
|
||||
|
||||
/**
|
||||
* Resolve policy id from request; optional $fallback (e.g. route default for debug only).
|
||||
*/
|
||||
protected function resolvePolicyId(?int $fallback = null): int
|
||||
{
|
||||
$id = (int) (
|
||||
$this->request->getGet('client_policy')
|
||||
?? $this->request->getGet('client_policy_id')
|
||||
?? $this->request->getPost('client_policy')
|
||||
?? $this->request->getPost('client_policy_id')
|
||||
?? 0
|
||||
);
|
||||
|
||||
if ($id > 0) {
|
||||
return $id;
|
||||
}
|
||||
|
||||
if ($fallback !== null && $fallback > 0) {
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve KPI by Metabase numeric id or method slug.
|
||||
*/
|
||||
protected function resolveKpiMethod(string $kpiKey): ?string
|
||||
{
|
||||
$kpiKey = trim($kpiKey);
|
||||
if ($kpiKey === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (in_array($kpiKey, ClaimsCollectionV2DashboardModel::KPI_MAP, true)) {
|
||||
return $kpiKey;
|
||||
}
|
||||
|
||||
if (ctype_digit($kpiKey)) {
|
||||
$id = (int) $kpiKey;
|
||||
return ClaimsCollectionV2DashboardModel::KPI_MAP[$id] ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON: single KPI by slug (e.g. incurred_ratio) or Metabase id (e.g. 207).
|
||||
* Requires client_policy or client_policy_id.
|
||||
*/
|
||||
public function kpi(string $kpiMethod = '')
|
||||
{
|
||||
$policyId = $this->resolvePolicyId();
|
||||
|
||||
if ($policyId <= 0) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'client_policy or client_policy_id is required.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$kpiMethod = $this->resolveKpiMethod($kpiMethod);
|
||||
if ($kpiMethod === null) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'Unknown KPI. Pass Metabase id or method slug.',
|
||||
'allowed' => ClaimsCollectionV2DashboardModel::KPI_MAP,
|
||||
], 404);
|
||||
}
|
||||
|
||||
$model = new ClaimsCollectionV2DashboardModel();
|
||||
$metabaseId = array_search($kpiMethod, ClaimsCollectionV2DashboardModel::KPI_MAP, true);
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'policy_id' => $policyId,
|
||||
'kpi_id' => $metabaseId !== false ? (int) $metabaseId : null,
|
||||
'kpi' => $kpiMethod,
|
||||
'label' => ClaimsCollectionV2DashboardModel::KPI_LABELS[$kpiMethod] ?? $kpiMethod,
|
||||
'rows' => $model->getKpi($kpiMethod, $policyId),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON: all KPIs. Requires client_policy or client_policy_id.
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
$policyId = $this->resolvePolicyId();
|
||||
|
||||
if ($policyId <= 0) {
|
||||
return $this->respond([
|
||||
'status' => false,
|
||||
'message' => 'client_policy or client_policy_id is required.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$generatedAt = (new ClaimDumpFileModel())->getGeneratedAtForPolicy($policyId);
|
||||
// if ($generatedAt === null) {
|
||||
// return $this->respond([
|
||||
// 'status' => false,
|
||||
// 'message' => 'claim_dump_date is mandatory. No claim dump date found for this policy.',
|
||||
// ], 422);
|
||||
// }
|
||||
|
||||
$model = new ClaimsCollectionV2DashboardModel();
|
||||
|
||||
return $this->respond([
|
||||
'status' => true,
|
||||
'policy_id' => $policyId,
|
||||
'generated_at' => $generatedAt,
|
||||
'data' => $model->getAllKpis($policyId),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin preview UI (authMVC only) — KPI grid for manual testing.
|
||||
* Default policy id only in method signature; override via query or /preview/{id}.
|
||||
*/
|
||||
public function preview(int $policyId = 4687)
|
||||
{
|
||||
$policyId = $this->resolvePolicyId($policyId);
|
||||
$path = $this->request->getUri()->getPath();
|
||||
$isJwt = stripos($path, 'employeeRest') !== false;
|
||||
$prefix = $isJwt ? 'employeeRest/claims-collection-v2' : 'util/claims-collection-v2';
|
||||
|
||||
return view('claims_collection_v2_dashboard', [
|
||||
'policy_id' => $policyId,
|
||||
'kpi_map' => ClaimsCollectionV2DashboardModel::KPI_MAP,
|
||||
'kpi_labels' => ClaimsCollectionV2DashboardModel::KPI_LABELS,
|
||||
'api_all_url' => base_url($prefix . '/all'),
|
||||
'api_kpi_url' => base_url($prefix . '/kpi'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin check only: raw JSON on screen (no dashboard UI).
|
||||
* Default policy id only here; override via ?client_policy= or /debug/{id}.
|
||||
*/
|
||||
public function debug(int $policyId = 4687)
|
||||
{
|
||||
$policyId = $this->resolvePolicyId($policyId);
|
||||
|
||||
if ($policyId <= 0) {
|
||||
return $this->response
|
||||
->setStatusCode(422)
|
||||
->setBody('client_policy or client_policy_id is required.');
|
||||
}
|
||||
|
||||
$model = new ClaimsCollectionV2DashboardModel();
|
||||
$body = json_encode([
|
||||
'status' => true,
|
||||
'policy_id' => $policyId,
|
||||
'data' => $model->getAllKpis($policyId),
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
return $this->response
|
||||
->setHeader('Content-Type', 'application/json; charset=UTF-8')
|
||||
->setBody($body);
|
||||
}
|
||||
}
|
||||
110
app/Controllers/ClaimsUploadController.php
Normal file
110
app/Controllers/ClaimsUploadController.php
Normal file
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
|
||||
use App\Controllers\BaseController;
|
||||
|
||||
class ClaimsUploadController extends BaseController
|
||||
{
|
||||
use ResponseTrait;
|
||||
protected $db;
|
||||
public function __construct()
|
||||
{
|
||||
$this->db = \Config\Database::connect();
|
||||
}
|
||||
|
||||
public function uploadDump()
|
||||
{
|
||||
$file = $this->request->getFile('file');
|
||||
|
||||
if (!$file || !$file->isValid()) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'message' => 'Invalid file'
|
||||
], 400);
|
||||
}
|
||||
|
||||
if (!validate_upload_extension($file, UPLOAD_EXT_EXCEL)) {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'message' => 'Only Excel files (xls, xlsx, ods, csv) are allowed.'
|
||||
], 400);
|
||||
}
|
||||
|
||||
$uploadFile = storage_file_Upload($file, WRITEPATH . 'uploads/claims_dump/', UPLOAD_EXT_EXCEL);
|
||||
if ($uploadFile === '') {
|
||||
return $this->respond([
|
||||
'status' => 'failed',
|
||||
'message' => 'File upload failed'
|
||||
], 500);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'client_id' => $this->request->getPost('client_id'),
|
||||
'tpa_id' => $this->request->getPost('tpa_id'),
|
||||
'client_policy_id' => $this->request->getPost('client_policy_id'),
|
||||
'from_date' => $this->request->getPost('from_date'),
|
||||
'to_date' => $this->request->getPost('to_date'),
|
||||
'upload_file' => $uploadFile,
|
||||
// 'uploaded_by' => user_id()
|
||||
];
|
||||
|
||||
$this->db->table('claims_dump_uploads')->insert($data);
|
||||
$insertId = $this->db->insertID();
|
||||
|
||||
return $this->respond([
|
||||
'status' => 'success',
|
||||
'message' => 'Claims dump uploaded',
|
||||
'id' => $insertId,
|
||||
'upload_file' => $uploadFile,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a claims_dump upload by claims_dump_uploads.id (local or S3).
|
||||
*/
|
||||
public function downloadDump($id = null)
|
||||
{
|
||||
$id = (int) ($id ?? $this->request->getGet('id'));
|
||||
if ($id <= 0) {
|
||||
$data['message'] = 'Invalid file id';
|
||||
return view('errors/404', $data);
|
||||
}
|
||||
|
||||
$record = $this->db->table('claims_dump_uploads')->where('id', $id)->get()->getRowArray();
|
||||
if (empty($record) || empty($record['upload_file'])) {
|
||||
$data['message'] = 'File record not found';
|
||||
return view('errors/404', $data);
|
||||
}
|
||||
|
||||
$fileName = basename((string) $record['upload_file']);
|
||||
$storage = \Config\Services::getFileStorageService();
|
||||
$result = $storage->download(WRITEPATH . 'uploads/claims_dump', $fileName);
|
||||
|
||||
if (! ($result['success'] ?? false)) {
|
||||
$data['message'] = 'The Physical File Not Found';
|
||||
return view('errors/404', $data);
|
||||
}
|
||||
|
||||
$downloadAs = storage_upload_display_name($fileName);
|
||||
if (! empty($result['path']) && is_file($result['path'])) {
|
||||
return $this->response->download($result['path'], null)->setFileName($downloadAs);
|
||||
}
|
||||
if (! empty($result['content'])) {
|
||||
return $this->response->download($downloadAs, $result['content'])->setFileName($downloadAs);
|
||||
}
|
||||
|
||||
$data['message'] = 'The Physical File Not Found';
|
||||
return view('errors/404', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve claims_dump file to a local path for any processors (local first, else S3).
|
||||
*/
|
||||
public function resolveDumpLocalPath(string $fileName): ?string
|
||||
{
|
||||
return storage_ensure_local_file(WRITEPATH . 'uploads/claims_dump', $fileName);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user