Merge branch 'dev' of bitbucket.org:jubilian/nhance into dev
This commit is contained in:
commit
eaca6513ee
@ -480,7 +480,6 @@ $routes->group("rfq", ["filter" => "authMVC"], function ($routes) {
|
||||
$routes->get("driveListFiles", "GoogleDriveController::listFiles");
|
||||
$routes->post('dmsSearch', 'PolicyTransactionController::dmsSearch', ['filter' => 'authMVC']);
|
||||
$routes->get('dmsSearch', 'PolicyTransactionController::dmsSearch', ['filter' => 'authMVC']);
|
||||
$routes->get('payouts', 'PolicyTransactionController::payouts', ['filter' => 'authMVC']);
|
||||
$routes->get('downloadGdriveFile', 'GoogleDriveController::downloadGdriveFile', ['filter' => 'authMVC']);
|
||||
$routes->get('cli/sendZeptoMail', 'MasterController::testZeptoSMTP');
|
||||
|
||||
@ -774,6 +773,10 @@ $routes->group('payout', function($routes) {
|
||||
$routes->post('fetchUtrDetails',"PayoutController::fetchUtrDetails");
|
||||
$routes->post('saveUtrDetails',"PayoutController::saveUtrDetails");
|
||||
$routes->post('removeUtrDetails',"PayoutController::removeUtrDetails");
|
||||
// invoice policy mapping
|
||||
$routes->get('invoices', 'PayoutController::invoices');
|
||||
$routes->post('invoices/save', 'PayoutController::saveInvoice');
|
||||
$routes->get('invoices/history', 'PayoutController::auditHistory');
|
||||
});
|
||||
|
||||
//PARTNER COMMISSION
|
||||
@ -787,5 +790,6 @@ $routes->group('commission', function($routes) {
|
||||
$routes->get('rules/list/(:any)',"RuleImportController::ruleList/$1");
|
||||
$routes->post('rules/save/',"RuleImportController::saveRule");
|
||||
$routes->post('rules/remove/',"RuleImportController::removeRule");
|
||||
$routes->get('checkRuleUsage',"RuleImportController::checkRuleUsage");
|
||||
});
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ class InsuranceCommissionController extends AdminController
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
set_session_context('Client');
|
||||
set_session_context('InsuranceCommissionController');
|
||||
$this->myLogger = \Config\Services::mylogger();
|
||||
// Load rules file if present in writable config path
|
||||
// $rulesPath = WRITEPATH . 'config/insurance_rules.json';
|
||||
@ -122,7 +122,10 @@ class InsuranceCommissionController extends AdminController
|
||||
// Normalise department keys to lowercase for consistent lookups
|
||||
$this->rules = [];
|
||||
foreach ($parsed as $dept => $rules) {
|
||||
$this->rules[strtolower($dept)] = $rules;
|
||||
if($rules['is_deleted'] === false)
|
||||
{
|
||||
$this->rules[strtolower($dept)] = $rules;
|
||||
}
|
||||
}
|
||||
|
||||
// print_r($this->rules);die();
|
||||
|
||||
@ -258,7 +258,7 @@ class NotificationController extends AdminController
|
||||
|
||||
$template_id = $this->request->getPost('template_id');
|
||||
|
||||
$test_mail = $this->request->getPost('test_mail');
|
||||
// $test_mail = $this->request->getPost('test_mail');
|
||||
|
||||
$test_mail_list = $this->request->getPost('test_mail_list');
|
||||
|
||||
@ -268,27 +268,67 @@ class NotificationController extends AdminController
|
||||
|
||||
if(!empty($notification_data)){
|
||||
|
||||
$params = [
|
||||
'client_data' => $client_data,
|
||||
'notification_data' => $notification_data,
|
||||
'test_mail' => $test_mail,
|
||||
'test_mail_list' => $test_mail_list
|
||||
];
|
||||
// First Index as test mail all are testmaillist
|
||||
// $params = [
|
||||
// 'client_data' => $client_data,
|
||||
// 'notification_data' => $notification_data,
|
||||
// 'test_mail' => $test_mail,
|
||||
// 'test_mail_list' => $test_mail_list
|
||||
// ];
|
||||
// $testMailData = sendMailNotification::sendMailNotificationForTesting($notification_data['template_name'], $params);
|
||||
// if (!empty($testMailData)) {
|
||||
// $mail_send_return1 = MailHelper::send_email($testMailData);
|
||||
// $this->myLogger->logme("info", $mail_send_return1);
|
||||
// $this->myLogger->logme("info", $mail_send_return1);
|
||||
// return $this->respond(['status' => true,'code' => 200, 'respond' => json_decode($mail_send_return1)]);
|
||||
// }else{
|
||||
// return $this->respond(['status' => false,'code' => 200, 'message' => 'Test Mail Data Does Not Exist']);
|
||||
// }
|
||||
|
||||
$testMailData = sendMailNotification::sendMailNotificationForTesting($notification_data['template_name'], $params);
|
||||
// print_r($testMailData); die;
|
||||
if (!empty($testMailData)) {
|
||||
|
||||
$mail_send_return1 = MailHelper::send_email($testMailData);
|
||||
$this->myLogger->logme("info", $mail_send_return1);
|
||||
$this->myLogger->logme("info", $mail_send_return1);
|
||||
$mailArray = explode(',', $test_mail_list);
|
||||
$successMails = [];
|
||||
$failedMails = [];
|
||||
|
||||
return $this->respond(['status' => true,'code' => 200, 'respond' => json_decode($mail_send_return1)]);
|
||||
foreach ($mailArray as $singleMail) {
|
||||
$singleMail = trim($singleMail);
|
||||
|
||||
}else{
|
||||
$params = [
|
||||
'client_data' => $client_data,
|
||||
'notification_data' => $notification_data,
|
||||
'test_mail' => $singleMail,
|
||||
'test_mail_list' => "" // optional
|
||||
];
|
||||
|
||||
$testMailData = sendMailNotification::sendMailNotificationForTesting($notification_data['template_name'], $params);
|
||||
|
||||
if (!empty($testMailData)) {
|
||||
$mail_send_return1 = MailHelper::send_email($testMailData);
|
||||
|
||||
if ($mail_send_return1) {
|
||||
$successMails[] = $singleMail;
|
||||
} else {
|
||||
$failedMails[] = $singleMail;
|
||||
}
|
||||
|
||||
$this->myLogger->logme("info", "Mail attempt to {$singleMail}: " . $mail_send_return1);
|
||||
} else {
|
||||
$failedMails[] = $singleMail;
|
||||
$this->myLogger->logme("info", "Test Mail Data Does Not Exist for {$singleMail}");
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare final response
|
||||
if (!empty($successMails)) {
|
||||
$responseMessage = "Count " . count($successMails) . " mail(s) sent successfully: \n" . implode("\n", $successMails);
|
||||
if (!empty($failedMails)) {
|
||||
$responseMessage .= "\nCount " . count($failedMails) . " mail(s) failed: \n" . implode("\n", $failedMails);
|
||||
}
|
||||
return $this->respond(['status' => true,'code' => 200,'message' => $responseMessage]);
|
||||
} else {
|
||||
return $this->respond(['status' => false,'code' => 200,'message' => 'All mails failed to send: ' . implode(", ", $failedMails)]);
|
||||
}
|
||||
|
||||
return $this->respond(['status' => false,'code' => 200, 'message' => 'Test Mail Data Does Not Exist']);
|
||||
}
|
||||
}else{
|
||||
|
||||
return $this->respond(['status' => false,'code' => 200, 'message' => 'Notification Template not enabled']);
|
||||
|
||||
@ -9,6 +9,7 @@ use App\Models\InvoiceItemModel;
|
||||
use App\Models\InvoiceModel;
|
||||
use App\Models\InvoiceUtrModel;
|
||||
use App\Models\PolicyTransactionModel;
|
||||
use App\Models\AuditHistoryModel;
|
||||
|
||||
class PayoutController extends BaseController
|
||||
{
|
||||
@ -20,21 +21,23 @@ class PayoutController extends BaseController
|
||||
protected $policyTransactionModel;
|
||||
protected $payout_status;
|
||||
|
||||
protected $auditHistory;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
set_session_context('PayoutController');
|
||||
$this->myLogger = \Config\Services::mylogger();
|
||||
|
||||
$this->payout_status = [
|
||||
1 => "Draft",
|
||||
2 => "Pending",
|
||||
3 => "Complete",
|
||||
1 => "Pending",
|
||||
2 => "Complete",
|
||||
];
|
||||
|
||||
$this->invoiceItemModel = new InvoiceItemModel();
|
||||
$this->invoiceModel = new InvoiceModel();
|
||||
$this->invoiceUtrModel = new InvoiceUtrModel();
|
||||
$this->policyTransactionModel = new PolicyTransactionModel();
|
||||
$this->auditHistory = new AuditHistoryModel();
|
||||
}
|
||||
|
||||
public function payoutList()
|
||||
@ -189,4 +192,263 @@ class PayoutController extends BaseController
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************************/
|
||||
//... Payout-invoice Mapping Commission's amount and Adjustment's Amount - Data Display
|
||||
public function invoices()
|
||||
{
|
||||
|
||||
$type = $this->request->getGet('type');
|
||||
|
||||
$title = $type === 'add' ? 'Add Invoices'
|
||||
: ($type === 'edit' ? 'Edit Invoices'
|
||||
: ($type === 'adjustment' ? 'Adjustment Invoices'
|
||||
: 'Invoices'));
|
||||
|
||||
$data['tab_name'] = $title;
|
||||
$data['page_name'] = $title;
|
||||
|
||||
$id = $this->request->getGet('id');
|
||||
|
||||
|
||||
$data['agents'] = $this->invoiceModel->agentList(); // common both add and edit
|
||||
// $data['checked_policy_numbers'] = [];
|
||||
// $data['invoice'] = [];
|
||||
// $data['extra_payouts'] = [];
|
||||
|
||||
//... Now Seperated add => 'policy_transaction_payouts1'
|
||||
//... Now Seperated edit and adjustment => 'policy_transaction_payouts' old file
|
||||
//... Reason : Due Datatable issues Export button Searching like that so seperated
|
||||
if ($type === 'add') {
|
||||
$invoiceNo = $this->generateInvoiceNumber();
|
||||
$data['payouts'] = $this->invoiceModel->payoutList(1);
|
||||
$data['invoice_number'] = $invoiceNo;
|
||||
return $this->loadLayout('invoice_policy_mapping_add', $data);
|
||||
}
|
||||
|
||||
if (($type === 'edit' || $type === 'adjustment') && !empty($id)) {
|
||||
|
||||
$invoice = $this->invoiceModel->where('id', $id)->first();
|
||||
$data['freeze_edit'] = $this->auditHistory->where('table_name', 'partner_invoice')->where('pk', $id)->countAllResults();
|
||||
|
||||
$agentId = $invoice['agent_id'] ?? null;
|
||||
|
||||
$data['payouts'] = $this->invoiceModel->payoutList(2 ,$agentId,$id);
|
||||
$data['extra_payouts'] = $agentId ? $this->invoiceModel->payoutList(3, $agentId) : [];
|
||||
|
||||
$invoice_items = $this->invoiceItemModel->where('invoice_id', $id)->findAll();
|
||||
|
||||
if (!$invoice) { return redirect()->to('payout/invoices')->with('error', 'Invoice not found'); }
|
||||
|
||||
$data['invoice'] = $invoice;
|
||||
$data['invoice_items'] = $invoice_items;
|
||||
// Initialize array for policy numbers
|
||||
$data['checked_policy_numbers'] = array_column(array_filter($invoice_items, fn($ii) => isset($ii['is_active']) && $ii['is_active'] == 1),'policy_no');
|
||||
|
||||
$data['invoice_number']= $invoice['invoice_no'];
|
||||
$data['type'] = $type;
|
||||
|
||||
return $this->loadLayout('invoice_policy_mapping', $data);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//... Payout-invoice Mapping - Save/update/soft Delete/Hard Delete Data
|
||||
public function saveInvoice()
|
||||
{
|
||||
$json = $this->request->getJSON(true);
|
||||
|
||||
if (!$json) {
|
||||
return $this->response->setJSON(['error' => 'Invalid JSON','message' => 'Invalid JSON received.'])->setStatusCode(400);
|
||||
}
|
||||
|
||||
$id = $json['invoice_id'] ?? null;
|
||||
try {
|
||||
|
||||
//... ADD Part
|
||||
if (empty($id)) {
|
||||
|
||||
$invoiceData = [
|
||||
'invoice_no' => $json['invoice_no'],
|
||||
'agent_id' => $json['agent_id'],
|
||||
'invoice_date' => $json['invoice_date'],
|
||||
'invoice_amount' => $json['invoice_amount'],
|
||||
'payout_status' => 1
|
||||
];
|
||||
|
||||
$invoiceId = $this->invoiceModel->insert($invoiceData);
|
||||
|
||||
foreach ($json['policies'] as $p) {
|
||||
$this->invoiceItemModel->insert([
|
||||
'invoice_id' => $invoiceId,
|
||||
'policy_id' => $p['policy_id'],
|
||||
'policy_no' => $p['policy_no'],
|
||||
'commission_amount' => $p['commission_amount'],
|
||||
'is_active' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
$message = "Invoice created successfully.\nInvoice No: " . $json['invoice_no'];
|
||||
|
||||
}
|
||||
|
||||
// ... EDIT part
|
||||
if (!empty($id)) {
|
||||
|
||||
$invoiceId = $id;
|
||||
|
||||
$invoiceData = [
|
||||
'invoice_no' => $json['invoice_no'],
|
||||
'agent_id' => $json['agent_id'],
|
||||
'invoice_date' => $json['invoice_date'],
|
||||
'invoice_amount' => $json['invoice_amount'],
|
||||
];
|
||||
|
||||
$this->invoiceModel->update($invoiceId, $invoiceData);
|
||||
|
||||
//... Fetch existing invoice item rows
|
||||
$existingItems = $this->invoiceItemModel
|
||||
->where('invoice_id', $invoiceId)
|
||||
->findAll();
|
||||
|
||||
//... Create map by policy_no
|
||||
$existingMap = [];
|
||||
foreach ($existingItems as $item) {
|
||||
$existingMap[$item['policy_no']] = $item;
|
||||
}
|
||||
|
||||
$newPolicyNos = [];
|
||||
|
||||
//... Loop new JSON policies
|
||||
foreach ($json['policies'] as $p) {
|
||||
|
||||
$newPolicyNos[] = $p['policy_no'];
|
||||
|
||||
if (isset($existingMap[$p['policy_no']])) {
|
||||
|
||||
//... Update existing item
|
||||
$this->invoiceItemModel
|
||||
->where('id', $existingMap[$p['policy_no']]['id'])
|
||||
->set([
|
||||
'commission_amount' => $p['commission_amount'],
|
||||
'is_active' => 1
|
||||
])
|
||||
->update();
|
||||
|
||||
} else {
|
||||
|
||||
//... Insert new item
|
||||
$this->invoiceItemModel->insert([
|
||||
'invoice_id' => $invoiceId,
|
||||
'policy_id' => $p['policy_id'],
|
||||
'policy_no' => $p['policy_no'],
|
||||
'commission_amount' => $p['commission_amount'],
|
||||
'is_active' => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
//... Delete items removed in JSON (hard delete)
|
||||
foreach ($existingItems as $old) {
|
||||
if (!in_array($old['policy_no'], $newPolicyNos)) {
|
||||
$this->invoiceItemModel
|
||||
->where('id', $old['id'])
|
||||
->delete();
|
||||
}
|
||||
}
|
||||
//... Delete items removed in JSON (soft delete REF : SVM )
|
||||
// foreach ($existingItems as $old) {
|
||||
// if (!in_array($old['policy_no'], $newPolicyNos)) {
|
||||
// $this->invoiceItemModel
|
||||
// ->where('id', $old['id'])
|
||||
// ->set(['is_active' => 0])
|
||||
// ->update();
|
||||
// }
|
||||
// }
|
||||
|
||||
$message = "Invoice updated successfully.";
|
||||
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => 'success',
|
||||
'message' => $message,
|
||||
'invoice_id' => $invoiceId
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => 'error',
|
||||
'message' => 'Unexpected error occurred: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//... Payout-invoice Mapping - Invoice number is auto-generated only for the Add mode.
|
||||
// Note: It will change each time the page is reloaded.. (REF:MGW)
|
||||
private function generateInvoiceNumber()
|
||||
{
|
||||
$year = date('Y');
|
||||
$month = date('m');
|
||||
|
||||
do {
|
||||
// random 3-digit number
|
||||
$random = str_pad(rand(1, 999), 3, '0', STR_PAD_LEFT);
|
||||
$invoiceNo = "INV{$year}{$month}{$random}";
|
||||
|
||||
// check main invoice table
|
||||
$existsMain = $this->invoiceModel
|
||||
->where('invoice_no', $invoiceNo)
|
||||
->first();
|
||||
|
||||
// check partner invoice table
|
||||
$existsPartner = $this->invoiceModel
|
||||
->where('invoice_no', $invoiceNo)
|
||||
->first();
|
||||
|
||||
} while ($existsMain || $existsPartner); // regenerate if duplicate found
|
||||
|
||||
return $invoiceNo;
|
||||
}
|
||||
|
||||
//... Payout-invoice Mapping Audit History Based on "Adjustment" value (REF: KV,SVM)
|
||||
public function auditHistory()
|
||||
{
|
||||
|
||||
|
||||
$iid = $this->request->getGet('id');
|
||||
|
||||
$details['invoice'] = $this->auditHistory
|
||||
->select('auditing_history.*,partner_invoice.invoice_no, user_profiles.first_name as created_name')
|
||||
->join('user_profiles', 'user_profiles.id = auditing_history.created_by', 'left')
|
||||
->join('partner_invoice', 'partner_invoice.id = auditing_history.pk', 'left')
|
||||
->where('auditing_history.table_name', 'partner_invoice') // ok
|
||||
->where('auditing_history.pk', $iid)
|
||||
->orderBy('auditing_history.created_at', 'desc')
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
$details['invoice_child'] = $this->auditHistory
|
||||
->select('auditing_history.*,partner_invoice.invoice_no, user_profiles.first_name as created_name')
|
||||
->join('user_profiles', 'user_profiles.id = auditing_history.created_by', 'left')
|
||||
->join('partner_invoice_items', 'partner_invoice_items.id = auditing_history.pk', 'left')
|
||||
->join('partner_invoice', 'partner_invoice.id = partner_invoice_items.invoice_id', 'left')
|
||||
->where('auditing_history.table_name', 'partner_invoice_items') // FIXED
|
||||
->where('partner_invoice.id', $iid)
|
||||
->orderBy('auditing_history.created_at', 'desc')
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
return $this->response->setJSON([
|
||||
'status' => 'success',
|
||||
'data' => $details
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2811,20 +2811,6 @@
|
||||
$this->loadLayout('dms_search', $data);
|
||||
}
|
||||
|
||||
public function payouts()
|
||||
{
|
||||
|
||||
$data['tab_name'] = 'Payouts';
|
||||
$data['page_name'] = 'Payouts';
|
||||
$data['payouts'] = [];
|
||||
$data['agents'] = [ 1 => "Agent 1", 2 => "Agent 2", 3 => "Agent 3", 4 => "Agent 4", 5 => "Agent 5", 6 => "Agent 6", 7 => "Agent 7", 8 => "Agent 8"];
|
||||
$data['brokers'] = [];
|
||||
|
||||
if ($this->request->is('post')) {}
|
||||
if ($this->request->is('get')) {}
|
||||
|
||||
$this->loadLayout('policy_transaction_payouts', $data);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
public function getCoShareStatementDetails($pt_id)
|
||||
|
||||
@ -4,6 +4,9 @@ namespace App\Controllers;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
use App\Models\CommissionFilesModel;
|
||||
use App\Models\InsurerModel;
|
||||
use App\Models\PartnerPolicyModel;
|
||||
|
||||
|
||||
|
||||
class RuleImportController extends AdminController
|
||||
{
|
||||
@ -14,6 +17,7 @@ class RuleImportController extends AdminController
|
||||
protected $departments;
|
||||
protected $departmentFields;
|
||||
protected $insurerModel;
|
||||
protected $partnerPolicyModel;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@ -21,7 +25,7 @@ class RuleImportController extends AdminController
|
||||
|
||||
$this->myLogger = \Config\Services::mylogger();
|
||||
$this->ruleImportService = \Config\Services::ruleImportService();
|
||||
|
||||
$this->partnerPolicyModel = new partnerPolicyModel();
|
||||
$this->commissionFilesModel = new CommissionFilesModel();
|
||||
$this->insurerModel = new InsurerModel();
|
||||
$this->departments = [
|
||||
@ -208,6 +212,8 @@ class RuleImportController extends AdminController
|
||||
'insurer_id' => (int)$insurerId,
|
||||
'department' => $department,
|
||||
'commission_month' => $commissionMonth,
|
||||
'created_by' => (int)$createdBy,
|
||||
|
||||
];
|
||||
|
||||
$this->myLogger->logme('info', 'RuleImportController::upload - Calling ruleImportService->processUpload', ['payload' => $payload]);
|
||||
@ -276,10 +282,22 @@ class RuleImportController extends AdminController
|
||||
// handle existing JSON file based on $override (bool)
|
||||
if (file_exists($jsonPath)) {
|
||||
if ($overwrite) {
|
||||
// delete existing file before writing (deterministic overwrite)
|
||||
if (!@unlink($jsonPath)) {
|
||||
$this->myLogger->logme('warning', 'RuleImportController::upload - Failed to delete existing JSON before overwrite', ['json_path' => $jsonPath]);
|
||||
// proceed to overwrite anyway by writing to the same path
|
||||
// rename existing file before overwrite
|
||||
if (file_exists($jsonPath)) {
|
||||
|
||||
$backupPath = $jsonPath . '.' . date('YmdHis') . '.bak';
|
||||
|
||||
if (!@rename($jsonPath, $backupPath)) {
|
||||
$this->myLogger->logme(
|
||||
'warning',
|
||||
'RuleImportController::upload - Failed to rename existing JSON before overwrite',
|
||||
[
|
||||
'json_path' => $jsonPath,
|
||||
'backup_path' => $backupPath
|
||||
]
|
||||
);
|
||||
// continue anyway; writing to same path will overwrite
|
||||
}
|
||||
}
|
||||
$finalJson = $jsonData;
|
||||
} else {
|
||||
@ -797,5 +815,15 @@ class RuleImportController extends AdminController
|
||||
return $this->respond(['status' => false, 'code' => 400, 'message' => $error_message], 200);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkRuleUsage()
|
||||
{
|
||||
$rule_id = $this->request->getGet('rule_id');
|
||||
|
||||
$count = $this->partnerPolicyModel->where('commission_applied_rule', $rule_id)
|
||||
->countAllResults();
|
||||
return $this->respond(['status' => true, 'code' => 200, 'count' => $count, 'message' => ""], 200);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -318,7 +318,7 @@ class MailHelper
|
||||
$attachments = isset($params['attachments']) ? $params['attachments'] : [];
|
||||
$common = isset($params['common']) ? $params['common'] : '';
|
||||
$bcc = isset($params['bcc']) ? $params['bcc'] : '';
|
||||
$cc = isset($params['cc']) ? $params['cc'] : '';
|
||||
$cc = (isset($params['cc']) && !empty($params['cc'])) ? $params['cc'] : '';
|
||||
|
||||
$from_address = isset($params['from_mail']) && !empty($params['from_mail']) ? $params['from_mail'] : getenv('email.fromEmail');
|
||||
// $from_address = "claims@nhanceindia.in";
|
||||
|
||||
@ -62,6 +62,7 @@ class RuleImportService
|
||||
|
||||
protected array $expectedColumns;
|
||||
protected array $columnValidators;
|
||||
protected array $incomingData;
|
||||
protected string $annotatedDir;
|
||||
protected string $department;
|
||||
protected string $uploadedCommissionFileID;
|
||||
@ -148,7 +149,7 @@ class RuleImportService
|
||||
{
|
||||
// dd($params);
|
||||
$startTime = microtime(true);
|
||||
|
||||
$this->incomingData = $params;
|
||||
try {
|
||||
$this->department = $params['department'];
|
||||
$this->uploadedCommissionFileID = $params['id'];
|
||||
@ -898,13 +899,29 @@ class RuleImportService
|
||||
*/
|
||||
protected function convertRowToRule(array $rowData): array
|
||||
{
|
||||
$temp_rule_id = $this->generateRuleId($rowData);
|
||||
//rule_e07f52d4366a2_32_oct2025_3_com
|
||||
|
||||
$conditions = $this->buildConditions($rowData);
|
||||
$temp_rule_id .= '_'.$this->incomingData['id'].'_'. strtolower(date('MY')).'_'.(count($conditions));
|
||||
$calculation = $this->buildCalculation($rowData);
|
||||
|
||||
|
||||
$temp_rule_id .= '_'.substr($calculation['type'], 0, 3);
|
||||
|
||||
$rule = [
|
||||
'id' => $this->generateRuleId($rowData),
|
||||
'id' => $temp_rule_id,
|
||||
'name' => $this->sanitize($rowData[self::COL_RULE_NAME] ?? ''),
|
||||
'department' => $this->department,
|
||||
'is_deleted' => false,
|
||||
'file_id' => $this->uploadedCommissionFileID,
|
||||
'conditions' => $this->buildConditions($rowData),
|
||||
'calculation' => $this->buildCalculation($rowData)
|
||||
'conditions' => $conditions,
|
||||
'calculation' => $calculation,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
'created_by' => $this->incomingData['created_by'],
|
||||
'updated_by' => $this->incomingData['created_by'],
|
||||
|
||||
];
|
||||
|
||||
return $rule;
|
||||
|
||||
@ -38,4 +38,29 @@ class InvoiceItemModel extends Model
|
||||
|
||||
protected $validationMessages = [];
|
||||
protected $skipValidation = false;
|
||||
|
||||
protected $beforeInsert = ["checkAndAddCreatedByValue"];
|
||||
protected $beforeUpdate = ["checkAndUpdateUpdatedByValue"];
|
||||
|
||||
protected function checkAndAddCreatedByValue(array $data)
|
||||
{
|
||||
// Check if 'updated_by' value is null or empty
|
||||
if (empty($data['data']['created_by'])) {
|
||||
// Set 'updated_by' value to the current session user ID
|
||||
$data['data']['created_by'] = get_session_userid();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function checkAndUpdateUpdatedByValue(array $data)
|
||||
{
|
||||
// Check if 'updated_by' value is null or empty
|
||||
if (empty($data['data']['updated_by'])) {
|
||||
// Set 'updated_by' value to the current session user ID
|
||||
$data['data']['updated_by'] = get_session_userid();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ class InvoiceModel extends Model
|
||||
protected $useTimestamps = false;
|
||||
protected $createdField = 'created_at';
|
||||
protected $updatedField = 'updated_at';
|
||||
|
||||
|
||||
// Validation (optional)
|
||||
protected $validationRules = [
|
||||
'invoice_no' => 'required|max_length[100]',
|
||||
@ -187,4 +187,57 @@ class InvoiceModel extends Model
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
public function payoutList($flag, $agentId = null, $invoiceId = null)
|
||||
{
|
||||
$builder = $this->db->table('policy_transaction pt')
|
||||
->select('
|
||||
pt.id,
|
||||
pt.policy_no AS policyNo,
|
||||
pt.agent_id AS agentId,
|
||||
pp.insured_name AS customer,
|
||||
pp.premium_amount AS premium,
|
||||
pp.commission_amount AS commission,
|
||||
pp.issued_date AS date_db,
|
||||
DATE_FORMAT(pp.issued_date, "%d/%m/%Y") AS date,
|
||||
pii.id AS invoiceItemId
|
||||
')
|
||||
->join('partner_policy pp','pt.policy_no = pp.policy_number AND pt.agent_id = pp.agent_id','left')
|
||||
->where('pt.is_active',1)
|
||||
->where('pt.agent_id IS NOT NULL', null, false);
|
||||
|
||||
if ($flag == 1) { // Add mode
|
||||
// EXCLUDE all policies that exist in partner_invoice_items
|
||||
$builder->join('partner_invoice_items pii','pii.policy_no = pt.policy_no','left');
|
||||
$builder->where("pt.policy_no NOT IN (SELECT policy_no FROM partner_invoice_items)", null, false);
|
||||
}
|
||||
|
||||
if ($flag == 2) { // Edit mode
|
||||
// Policies belonging to a specific invoice
|
||||
$builder->select('pii.commission_amount as paid_amount');
|
||||
$builder->join('partner_invoice_items pii','pii.policy_no = pt.policy_no','inner'); // must exist
|
||||
$builder->join('partner_invoice pi','pi.id = pii.invoice_id','inner'); // must exist
|
||||
$builder->where('pii.is_active',1);
|
||||
if ($invoiceId) {
|
||||
$builder->where('pi.id', $invoiceId); // only policies of this invoice
|
||||
}
|
||||
if ($agentId) {
|
||||
$builder->where('pt.agent_id', $agentId);
|
||||
}
|
||||
}
|
||||
|
||||
if ($flag == 3) { // Extra policies
|
||||
// Policies not assigned to any invoice
|
||||
$builder->select('pii.commission_amount as paid_amount');
|
||||
$builder->join('partner_invoice_items pii','pii.policy_no = pt.policy_no','left');
|
||||
$builder->where('pii.id IS NULL', null, false);
|
||||
if ($agentId) {
|
||||
$builder->where('pt.agent_id', $agentId);
|
||||
}
|
||||
}
|
||||
|
||||
return $builder->get()->getResultArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -36,9 +36,9 @@ table.dataTable thead th {
|
||||
<table data-custom-table-css="table" class="table mb-0 nowrap w-100 table-centered" cellspacing="0" id="tickets-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="font-weight-medium">Advertisement Image Name</th>
|
||||
<th class="font-weight-medium">Status</th>
|
||||
<th class="font-weight-medium">Action</th>
|
||||
<th class="font-weight-medium"><div class="column-header">Advertisement Image Name</div></th>
|
||||
<th class="font-weight-medium"><div class="column-header">Status</div></th>
|
||||
<th class="font-weight-medium"><div class="column-header">Action</div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@ -222,7 +222,8 @@ var table;
|
||||
|
||||
$(document).ready(function() {
|
||||
table = $('#tickets-table').DataTable({
|
||||
dom: "<'row'<'col-sm-1'f><'col-sm-11 text-right'B>>" + // Filter left, button right
|
||||
// dom: "<'row'<'col-sm-1'f><'col-sm-11 text-right'B>>" + // Filter left, button right
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
buttons: [
|
||||
|
||||
@ -17,6 +17,8 @@ table.dataTable thead th {
|
||||
max-width: 98% !important;
|
||||
}
|
||||
|
||||
.column-header {margin-right: 10px;}
|
||||
|
||||
.custom-dropdown-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
@ -70,15 +72,15 @@ table.dataTable thead th {
|
||||
<table data-custom-table-css="table" id="scroll-horizontal-datatable" class="table w-100 nowrap text-custom-black text-custom app-datatable">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="font-weight-medium">S.No </th>
|
||||
<th class="font-weight-medium">Client Name </th>
|
||||
<th class="font-weight-medium">Insurer Name </th>
|
||||
<th class="font-weight-medium">Insurer Branch Name </th>
|
||||
<th class="font-weight-medium">Opening Date </th>
|
||||
<th class="font-weight-medium">CD Account No </th>
|
||||
<th class="font-weight-medium">Opening Amount </th>
|
||||
<th class="font-weight-medium">Date/User </th>
|
||||
<th class="font-weight-medium">Action </th>
|
||||
<th class="font-weight-medium"> <div class="column-header">S.No </div> </th>
|
||||
<th class="font-weight-medium"> <div class="column-header">Client Name </div> </th>
|
||||
<th class="font-weight-medium"> <div class="column-header">Insurer Name </div> </th>
|
||||
<th class="font-weight-medium"> <div class="column-header">Insurer Branch Name </div> </th>
|
||||
<th class="font-weight-medium"> <div class="column-header">Opening Date </div> </th>
|
||||
<th class="font-weight-medium"> <div class="column-header">CD Account No </div> </th>
|
||||
<th class="font-weight-medium"> <div class="column-header">Opening Amount </div> </th>
|
||||
<th class="font-weight-medium"> <div class="column-header">Date/User </div> </th>
|
||||
<th class="font-weight-medium"> <div class="column-header">Action </div> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="app-table-body">
|
||||
@ -215,7 +217,7 @@ table.dataTable thead th {
|
||||
|
||||
$('#scroll-horizontal-datatable').DataTable({
|
||||
scrollX: true,
|
||||
dom: "<'row'<'col-sm-2'f><'col-sm-10 text-right'B>>" +
|
||||
dom: "<'row'<'col-sm-6'f><'col-sm-6 text-right'B>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
buttons: [
|
||||
|
||||
@ -103,9 +103,9 @@ $(document).ready(function() {
|
||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
|
||||
_INPUT_
|
||||
<i class="mdi mdi-magnify datatable-search-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666;"></i>
|
||||
style="position:absolute; right:11px; top:50%; transform:translateY(-53%); color:#666;"></i>
|
||||
<i class="mdi mdi-close-circle datatable-clear-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
|
||||
style="position:absolute; right:11px; top:50%; transform:translateY(-53%); color:#666; display:none;"></i>
|
||||
</div>`,
|
||||
searchPlaceholder: "Search",
|
||||
emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
||||
|
||||
@ -344,9 +344,9 @@ $(document).ready(function()
|
||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
|
||||
_INPUT_
|
||||
<i class="mdi mdi-magnify datatable-search-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666;"></i>
|
||||
style="position:absolute; right:11px; top:50%; transform:translateY(-53%); color:#666;"></i>
|
||||
<i class="mdi mdi-close-circle datatable-clear-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
|
||||
style="position:absolute; right:11px; top:50%; transform:translateY(-53%); color:#666; display:none;"></i>
|
||||
</div>`,
|
||||
searchPlaceholder: "Search",
|
||||
emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
||||
|
||||
@ -560,6 +560,20 @@ input:checked + .slider_blue::before {
|
||||
searching: true,
|
||||
autoWidth: false,
|
||||
responsive: true,
|
||||
language: {
|
||||
search: `
|
||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block; width:100%;">
|
||||
_INPUT_
|
||||
<i class="mdi mdi-magnify datatable-search-icon"
|
||||
style="position:absolute; right:122px !important; top:50%; transform:translateY(-50%); color:#666;"></i>
|
||||
|
||||
<i class="mdi mdi-close-circle datatable-clear-icon"
|
||||
style="position:absolute; right:122px !important; top:50%; transform:translateY(-50%); color:#666; display:none; cursor:pointer;"></i>
|
||||
</div>
|
||||
`,
|
||||
searchPlaceholder: "Search",
|
||||
emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@ -2543,6 +2557,20 @@ $(document).ready(function () {
|
||||
searching: true,
|
||||
autoWidth: false,
|
||||
responsive: true,
|
||||
// language: {
|
||||
// search: `
|
||||
// <div class="datatable-search-wrapper" style="position:relative; display:inline-block; width:100%;">
|
||||
// _INPUT_
|
||||
// <i class="mdi mdi-magnify datatable-search-icon"
|
||||
// style="position:absolute; right:122px !important; top:50%; transform:translateY(-50%); color:#666;"></i>
|
||||
|
||||
// <i class="mdi mdi-close-circle datatable-clear-icon"
|
||||
// style="position:absolute; right:122px !important; top:50%; transform:translateY(-50%); color:#666; display:none; cursor:pointer;"></i>
|
||||
// </div>
|
||||
// `,
|
||||
// searchPlaceholder: "Search",
|
||||
// emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
||||
// },
|
||||
});
|
||||
|
||||
|
||||
|
||||
@ -173,7 +173,7 @@
|
||||
if (ticketsTable.length) {
|
||||
ticketsTable.DataTable({
|
||||
scrollX: true,
|
||||
dom: "<'row'<'col-sm-2'f><'col-sm-10 text-right'B>>" +
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
buttons: [{
|
||||
@ -197,6 +197,20 @@
|
||||
}],
|
||||
paging: true,
|
||||
pageLength: 10,
|
||||
language: {
|
||||
search: `
|
||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block; width:100%;">
|
||||
_INPUT_
|
||||
<i class="mdi mdi-magnify datatable-search-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666;"></i>
|
||||
|
||||
<i class="mdi mdi-close-circle datatable-clear-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none; cursor:pointer;"></i>
|
||||
</div>
|
||||
`,
|
||||
searchPlaceholder: "Search",
|
||||
emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
||||
},
|
||||
order: [
|
||||
[0, 'desc']
|
||||
]
|
||||
|
||||
@ -102,8 +102,18 @@ $(document).ready(function () {
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
language: {
|
||||
search: "_INPUT_",
|
||||
searchPlaceholder: "Search..."
|
||||
search: `
|
||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block; width:100%;">
|
||||
_INPUT_
|
||||
<i class="mdi mdi-magnify datatable-search-icon"
|
||||
style="position:absolute; right:122px !important; top:50%; transform:translateY(-50%); color:#666;"></i>
|
||||
|
||||
<i class="mdi mdi-close-circle datatable-clear-icon"
|
||||
style="position:absolute; right:122px !important; top:50%; transform:translateY(-50%); color:#666; display:none; cursor:pointer;"></i>
|
||||
</div>
|
||||
`,
|
||||
searchPlaceholder: "Search",
|
||||
emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
||||
},
|
||||
paging: true,
|
||||
pageLength: 10,
|
||||
|
||||
@ -164,6 +164,15 @@
|
||||
margin-left: 5px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
th.no-sort {
|
||||
pointer-events: none; /* disable click */
|
||||
}
|
||||
|
||||
th.no-sort:before,
|
||||
th.no-sort:after {
|
||||
display: none !important; /* hide DataTables sorting arrows */
|
||||
}
|
||||
|
||||
</style>
|
||||
<div class="container-fluid-min">
|
||||
<div class="row" id="inception_list">
|
||||
@ -205,11 +214,12 @@
|
||||
</div>
|
||||
<div class="dataTables_length d-flex align-items-center">
|
||||
</div>
|
||||
<div>
|
||||
<div class="table-responsive">
|
||||
|
||||
<table data-custom-table-css="table" class="table table-striped mb-0 nowrap" cellspacing="0" id="tickets-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="no-sort"></th>
|
||||
<th>
|
||||
<div class="column-header">Insurer</div>
|
||||
</th>
|
||||
@ -1734,9 +1744,24 @@
|
||||
"order": [], // Disable initial sorting if needed
|
||||
"pageLength": 10,
|
||||
// "dom": '<"top"lf>rt<"bottom"ip><"clear">', // This places length and filter controls at top
|
||||
"language": {
|
||||
"lengthMenu": "Show _MENU_ entries",
|
||||
"search": "Search:"
|
||||
// "language": {
|
||||
// "lengthMenu": "Show _MENU_ entries",
|
||||
// "search": "Search:"
|
||||
// }
|
||||
language: {
|
||||
lengthMenu: "Show _MENU_ entries",
|
||||
search: `
|
||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block; width:100%;">
|
||||
_INPUT_
|
||||
<i class="mdi mdi-magnify datatable-search-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666;"></i>
|
||||
|
||||
<i class="mdi mdi-close-circle datatable-clear-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none; cursor:pointer;"></i>
|
||||
</div>
|
||||
`,
|
||||
searchPlaceholder: "Search",
|
||||
emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
970
app/Views/invoice_policy_mapping.php
Normal file
970
app/Views/invoice_policy_mapping.php
Normal file
@ -0,0 +1,970 @@
|
||||
<style>
|
||||
/* ---- CSS cleaned/optimized ---- */
|
||||
.table th, .table td { padding: 8px; }
|
||||
table.dataTable tbody td { padding: 4px 4px !important; }
|
||||
.col-12 { max-width: 98% !important; }
|
||||
.dataTables_filter { position: absolute; }
|
||||
.column-header { margin-right: 10px; }
|
||||
.filter-inline { display: flex; align-items: center; gap: 10px; }
|
||||
.filter-inline input[type="date"] { padding: 8px 12px; border:1px solid #ddd; border-radius:4px; font-size:13px; background:white; cursor:pointer; }
|
||||
.filter-inline input[type="date"]:focus { outline:none; border-color:#00a9a3; }
|
||||
|
||||
.icon-btn { width:36px; height:36px; border:1px solid #ddd; background:white; border-radius:4px; cursor:pointer; display:flex; align-items:center; justify-content:center; transition:all 0.2s; }
|
||||
.icon-btn:hover { background:#f5f5f5; border-color:#00a9a3; }
|
||||
.icon-btn svg { width:18px; height:18px; fill:#666; }
|
||||
.icon-btn:hover svg { fill:#00a9a3; }
|
||||
|
||||
.policy-list { border:1px solid #e0e0e0; border-radius:4px; overflow:hidden; margin-top:0; }
|
||||
.policy-header { background:#f8f9fa; padding:12px 15px; font-weight:600; border-bottom:1px solid #e0e0e0; display:flex; align-items:center; font-size:14px; }
|
||||
.policy-header input[type="checkbox"] { width:18px; height:18px; margin-right:10px; cursor:pointer; }
|
||||
.policy-item { border-bottom:1px solid #e0e0e0; padding:12px 15px; display:flex; align-items:center; background:white; transition:background 0.2s; }
|
||||
.policy-item:hover { background:#f9f9f9; }
|
||||
.policy-item:last-child { border-bottom:none; }
|
||||
.policy-item.selected { background:#e8f5f4; }
|
||||
.policy-checkbox { width:18px; height:18px; margin-right:15px; cursor:pointer; }
|
||||
.policy-info { flex:1; display:flex; justify-content:space-between; align-items:center; }
|
||||
.policy-details { flex:1; }
|
||||
.policy-number { font-weight:600; color:#333; margin-bottom:4px; font-size:14px; }
|
||||
.policy-meta { color:#666; font-size:13px; }
|
||||
.policy-amount { font-size:16px; font-weight:600; color:#00a9a3; margin-left:20px; }
|
||||
|
||||
.badge { display:inline-block; padding:4px 10px; border-radius:12px; font-size:12px; font-weight:600; margin-left:10px; }
|
||||
.badge-selected { background:#00a9a3; color:white; }
|
||||
|
||||
.summary-bar { position:fixed; bottom:0; left:0; right:0; width:100%; background:white; border-top:2px solid #00a9a3; padding:15px 30px; display:none; box-shadow:0 -2px 10px rgba(0,0,0,0.1); z-index:100; }
|
||||
.summary-bar.show { display:flex; justify-content:flex-start; align-items:center; }
|
||||
.summary-info { margin-left:auto; margin-right:0; display:flex; gap:40px; align-items:center; }
|
||||
.summary-item { display:flex; flex-direction:column; }
|
||||
.summary-label { font-size:12px; color:#666; margin-bottom:2px; }
|
||||
.summary-value { font-size:18px; font-weight:600; color:#00a9a3; }
|
||||
.summary-actions { display:flex; gap:10px; margin-left:auto; }
|
||||
.btn-icon { background:#008b8b; border:none; border-radius:50%; width:32px; height:32px; display:flex; align-items:center; justify-content:center; cursor:pointer; }
|
||||
.btn-icon i { color:#fff !important; font-size:18px; }
|
||||
.btn-icon:hover { background:#00a3a3; }
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width:1024px){ .filter-inline{flex-wrap:wrap;} .filter-inline input[type="date"]{font-size:12px;padding:6px 8px;} }
|
||||
@media (max-width:768px){
|
||||
.section-header{flex-direction:column;gap:10px;align-items:flex-start;}
|
||||
.header-right{width:100%;justify-content:space-between;}
|
||||
.filter-inline{flex:1;}
|
||||
.summary-bar{left:0;flex-direction:column;gap:15px;padding:15px;}
|
||||
.summary-info{width:100%;justify-content:space-around;}
|
||||
.summary-actions{width:100%;}
|
||||
}
|
||||
.policy-adjustment::-webkit-inner-spin-button,
|
||||
.policy-adjustment::-webkit-outer-spin-button {-webkit-appearance:none;margin:0;}
|
||||
.policy-adjustment { -moz-appearance:textfield; }
|
||||
</style>
|
||||
|
||||
<div class="row" id="invoices_details">
|
||||
<div class="col-12">
|
||||
<div id="invoices_accordion" class="ml-3">
|
||||
<div class="card mb-1">
|
||||
<h4 class="m-1">
|
||||
<a href="#" onclick="history.back(); return false;">
|
||||
<i style="font-size: 18px;" class="mdi mdi-chevron-left" title="Back"></i>
|
||||
</a>
|
||||
<span>Invoice Details</span>
|
||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne"
|
||||
aria-expanded="true">
|
||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
</h4>
|
||||
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#invoices_accordion">
|
||||
<div class="card-body" style="padding-bottom: unset;">
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-3">
|
||||
<label>Invoice Number </label>
|
||||
<input class="form-control" type="text" id="invoiceNo" placeholder="Auto-generated" readonly
|
||||
value="<?= isset($invoice['invoice_no']) ? $invoice['invoice_no'] : '' ?>">
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<label>Invoice Date <span class="text-danger"></span></label>
|
||||
<input class="form-control" type="date" id="invoiceDate" required value="<?= isset($invoice['invoice_date']) ? $invoice['invoice_date'] : '' ?>" >
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<label for="agents"> Agents <span class="text-danger"></span></label>
|
||||
|
||||
<select class="form-control" id="agentSelect" name="agents_id" onchange="loadPolicies()" required>
|
||||
<option value="">Select Agent</option>
|
||||
<?php
|
||||
if(isset($agents) && count($agents)) {
|
||||
foreach($agents as $agent): ?>
|
||||
<option value="<?= $agent['id'] ?>"
|
||||
<?= isset($invoice['agent_id']) && $invoice['agent_id'] == $agent['id'] ? 'selected' : '' ?>>
|
||||
<?= $agent['name'] ?> - <?= $agent['agent_code'] ?>
|
||||
</option>
|
||||
<?php endforeach;
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<label>Policy Till Date<span class="text-danger"></span></label>
|
||||
<input class="form-control" type="date" id="policyTillDate" required onchange="loadPolicies()" max="<?= date('Y-m-d') ?>"
|
||||
value="<?= isset($invoice['invoice_date']) ? $invoice['invoice_date'] : '' ?>" >
|
||||
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<input class="form-control" type="hidden" id="invoiceID" required value="<?= isset($invoice['id']) ? $invoice['id'] : '' ?>" >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="payouts_details">
|
||||
<div class="col-12">
|
||||
<div id="policy_accordion" class="ml-3">
|
||||
<div class="card mb-1">
|
||||
<div class="row align-items-center m-1" id="policy_filter">
|
||||
|
||||
|
||||
<div class="col d-flex align-items-center">
|
||||
<h4 class="mb-0">
|
||||
Policy Selection
|
||||
<span id="selectedCount" class="badge badge-selected" style="display:none;">0 selected</span>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-auto d-flex align-items-center">
|
||||
|
||||
<input type="date" class="form-control mr-2" id="fromDate" placeholder="From Date" title="From Date" style="width:170px;">
|
||||
<input type="date" class="form-control mr-2" id="toDate" placeholder="To Date" title="To Date" style="width:170px;">
|
||||
|
||||
<button type="button" class="btn-icon mr-1" title="Reset Filters" onclick="resetFilters()"><i class="mdi mdi-refresh"></i></button>
|
||||
|
||||
<button type="button" class="btn-icon mr-1" title="Search" onclick="searchPolicies()"><i class="mdi mdi-magnify"></i></button>
|
||||
|
||||
|
||||
<!-- <button class="btn app-btn-primary mr-2" onclick="resetFilters()" title="Reset Filters">
|
||||
<i class="ri-reset-right-fill"></i>
|
||||
</button>
|
||||
|
||||
<button class="btn app-btn-primary mr-2" onclick="searchPolicies()" title="Search">
|
||||
<i class="ri-search-line"></i>
|
||||
</button> -->
|
||||
|
||||
<?php if(isset($type) && $type === 'edit'): ?>
|
||||
<button class="btn app-btn-primary mr-2 M" onclick="showMorePolicies()" title="More Policies">
|
||||
More Policies
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-auto">
|
||||
<a id="toggleIcon" class="text-dark" data-toggle="collapse" href="#collapseTwo">
|
||||
<i id="icon" class="mdi mdi-chevron-down text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="collapseTwo" class="collapse show" aria-labelledby="headingTwo" data-parent="#policy_accordion">
|
||||
|
||||
<div class="card-body" style="padding-top: unset !important; border: white !important; background: unset !important;">
|
||||
<div>
|
||||
<table data-custom-table-css="table" class="table table-striped mb-0 nowrap" cellspacing="0" id="policy_table">
|
||||
|
||||
<!-- <table data-custom-table-css="table" class="table table-borderless mb-0 nowrap" id="policy_table"> -->
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align:center;"><input type="checkbox" class="CB" id="selectAll" onchange="toggleSelectAll(this)"></th>
|
||||
<th><div class="column-header">Policy No</div></th><!-- 2 -->
|
||||
<th><div class="column-header">Customer</div></th> <!-- 3 -->
|
||||
<th><div class="column-header">Premium</div></th> <!-- 4 -->
|
||||
<th><div class="column-header">Policy Issues Date</div></th> <!-- 5 -->
|
||||
<th><div class="column-header">Commission Amount</div></th> <!-- 6 -->
|
||||
<?php if(isset($type) && $type === 'adjustment'): ?>
|
||||
<th><div class="column-header">Paid Amount</div></th> <!-- 7 -->
|
||||
<?php endif; ?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="policyListBody"></tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<div id="summaryBar" class="summary-bar">
|
||||
<div class="summary-info">
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">Selected Policies</span>
|
||||
<span class="summary-value" id="totalPolicies">0</span>
|
||||
</div>
|
||||
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">Total Commission</span>
|
||||
<span class="summary-value" id="totalAmount">₹0.00</span>
|
||||
</div>
|
||||
|
||||
<?php if(isset($type) && $type === 'adjustment'): ?>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">Total Paid Amount</span>
|
||||
<span class="summary-value" id="totalAdjustmentAmount">₹0.00</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if(isset($type) && $type !== 'add'): ?>
|
||||
<div class="summary-item">
|
||||
<button type="button" class="btn-icon mr-1 text-warning" data-toggle="modal" data-target="#auditingModal" onclick="auditingHistory()" title="History"><i class="mdi mdi-history"></i></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="summary-actions">
|
||||
<button type="button" class="btn app-btn-outline-primary C" onclick="clearSelection()">Clear</button>
|
||||
<button type="button" class="btn app-btn-secondary S" onclick="saveInvoice()">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- end col -->
|
||||
</div>
|
||||
|
||||
<div class="modal fade " id="auditingModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg" >
|
||||
<!-- style="max-width: 775px !important;"> -->
|
||||
<div class="modal-content" style="border-radius:15px;">
|
||||
|
||||
<div class="modal-header" style="border-bottom-width:0;">
|
||||
<h4 class="modal-title title-text ">Auditing History</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" data-bs-dismiss="modal">×</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body card-scroll history-container" style="padding:5px 11px 11px 11px!important;">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
// ---- Internal state ----
|
||||
let policyTable = null;
|
||||
const selectedPolicies = new Set();
|
||||
let filteredPolicies = [];
|
||||
window.allPolicies = <?php echo json_encode($payouts); ?> || [];
|
||||
window.extraPolicies = <?php echo json_encode($extra_payouts); ?> || [];
|
||||
window.checkedPolicyNumbers = <?php echo json_encode($checked_policy_numbers); ?> || [];
|
||||
|
||||
const invoiceType = <?php echo json_encode($type); ?>;
|
||||
|
||||
const colspan = invoiceType == "adjustment" ? 7 : 6;
|
||||
|
||||
// ---- Safe DOM getter ----
|
||||
function getEl(selector) {
|
||||
const el = document.getElementById(selector) || document.querySelector(selector);
|
||||
return el || null;
|
||||
}
|
||||
|
||||
// ---- Generate invoice number ----
|
||||
function generateInvoiceNumber() {
|
||||
const invoiceNoEl = getEl('invoiceNo');
|
||||
if (invoiceNoEl && invoiceType == 'add') invoiceNoEl.value = "<?= $invoice_number ?>";
|
||||
}
|
||||
|
||||
// ---- Toggle collapsible sections ----
|
||||
function toggleSection(sectionId) {
|
||||
const content = getEl(sectionId);
|
||||
const icon = getEl(sectionId + 'Icon');
|
||||
if (!content) return;
|
||||
content.classList.toggle('collapsed');
|
||||
if (icon) icon.classList.toggle('collapsed');
|
||||
}
|
||||
|
||||
|
||||
// ---- Reload Data table ----
|
||||
function reloadDataTable() {
|
||||
|
||||
// Destroy existing table
|
||||
if ($.fn.DataTable && $.fn.DataTable.isDataTable('#policy_table')) {
|
||||
try { $('#policy_table').DataTable().destroy(); } catch(e) {}
|
||||
$('#policy_table').find('thead').show();
|
||||
}
|
||||
|
||||
// -------- EXPORT LOGIC --------
|
||||
let exportColumns = ':not(:first-child)';
|
||||
let exportFormat = null;
|
||||
|
||||
if (invoiceType === 'adjustment') {
|
||||
exportFormat = function (data, row, column, node) {
|
||||
const input = node.querySelector('input');
|
||||
return input ? `₹${Number(input.value || 0).toFixed(2)}` : data;
|
||||
};
|
||||
}
|
||||
// --------------------------------
|
||||
|
||||
policyTable = $('#policy_table').DataTable({
|
||||
scrollX: true,
|
||||
paging: true,
|
||||
searching: true,
|
||||
ordering: false,
|
||||
autoWidth: false,
|
||||
stateSave: false,
|
||||
|
||||
dom:
|
||||
"<'row'<'col-sm-6'f><'col-sm-6 text-right'B>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
|
||||
buttons: [
|
||||
{
|
||||
extend: 'collection',
|
||||
text: '<span class="btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
className: 'btn app-btn-secondary',
|
||||
buttons: [
|
||||
{
|
||||
extend: 'csv',
|
||||
text: '<i class="mdi mdi-file-delimited"></i><span class="btn-custom"> CSV </span>',
|
||||
className: 'app-btn-primary',
|
||||
title: getExportFileName(),
|
||||
exportOptions: {
|
||||
columns: exportColumns,
|
||||
format: exportFormat ? { body: exportFormat } : undefined
|
||||
}
|
||||
},
|
||||
{
|
||||
extend: 'excel',
|
||||
text: '<i class="mdi mdi-file-excel"></i><span class="btn-custom"> EXCEL </span>',
|
||||
className: 'app-btn-primary',
|
||||
title: getExportFileName(),
|
||||
sheetName: getExportFileName(),
|
||||
exportOptions: {
|
||||
columns: exportColumns,
|
||||
format: exportFormat ? { body: exportFormat } : undefined
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
language: {
|
||||
search: `
|
||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
|
||||
_INPUT_
|
||||
<i class="mdi mdi-magnify datatable-search-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666;"></i>
|
||||
<i class="mdi mdi-close-circle datatable-clear-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
|
||||
</div>`,
|
||||
searchPlaceholder: "Search",
|
||||
emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
||||
}
|
||||
});
|
||||
|
||||
// FIX alignment
|
||||
setTimeout(() => {
|
||||
if (policyTable) policyTable.columns.adjust().draw(false);
|
||||
}, 300);
|
||||
}
|
||||
|
||||
|
||||
// ---- Render policies in table ----
|
||||
function renderPolicies() {
|
||||
if (!policyTable) {
|
||||
reloadDataTable(); // initialize DataTable if not yet
|
||||
}
|
||||
|
||||
// Clear existing rows
|
||||
policyTable.clear();
|
||||
|
||||
if (!filteredPolicies || filteredPolicies.length === 0) {
|
||||
// Add "No policies found" row
|
||||
policyTable.row.add([
|
||||
"No policies found"
|
||||
]).draw();
|
||||
$('#policy_table tbody tr td').attr('colspan', colspan).addClass('text-center');
|
||||
} else {
|
||||
filteredPolicies.forEach(p => {
|
||||
const checked = selectedPolicies.has(String(p.id)) ? 'checked' : '';
|
||||
const row = [
|
||||
`<input type="checkbox" class="policy-checkbox CB" ${checked} onchange="togglePolicy(${p.id})">`,
|
||||
p.policyNo || '',
|
||||
p.customer || '',
|
||||
`₹${Number(p.premium || 0).toFixed(2)}`,
|
||||
p.date || '',
|
||||
`₹${Number(p.commission || 0).toFixed(2)}`
|
||||
];
|
||||
|
||||
// Add adjustment input if needed
|
||||
if (invoiceType === 'adjustment') {
|
||||
row.push(`<input type="number"
|
||||
class="policy-adjustment"
|
||||
data-invoiceItemId="${p.invoiceItemId}"
|
||||
value="${p.paid_amount ?? p.commission ?? 0}"
|
||||
min="0"
|
||||
oninput="updateSummary();">`);
|
||||
}
|
||||
|
||||
policyTable.row.add(row);
|
||||
});
|
||||
|
||||
policyTable.draw();
|
||||
|
||||
$('#policy_table tbody tr td:first-child').css('text-align', 'center');
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Update Select All checkbox visibility
|
||||
const selectAllEl = getEl('selectAll');
|
||||
if (selectAllEl) selectAllEl.style.display = filteredPolicies.length > 0 ? '' : 'none';
|
||||
|
||||
// Update select-all checkbox state & summary
|
||||
updateSelectAllCheckbox();
|
||||
updateSummary();
|
||||
// ---- Make checkboxes readonly in adjustment mode ----
|
||||
if (invoiceType === 'adjustment') {
|
||||
$('#policy_table').on('draw.dt', function() {
|
||||
document.querySelectorAll('.CB').forEach(cb => cb.disabled = true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---- Filter policies by agent/date ----
|
||||
function filterPolicies() {
|
||||
const fromDate = getEl('fromDate')?.value || '';
|
||||
const toDate = getEl('toDate')?.value || '';
|
||||
const agentId = getEl('agentSelect')?.value || '';
|
||||
const policyTillDate = getEl('policyTillDate')?.value || '';
|
||||
|
||||
if (!agentId || agentId === '0') return toastr.warning('Please select an agent', 'Required');
|
||||
|
||||
filteredPolicies = window.allPolicies.filter(p => {
|
||||
if (String(p.agentId) !== String(agentId)) return false;
|
||||
if (policyTillDate && p.date_db > policyTillDate) return false;
|
||||
if (fromDate && p.date_db < fromDate) return false;
|
||||
if (toDate && p.date_db > toDate) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
renderPolicies();
|
||||
}
|
||||
|
||||
// ---- Load policies for selected agent ----
|
||||
function loadPolicies() {
|
||||
const agentId = getEl('agentSelect')?.value || '';
|
||||
const policyTillDate = getEl('policyTillDate')?.value || '';
|
||||
const list = getEl('policyListBody');
|
||||
if (!list) return;
|
||||
|
||||
selectedPolicies.clear();
|
||||
|
||||
// Destroy DataTable
|
||||
if (policyTable && $.fn.DataTable.isDataTable('#policy_table')) {
|
||||
try { policyTable.destroy(); } catch(e) {}
|
||||
policyTable = null;
|
||||
}
|
||||
|
||||
list.innerHTML = '';
|
||||
|
||||
if (!agentId || agentId === '0') {
|
||||
list.innerHTML = `<tr><td colspan="${colspan}" class="text-center text-danger">Please select an agent</td></tr>`;
|
||||
updateSummary();
|
||||
reloadDataTable();
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter policies for the selected agent
|
||||
filteredPolicies = window.allPolicies.filter(p => {
|
||||
if (String(p.agentId) !== String(agentId)) return false;
|
||||
if (policyTillDate && p.date_db > policyTillDate) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
if (invoiceType != 'add') {
|
||||
filteredPolicies.forEach(p => {
|
||||
if (window.checkedPolicyNumbers.includes(p.policyNo)) {
|
||||
selectedPolicies.add(String(p.id));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
renderPolicies();
|
||||
}
|
||||
|
||||
|
||||
// ---- Toggle select-all checkbox ----
|
||||
function toggleSelectAll() {
|
||||
const selectAllBox = getEl('selectAll');
|
||||
if (!selectAllBox) return;
|
||||
const selectAll = selectAllBox.checked;
|
||||
|
||||
if (selectAll) {
|
||||
filteredPolicies.forEach(p => selectedPolicies.add(String(p.id)));
|
||||
} else {
|
||||
filteredPolicies.forEach(p => selectedPolicies.delete(String(p.id)));
|
||||
}
|
||||
renderPolicies();
|
||||
}
|
||||
|
||||
// ---- Toggle single policy ----
|
||||
function togglePolicy(policyId) {
|
||||
policyId = String(policyId);
|
||||
if (selectedPolicies.has(policyId)) selectedPolicies.delete(policyId);
|
||||
else selectedPolicies.add(policyId);
|
||||
renderPolicies();
|
||||
}
|
||||
|
||||
// ---- Update select-all header ----
|
||||
function updateSelectAllCheckbox() {
|
||||
const selectAllCheckbox = getEl('selectAll');
|
||||
if (!selectAllCheckbox) return;
|
||||
selectAllCheckbox.checked = filteredPolicies.length > 0 &&
|
||||
filteredPolicies.every(p => selectedPolicies.has(String(p.id)));
|
||||
}
|
||||
|
||||
// ---- Update summary footer ----
|
||||
function updateSummary() {
|
||||
const summaryBar = getEl('summaryBar');
|
||||
const selectedCountBadge = getEl('selectedCount');
|
||||
const totalPoliciesEl = getEl('totalPolicies');
|
||||
const totalAmountEl = getEl('totalAmount');
|
||||
let totalAdjustmentAmountEl;
|
||||
if (invoiceType === 'adjustment') { totalAdjustmentAmountEl = getEl('totalAdjustmentAmount');}
|
||||
|
||||
if (!summaryBar || !totalPoliciesEl || !totalAmountEl) return;
|
||||
|
||||
if (selectedPolicies.size === 0) {
|
||||
summaryBar.classList.remove('show')
|
||||
const summaryBar = getEl('summaryBar');;
|
||||
if (selectedCountBadge) selectedCountBadge.style.display = 'none';
|
||||
totalPoliciesEl.textContent = '0';
|
||||
totalAmountEl.textContent = '₹0.00';
|
||||
if (invoiceType === 'adjustment') totalAdjustmentAmountEl.textContent = '₹0.00';
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedCountBadge) {
|
||||
selectedCountBadge.style.display = 'inline-block';
|
||||
selectedCountBadge.textContent = `${selectedPolicies.size} selected`;
|
||||
}
|
||||
|
||||
summaryBar.classList.add('show');
|
||||
|
||||
// TOTAL COMMISSION AMOUNT
|
||||
const totalAmount = filteredPolicies
|
||||
.filter(p => selectedPolicies.has(String(p.id)))
|
||||
.reduce((sum, p) => sum + Number(p.commission || 0), 0);
|
||||
|
||||
totalPoliciesEl.textContent = selectedPolicies.size;
|
||||
totalAmountEl.textContent = `₹${totalAmount.toFixed(2)}`;
|
||||
|
||||
// TOTAL ADJUSTMENT AMOUNT
|
||||
if (invoiceType === 'adjustment') {
|
||||
|
||||
// read all input boxes
|
||||
let totalAdjustment = 0;
|
||||
|
||||
document.querySelectorAll('.policy-adjustment').forEach(input => {
|
||||
const val = parseFloat(input.value) || 0;
|
||||
totalAdjustment += val;
|
||||
});
|
||||
|
||||
totalAdjustmentAmountEl.textContent = `₹${totalAdjustment.toFixed(2)}`;
|
||||
// const difference = totalAmount - totalAdjustment;
|
||||
// totalAdjustmentAmountEl.textContent = `₹${difference.toFixed(2)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---- Clear selection ----
|
||||
function clearSelection() {
|
||||
|
||||
const count = selectedPolicies.size;
|
||||
if (count === 0) return toastr.info('No policies selected.', 'Info');
|
||||
if (count === 1) return toastr.warning('At least one policy must remain selected.', 'Warning');
|
||||
if (!confirm('Are you sure you want to clear all selections?')) return;
|
||||
selectedPolicies.clear();
|
||||
renderPolicies();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ---- Reset filters ----
|
||||
function resetFilters() {
|
||||
['fromDate','toDate','from_date','to_date'].forEach(id => {
|
||||
if (getEl(id)) getEl(id).value = '';
|
||||
});
|
||||
loadPolicies();
|
||||
}
|
||||
|
||||
|
||||
// ---- Show More Policies ----
|
||||
function showMorePolicies() {
|
||||
const agentId = getEl('agentSelect')?.value || '';
|
||||
if (!agentId) return toastr.warning('Please select an agent', 'Required');
|
||||
|
||||
if (!window.extraPolicies || window.extraPolicies.length === 0) {
|
||||
return toastr.info('No more policies available.', 'Information');
|
||||
}
|
||||
|
||||
// Get existing policy IDs in filteredPolicies
|
||||
const existingPolicyIds = new Set(filteredPolicies.map(p => p.id));
|
||||
|
||||
// Filter extraPolicies: same agent + not already in filteredPolicies
|
||||
const newPolicies = window.extraPolicies.filter(p => {
|
||||
return String(p.agentId) === String(agentId) && !existingPolicyIds.has(p.id);
|
||||
});
|
||||
|
||||
if (newPolicies.length === 0) {
|
||||
toastr.info('No more policies available for this agent.', 'Information');
|
||||
return;
|
||||
}
|
||||
|
||||
// Append new policies to filteredPolicies
|
||||
filteredPolicies = [...filteredPolicies, ...newPolicies];
|
||||
|
||||
// In EDIT mode, pre-check any policies that already have invoiceItemId
|
||||
if (invoiceType != 'add') {
|
||||
filteredPolicies.forEach(p => {
|
||||
if (p.invoiceItemId && !selectedPolicies.has(String(p.id))) {
|
||||
selectedPolicies.add(String(p.id));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Re-render table
|
||||
renderPolicies();
|
||||
}
|
||||
|
||||
// ---- Save invoice ----
|
||||
function saveInvoice() {
|
||||
const agentId = getEl('agentSelect')?.value || '';
|
||||
const invoiceNo = getEl('invoiceNo')?.value || '';
|
||||
const invoiceDate = getEl('invoiceDate')?.value || '';
|
||||
const policyTillDate = getEl('policyTillDate')?.value || '';
|
||||
const invoiceID = getEl('invoiceID')?.value || '';
|
||||
|
||||
if (!agentId || agentId === '0') return toastr.warning('Please select an agent', 'Required');
|
||||
if (!invoiceDate) return toastr.warning('Please select invoice date', 'Required');
|
||||
if (selectedPolicies.size === 0) return toastr.warning('Please select at least one policy', 'Required');
|
||||
|
||||
const selectedPolicyData = filteredPolicies.filter(p => selectedPolicies.has(String(p.id)));
|
||||
|
||||
// Map policies and compute totalAmount using adjustment if applicable
|
||||
let totalAmount = 0;
|
||||
const policies = selectedPolicyData.map(p => {
|
||||
let finalAmount = Number(p.commission || 0);
|
||||
|
||||
if (invoiceType === 'adjustment') {
|
||||
const adjustmentInput = document.querySelector(`.policy-adjustment[data-invoiceItemId="${p.invoiceItemId}"]`);
|
||||
const adjustmentValue = adjustmentInput ? parseFloat(adjustmentInput.value) : null;
|
||||
if (adjustmentValue !== null && !isNaN(adjustmentValue)) {
|
||||
finalAmount = adjustmentValue;
|
||||
}
|
||||
}
|
||||
|
||||
totalAmount += finalAmount;
|
||||
|
||||
return {
|
||||
policy_id: p.id,
|
||||
policy_no: p.policyNo,
|
||||
commission_amount: finalAmount
|
||||
};
|
||||
});
|
||||
|
||||
const invoiceData = {
|
||||
invoice_id: invoiceID,
|
||||
invoice_no: invoiceNo,
|
||||
agent_id: agentId,
|
||||
invoice_date: invoiceDate,
|
||||
invoice_amount: totalAmount,
|
||||
policies: policies
|
||||
};
|
||||
|
||||
// console.log(invoiceData);return true;
|
||||
|
||||
$.ajax({
|
||||
url: '<?= base_url('payout/invoices/save') ?>',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(invoiceData),
|
||||
contentType: 'application/json',
|
||||
success: function(response) {
|
||||
// alert(`Invoice created successfully!\n\nInvoice No: ${invoiceNo}`);
|
||||
toastr.success(response.message, 'Success');
|
||||
window.location.href = '<?= base_url('payout/list') ?>';
|
||||
},
|
||||
error: function() {
|
||||
// alert('Error creating invoice');
|
||||
toastr.error(response.message, 'Invoice Error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ---- Expose functions globally ----
|
||||
window.loadPolicies = loadPolicies;
|
||||
window.filterPolicies = filterPolicies;
|
||||
window.resetFilters = resetFilters;
|
||||
window.toggleSection = toggleSection;
|
||||
window.clearSelection = clearSelection;
|
||||
window.saveInvoice = saveInvoice;
|
||||
window.generateInvoiceNumber = generateInvoiceNumber;
|
||||
window.toggleSelectAll = toggleSelectAll;
|
||||
window.togglePolicy = togglePolicy;
|
||||
|
||||
// ---- DOM Ready Initialization ----
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
selectedPolicies.clear();
|
||||
filteredPolicies = [];
|
||||
|
||||
const today = new Date().toISOString().split("T")[0];
|
||||
|
||||
const invoiceDateEl = getEl('invoiceDate');
|
||||
const policyTillDateEl = getEl('policyTillDate');
|
||||
const agentSelectEl = getEl('agentSelect');
|
||||
|
||||
// Set max date (today)
|
||||
if (invoiceDateEl) invoiceDateEl.max = today;
|
||||
if (policyTillDateEl) policyTillDateEl.max = today;
|
||||
|
||||
if (invoiceType == 'add') {
|
||||
|
||||
// -------- ADD MODE --------
|
||||
if (agentSelectEl) agentSelectEl.value = '';
|
||||
if (invoiceDateEl) invoiceDateEl.required = true;
|
||||
if (policyTillDateEl) policyTillDateEl.required = true;
|
||||
if (agentSelectEl) agentSelectEl.required = true;
|
||||
|
||||
// Enable Select2
|
||||
$('#agentSelect').prop('disabled', false).select2();
|
||||
}
|
||||
else {
|
||||
// -------- EDIT,ADJUSTMENT MODE --------
|
||||
if (agentSelectEl) agentSelectEl.readOnly = true;
|
||||
|
||||
// Disable Select2 (readonly)
|
||||
$('#agentSelect').prop('disabled', true);
|
||||
|
||||
// Remove red star (*) from label
|
||||
const agentLabel = document.querySelector('label[for="agents"] .text-danger');
|
||||
if (agentLabel) agentLabel.remove();
|
||||
}
|
||||
|
||||
|
||||
generateInvoiceNumber();
|
||||
if (invoiceType == 'add') {
|
||||
if (getEl('invoiceDate')) getEl('invoiceDate').valueAsDate = new Date();
|
||||
if (getEl('policyTillDate')) getEl('policyTillDate').valueAsDate = new Date();
|
||||
}
|
||||
|
||||
const agentEl = getEl('agentSelect');
|
||||
if (agentEl) {
|
||||
agentEl.addEventListener('change', () => {
|
||||
selectedPolicies.clear();
|
||||
loadPolicies();
|
||||
});
|
||||
}
|
||||
|
||||
['fromDate','toDate'].forEach(id => {
|
||||
const el = getEl(id);
|
||||
if (el) el.addEventListener('change', filterPolicies);
|
||||
});
|
||||
|
||||
|
||||
if (policyTillDateEl) policyTillDateEl.addEventListener('change', loadPolicies);
|
||||
|
||||
if (agentEl && agentEl.value && agentEl.value !== '0') loadPolicies();
|
||||
else {
|
||||
const body = getEl('policyListBody');
|
||||
if (body) body.innerHTML = `<tr><td colspan="${colspan}" class="text-center">Please select an agent</td></tr>`;
|
||||
reloadDataTable();
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const freeze = <?= isset($freeze_edit) ? (int)$freeze_edit : 0 ?>;
|
||||
const type = '<?= isset($type) ? $type : '' ?>';
|
||||
|
||||
if (freeze > 0 && type === 'edit') {
|
||||
console.log("Freeze edit active");
|
||||
|
||||
// Wait until DataTable is initialized
|
||||
$('#policy_table').on('draw.dt', function() {
|
||||
document.querySelectorAll('.CB').forEach(cb => cb.disabled = true);
|
||||
});
|
||||
|
||||
// Hide summary bar actions
|
||||
document.querySelector('.S').style.display = 'none'; // hides it
|
||||
document.querySelector('.C').style.display = 'none';
|
||||
document.querySelector('.M').style.display = 'none'; // hides it
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
function auditingHistory() {
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const InvoiceID = params.get('id'); // "12"
|
||||
|
||||
$.ajax({
|
||||
url: "<?= base_url('payout/invoices/history?id=') ?>" + InvoiceID,
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
|
||||
success: function(response) {
|
||||
let container = $(".history-container");
|
||||
container.empty();
|
||||
|
||||
const invoiceArray = response?.data?.invoice || [];
|
||||
const childArray = response?.data?.invoice_child || [];
|
||||
|
||||
// ---------- FIXED CONDITION ----------
|
||||
if (
|
||||
response.status === "success" &&
|
||||
(invoiceArray.length > 0 || childArray.length > 0)
|
||||
) {
|
||||
let table = `
|
||||
<div class="table-responsive">
|
||||
<table data-custom-table-css="table"
|
||||
class="table w-100 nowrap text-custom-black text-custom app-datatable">
|
||||
|
||||
<thead class="bg-light">
|
||||
<!-- MAIN TITLE
|
||||
<tr>
|
||||
<th colspan="6" class="text-center"
|
||||
style="font-size:22px; font-weight:700;">
|
||||
Auditing History
|
||||
</th>
|
||||
</tr>
|
||||
-->
|
||||
|
||||
<!-- INVOICE TITLE -->
|
||||
<tr>
|
||||
<th colspan="6" class="text-left"
|
||||
style="font-size:18px; font-weight:600;">
|
||||
Invoice No: ${invoiceArray[0]?.invoice_no ?? '-'}
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>S.No</th>
|
||||
<th>Field Name</th>
|
||||
<th>Old Value</th>
|
||||
<th>New Value</th>
|
||||
<th>Updated By</th>
|
||||
<th>Date/Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="app-table-body">`;
|
||||
|
||||
// ============= MAIN INVOICE HISTORY =============
|
||||
invoiceArray.forEach((row, index) => {
|
||||
table += `
|
||||
<tr>
|
||||
<td>${index + 1}</td>
|
||||
<td>${row.field_name.replace(/_/g, " ")}</td>
|
||||
<td>${row.old_value ?? '-'}</td>
|
||||
<td>${row.new_value ?? '-'}</td>
|
||||
<td>${row.created_name ?? '-'}</td>
|
||||
<td>${formatDate(row.created_at)}</td>
|
||||
</tr>`;
|
||||
});
|
||||
|
||||
// ============= CHILD ITEMS HISTORY =============
|
||||
if (childArray.length > 0) {
|
||||
table += `
|
||||
<tr>
|
||||
<th colspan="6" style="font-size:18px; font-weight:600;background:#eee;">
|
||||
Invoice Item Changes
|
||||
</th>
|
||||
</tr>`;
|
||||
|
||||
childArray.forEach((row, index) => {
|
||||
table += `
|
||||
<tr>
|
||||
<td>${index + 1}</td>
|
||||
<td>${row.field_name.replace(/_/g, " ")}</td>
|
||||
<td>${row.old_value ?? '-'}</td>
|
||||
<td>${row.new_value ?? '-'}</td>
|
||||
<td>${row.created_name ?? '-'}</td>
|
||||
<td>${formatDate(row.created_at)}</td>
|
||||
</tr>`;
|
||||
});
|
||||
}
|
||||
|
||||
table += `
|
||||
</tbody>
|
||||
</table>
|
||||
</div>`;
|
||||
|
||||
container.html(table); // Render final table
|
||||
}
|
||||
else {
|
||||
// ---------- NO DATA ----------
|
||||
container.html(`
|
||||
<div class="d-flex justify-content-center align-items-center"
|
||||
style="height: 50vh; width:100%;">
|
||||
<p class="text-center font-color-black">
|
||||
<i>No history found</i>
|
||||
</p>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
$("#auditingModal").modal("show");
|
||||
},
|
||||
|
||||
error: function(xhr) {
|
||||
$(".history-container").html(`
|
||||
<div class="d-flex justify-content-center align-items-center"
|
||||
style="height: 50vh; width:100%;">
|
||||
<p class="text-center font-color-black"><i>No Data Found</i></p>
|
||||
</div>
|
||||
`);
|
||||
$("#auditingModal").modal("show");
|
||||
console.error(xhr.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------- DATE FORMAT -----------------
|
||||
function formatDate(dateString) {
|
||||
if (!dateString) return "-";
|
||||
let dt = new Date(dateString);
|
||||
|
||||
let dateStr = dt.toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
year: "numeric"
|
||||
});
|
||||
|
||||
let timeStr = dt.toLocaleTimeString("en-US", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: true
|
||||
}).toLowerCase();
|
||||
|
||||
return `${dateStr} ${timeStr}`;
|
||||
}
|
||||
|
||||
function getExportFileName() {
|
||||
let d = new Date();
|
||||
let day = String(d.getDate()).padStart(2, '0');
|
||||
let month = String(d.getMonth() + 1).padStart(2, '0');
|
||||
let year = d.getFullYear();
|
||||
return `Invoice Policies List ${day}-${month}-${year}`;
|
||||
}
|
||||
</script>
|
||||
543
app/Views/invoice_policy_mapping_add.php
Executable file
543
app/Views/invoice_policy_mapping_add.php
Executable file
@ -0,0 +1,543 @@
|
||||
<style>
|
||||
/* ---- CSS cleaned/optimized ---- */
|
||||
.table th, .table td { padding: 8px; }
|
||||
table.dataTable tbody td { padding: 4px 4px !important; }
|
||||
.col-12 { max-width: 98% !important; }
|
||||
.dataTables_filter { position: absolute; }
|
||||
.column-header { margin-right: 10px; }
|
||||
.filter-inline { display: flex; align-items: center; gap: 10px; }
|
||||
.filter-inline input[type="date"] { padding: 8px 12px; border:1px solid #ddd; border-radius:4px; font-size:13px; background:white; cursor:pointer; }
|
||||
.filter-inline input[type="date"]:focus { outline:none; border-color:#00a9a3; }
|
||||
|
||||
.icon-btn { width:36px; height:36px; border:1px solid #ddd; background:white; border-radius:4px; cursor:pointer; display:flex; align-items:center; justify-content:center; transition:all 0.2s; }
|
||||
.icon-btn:hover { background:#f5f5f5; border-color:#00a9a3; }
|
||||
.icon-btn svg { width:18px; height:18px; fill:#666; }
|
||||
.icon-btn:hover svg { fill:#00a9a3; }
|
||||
|
||||
.policy-list { border:1px solid #e0e0e0; border-radius:4px; overflow:hidden; margin-top:0; }
|
||||
.policy-header { background:#f8f9fa; padding:12px 15px; font-weight:600; border-bottom:1px solid #e0e0e0; display:flex; align-items:center; font-size:14px; }
|
||||
.policy-header input[type="checkbox"] { width:18px; height:18px; margin-right:10px; cursor:pointer; }
|
||||
.policy-item { border-bottom:1px solid #e0e0e0; padding:12px 15px; display:flex; align-items:center; background:white; transition:background 0.2s; }
|
||||
.policy-item:hover { background:#f9f9f9; }
|
||||
.policy-item:last-child { border-bottom:none; }
|
||||
.policy-item.selected { background:#e8f5f4; }
|
||||
.policy-checkbox { width:18px; height:18px; margin-right:15px; cursor:pointer; }
|
||||
.policy-info { flex:1; display:flex; justify-content:space-between; align-items:center; }
|
||||
.policy-details { flex:1; }
|
||||
.policy-number { font-weight:600; color:#333; margin-bottom:4px; font-size:14px; }
|
||||
.policy-meta { color:#666; font-size:13px; }
|
||||
.policy-amount { font-size:16px; font-weight:600; color:#00a9a3; margin-left:20px; }
|
||||
|
||||
.badge { display:inline-block; padding:4px 10px; border-radius:12px; font-size:12px; font-weight:600; margin-left:10px; }
|
||||
.badge-selected { background:#00a9a3; color:white; }
|
||||
|
||||
.summary-bar { position:fixed; bottom:0; left:0; right:0; width:100%; background:white; border-top:2px solid #00a9a3; padding:15px 30px; display:none; box-shadow:0 -2px 10px rgba(0,0,0,0.1); z-index:100; }
|
||||
.summary-bar.show { display:flex; justify-content:flex-start; align-items:center; }
|
||||
.summary-info { margin-left:auto; margin-right:0; display:flex; gap:40px; align-items:center; }
|
||||
.summary-item { display:flex; flex-direction:column; }
|
||||
.summary-label { font-size:12px; color:#666; margin-bottom:2px; }
|
||||
.summary-value { font-size:18px; font-weight:600; color:#00a9a3; }
|
||||
.summary-actions { display:flex; gap:10px; margin-left:auto; }
|
||||
.btn-icon { background:#008b8b; border:none; border-radius:50%; width:32px; height:32px; display:flex; align-items:center; justify-content:center; cursor:pointer; }
|
||||
.btn-icon i { color:#fff !important; font-size:18px; }
|
||||
.btn-icon:hover { background:#00a3a3; }
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width:1024px){ .filter-inline{flex-wrap:wrap;} .filter-inline input[type="date"]{font-size:12px;padding:6px 8px;} }
|
||||
@media (max-width:768px){
|
||||
.section-header{flex-direction:column;gap:10px;align-items:flex-start;}
|
||||
.header-right{width:100%;justify-content:space-between;}
|
||||
.filter-inline{flex:1;}
|
||||
.summary-bar{left:0;flex-direction:column;gap:15px;padding:15px;}
|
||||
.summary-info{width:100%;justify-content:space-around;}
|
||||
.summary-actions{width:100%;}
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- ---- HTML structure remains mostly same ---- -->
|
||||
<div class="row" id="invoices_details">
|
||||
<div class="col-12">
|
||||
<div id="invoices_accordion" class="ml-3">
|
||||
<div class="card mb-1">
|
||||
<h4 class="m-1">
|
||||
<a href="#" onclick="history.back(); return false;">
|
||||
<i style="font-size: 18px;" class="mdi mdi-chevron-left" title="Back"></i>
|
||||
</a>
|
||||
<span>Invoice Details</span>
|
||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseOne"
|
||||
aria-expanded="true">
|
||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
</h4>
|
||||
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#invoices_accordion">
|
||||
<div class="card-body" style="padding-bottom: unset;">
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-3">
|
||||
<label>Invoice Number </label>
|
||||
<input class="form-control" type="text" id="invoiceNo" placeholder="Auto-generated" readonly>
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<label>Invoice Date <span class="text-danger"></span></label>
|
||||
<input class="form-control" type="date" id="invoiceDate" required value="" >
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<label for="agents"> Agents <span class="text-danger"></span></label>
|
||||
|
||||
<select class="form-control" id="agentSelect" name="agents_id" onchange="loadPolicies()" required>
|
||||
<option value="">Select Agent</option>
|
||||
<?php
|
||||
if(isset($agents) && count($agents)) {
|
||||
foreach($agents as $agent): ?>
|
||||
<option value="<?= $agent['id'] ?>">
|
||||
<?= $agent['name'] ?> - <?= $agent['agent_code'] ?>
|
||||
</option>
|
||||
<?php endforeach;
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<label>Policy Till Date<span class="text-danger"></span></label>
|
||||
<input class="form-control" type="date" id="policyTillDate" required onchange="loadPolicies()" max="<?= date('Y-m-d') ?>" >
|
||||
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<input class="form-control" type="hidden" id="invoiceID" required value="<?= isset($invoice['id']) ? $invoice['id'] : '' ?>" >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="payouts_details">
|
||||
<div class="col-12">
|
||||
<div id="policy_accordion" class="ml-3">
|
||||
<div class="card mb-1">
|
||||
<div class="row align-items-center m-1" id="policy_filter">
|
||||
<div class="col d-flex align-items-center">
|
||||
<h4 class="mb-0">
|
||||
Policy Selection
|
||||
<span id="selectedCount" class="badge badge-selected" style="display:none;">0 selected</span>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="col-auto d-flex align-items-center">
|
||||
<input type="date" class="form-control mr-2" id="fromDate" placeholder="From Date" title="From Date" style="width:170px;">
|
||||
<input type="date" class="form-control mr-2" id="toDate" placeholder="To Date" title="To Date" style="width:170px;">
|
||||
<button type="button" class="btn-icon mr-1" title="Reset Filters" onclick="resetFilters()"><i class="mdi mdi-refresh"></i></button>
|
||||
<button type="button" class="btn-icon mr-1" title="Search" onclick="searchPolicies()"><i class="mdi mdi-magnify"></i></button>
|
||||
</div>
|
||||
|
||||
<a id="toggleIcon" class="text-dark float-right" data-toggle="collapse" href="#collapseTwo" aria-expanded="true">
|
||||
<i id="icon" class="mdi mdi-chevron-down mr-1 text-primary" style="font-size: 28px;"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="collapseTwo" class="collapse show" aria-labelledby="headingTwo" data-parent="#policy_accordion">
|
||||
|
||||
<div class="card-body" style="padding-top: unset !important; border: white !important; background: unset !important;">
|
||||
<div>
|
||||
<div>
|
||||
<table data-custom-table-css="table" class="table table-striped mb-0 nowrap" cellspacing="0" id="tickets-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th style="text-align:center;"><input type="checkbox" class="CB" id="selectAll" onchange="toggleSelectAll(this)"></th>
|
||||
<th><div class="column-header">Policy No</div></th> <!-- 2 -->
|
||||
<th><div class="column-header">Customer</div></th> <!-- 3 -->
|
||||
<th><div class="column-header">Premium</div></th> <!-- 4 -->
|
||||
<th><div class="column-header">Policy Issues Date</div></th> <!-- 5 -->
|
||||
<th><div class="column-header">Commission Amount</div></th> <!-- 6 -->
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody id="ticketListBody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="summaryBar" class="summary-bar">
|
||||
<div class="summary-info">
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">Selected Policies</span>
|
||||
<span class="summary-value" id="totalPolicies">0</span>
|
||||
</div>
|
||||
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">Total Commission</span>
|
||||
<span class="summary-value" id="totalAmount">₹0.00</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="summary-actions">
|
||||
<button type="button" class="btn app-btn-outline-primary C" onclick="clearSelection()">Clear</button>
|
||||
<button type="button" class="btn app-btn-secondary S" onclick="saveInvoice()">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- end col -->
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
let selectedPolicies = new Set();
|
||||
let filteredPolicies = [];
|
||||
let allPolicies = <?php echo json_encode($payouts); ?> || [];
|
||||
|
||||
// Helper
|
||||
function getEl(id) { return document.getElementById(id); }
|
||||
|
||||
$(document).ready(function(){
|
||||
$('#agentSelect').select2 && $('#agentSelect').select2();
|
||||
const today = new Date().toISOString().split("T")[0];
|
||||
|
||||
if (getEl('agentSelect')) getEl('agentSelect').value = "";
|
||||
if (getEl('invoiceNo')) getEl('invoiceNo').value = "<?= $invoice_number ?>";
|
||||
if (getEl('invoiceDate')) getEl('invoiceDate').max = today;
|
||||
if (getEl('invoiceDate')) getEl('invoiceDate').valueAsDate = new Date();
|
||||
if (getEl('policyTillDate')) getEl('policyTillDate').max = today;
|
||||
if (getEl('policyTillDate')) getEl('policyTillDate').valueAsDate = new Date();
|
||||
|
||||
// Initial empty DataTable so buttons/search are available even before loading policies
|
||||
if ($.fn.DataTable.isDataTable('#tickets-table')) {
|
||||
$('#tickets-table').DataTable().clear().destroy();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$('#tickets-table').DataTable({
|
||||
dom:
|
||||
"<'row'<'col-sm-6'f><'col-sm-6 text-right'B>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
buttons: [
|
||||
{
|
||||
extend: 'collection',
|
||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
className: 'btn app-btn-secondary ',
|
||||
buttons: [
|
||||
{
|
||||
extend: 'csv',
|
||||
text: '<i class="mdi mdi-file-delimited"></i><span class="btn-custom"> CSV </span>',
|
||||
className: 'app-btn-primary',
|
||||
title: getExportFileName(),
|
||||
exportOptions: {columns: ':not(:first-child)'}
|
||||
},
|
||||
{
|
||||
extend: 'excel',
|
||||
text: '<i class="mdi mdi-file-excel"></i><span class="btn-custom"> EXCEL </span>',
|
||||
className: 'app-btn-primary',
|
||||
title: getExportFileName(),
|
||||
filename: getExportFileName(),
|
||||
exportOptions: {columns: ':not(:first-child)'}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
language: {
|
||||
search: `
|
||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
|
||||
_INPUT_
|
||||
<i class="mdi mdi-magnify datatable-search-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666;"></i>
|
||||
<i class="mdi mdi-close-circle datatable-clear-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
|
||||
</div>`,
|
||||
searchPlaceholder: "Search",
|
||||
emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
||||
},
|
||||
paging: false,
|
||||
ordering: false,
|
||||
});
|
||||
});
|
||||
|
||||
// MAIN: load and render policies for selected agent / date
|
||||
function loadPolicies() {
|
||||
let agentEl = getEl('agentSelect');
|
||||
let policyTillDateEl = getEl('policyTillDate');
|
||||
let list = getEl('ticketListBody');
|
||||
|
||||
if (!list) return;
|
||||
|
||||
let agentId = agentEl ? agentEl.value : '';
|
||||
let policyTillDate = policyTillDateEl ? policyTillDateEl.value : '';
|
||||
|
||||
selectedPolicies.clear();
|
||||
|
||||
if (!agentId || agentId === '' || agentId === '0') {
|
||||
// Destroy DataTable so table returns to normal
|
||||
if ($.fn.DataTable.isDataTable('#tickets-table')) {
|
||||
$('#tickets-table').DataTable().clear().destroy();
|
||||
}
|
||||
|
||||
list.innerHTML = `<tr><td colspan="6" class="text-center text-danger">Please select an agent</td></tr>`;
|
||||
reinitDataTable();
|
||||
updateSummary();
|
||||
return;
|
||||
}
|
||||
|
||||
filteredPolicies = allPolicies.filter(p => String(p.agentId) === String(agentId));
|
||||
|
||||
let rows = '';
|
||||
filteredPolicies.forEach(p => {
|
||||
const idStr = String(p.id);
|
||||
const checked = selectedPolicies.has(idStr) ? 'checked' : '';
|
||||
rows += `
|
||||
<tr>
|
||||
<td style="text-align:center;">
|
||||
<input type="checkbox" class="policy-checkbox CB"
|
||||
value="${idStr}" ${checked}
|
||||
onchange="togglePolicy(this.value, this.checked)">
|
||||
</td>
|
||||
<td>${p.policyNo || ''}</td>
|
||||
<td>${p.customer || ''}</td>
|
||||
<td>₹${Number(p.premium || 0).toFixed(2)}</td>
|
||||
<td>${p.date || ''}</td>
|
||||
<td>₹${Number(p.commission || 0).toFixed(2)}</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
// Destroy DataTable BEFORE inserting rows
|
||||
if ($.fn.DataTable.isDataTable('#tickets-table')) {
|
||||
$('#tickets-table').DataTable().clear().destroy();
|
||||
}
|
||||
|
||||
// Insert rows
|
||||
list.innerHTML = rows;
|
||||
|
||||
// Reinitialize AFTER writing rows
|
||||
reinitDataTable();
|
||||
updateSelectAllCheckbox();
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
|
||||
// DataTable re-init helper (destroy + create)
|
||||
function reinitDataTable() {
|
||||
if ($.fn.DataTable.isDataTable('#tickets-table')) {
|
||||
try {
|
||||
$('#tickets-table').DataTable().clear().destroy();
|
||||
} catch (e) {
|
||||
// ignore destroy errors
|
||||
}
|
||||
}
|
||||
|
||||
$('#tickets-table').DataTable({
|
||||
dom:
|
||||
"<'row'<'col-sm-6'f><'col-sm-6 text-right'B>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
buttons: [
|
||||
{
|
||||
extend: 'collection',
|
||||
text: '<span class=" btn-custom"> Export </span><i class="mdi mdi-menu-down"></i>',
|
||||
className: 'btn app-btn-secondary ',
|
||||
buttons: [
|
||||
{
|
||||
extend: 'csv',
|
||||
text: '<i class="mdi mdi-file-delimited"></i><span class="btn-custom"> CSV </span>',
|
||||
className: 'app-btn-primary',
|
||||
title: getExportFileName(),
|
||||
exportOptions: {columns: ':not(:first-child)'}
|
||||
},
|
||||
{
|
||||
extend: 'excel',
|
||||
text: '<i class="mdi mdi-file-excel"></i><span class="btn-custom"> EXCEL </span>',
|
||||
className: 'app-btn-primary',
|
||||
title: getExportFileName(),
|
||||
filename: getExportFileName(),
|
||||
exportOptions: {columns: ':not(:first-child)'}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
language: {
|
||||
search: `
|
||||
<div class="datatable-search-wrapper" style="position:relative; display:inline-block;">
|
||||
_INPUT_
|
||||
<i class="mdi mdi-magnify datatable-search-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666;"></i>
|
||||
<i class="mdi mdi-close-circle datatable-clear-icon"
|
||||
style="position:absolute; right:10px; top:50%; transform:translateY(-50%); color:#666; display:none;"></i>
|
||||
</div>`,
|
||||
searchPlaceholder: "Search",
|
||||
emptyTable: '<div class="text-center text-muted">No Data found</div>'
|
||||
},
|
||||
paging: false,
|
||||
ordering: false,
|
||||
// After init, align first column
|
||||
initComplete: function() {
|
||||
$('#tickets-table tbody tr td:first-child').css('text-align', 'center');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Toggle single policy selection (called from checkbox onchange)
|
||||
function togglePolicy(policyId, checked) {
|
||||
policyId = String(policyId);
|
||||
if (checked === undefined) {
|
||||
// if called without the checked param, toggle based on presence
|
||||
if (selectedPolicies.has(policyId)) selectedPolicies.delete(policyId);
|
||||
else selectedPolicies.add(policyId);
|
||||
} else {
|
||||
if (checked) selectedPolicies.add(policyId);
|
||||
else selectedPolicies.delete(policyId);
|
||||
}
|
||||
// update UI elements that reflect selection
|
||||
updateSelectAllCheckbox();
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
// Select / deselect all currently filtered policies
|
||||
function toggleSelectAll(sourceCheckbox) {
|
||||
if (!filteredPolicies || filteredPolicies.length === 0) return;
|
||||
if (sourceCheckbox.checked) {
|
||||
filteredPolicies.forEach(p => selectedPolicies.add(String(p.id)));
|
||||
} else {
|
||||
filteredPolicies.forEach(p => selectedPolicies.delete(String(p.id)));
|
||||
}
|
||||
// update all visible checkboxes to match selection
|
||||
document.querySelectorAll('#ticketListBody .policy-checkbox').forEach(cb => {
|
||||
cb.checked = selectedPolicies.has(cb.value);
|
||||
});
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
// Update header select-all checkbox state
|
||||
function updateSelectAllCheckbox() {
|
||||
const selectAllCheckbox = getEl('selectAll');
|
||||
if (!selectAllCheckbox) return;
|
||||
const allSelected = filteredPolicies.length > 0 && filteredPolicies.every(p => selectedPolicies.has(String(p.id)));
|
||||
selectAllCheckbox.checked = !!allSelected;
|
||||
}
|
||||
|
||||
// Update summary UI (footer/summaryBar)
|
||||
function updateSummary() {
|
||||
const summaryBar = getEl('summaryBar');
|
||||
const selectedCountBadge = getEl('selectedCount');
|
||||
const totalPoliciesEl = getEl('totalPolicies');
|
||||
const totalAmountEl = getEl('totalAmount');
|
||||
|
||||
if (!totalPoliciesEl || !totalAmountEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedPolicies.size === 0) {
|
||||
if (summaryBar) summaryBar.classList && summaryBar.classList.remove('show');
|
||||
if (selectedCountBadge) selectedCountBadge.style.display = 'none';
|
||||
totalPoliciesEl.textContent = '0';
|
||||
totalAmountEl.textContent = '₹0.00';
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedCountBadge) {
|
||||
selectedCountBadge.style.display = 'inline-block';
|
||||
selectedCountBadge.textContent = `${selectedPolicies.size} selected`;
|
||||
}
|
||||
if (summaryBar) summaryBar.classList && summaryBar.classList.add('show');
|
||||
|
||||
const totalAmount = filteredPolicies
|
||||
.filter(p => selectedPolicies.has(String(p.id)))
|
||||
.reduce((sum, p) => sum + Number(p.commission || 0), 0);
|
||||
|
||||
totalPoliciesEl.textContent = String(selectedPolicies.size);
|
||||
totalAmountEl.textContent = `₹${totalAmount.toFixed(2)}`;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Clear selection
|
||||
function clearSelection() {
|
||||
if (!confirm('Are you sure you want to clear the selection?')) return;
|
||||
selectedPolicies.clear();
|
||||
// uncheck all checkboxes in view
|
||||
document.querySelectorAll('#ticketListBody .policy-checkbox').forEach(cb => cb.checked = false);
|
||||
updateSelectAllCheckbox();
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
// Reset filters (keeps agent selection; clear others)
|
||||
function resetFilters() {
|
||||
['fromDate','toDate','from_date','to_date','policyTillDate'].forEach(id => {
|
||||
const el = getEl(id);
|
||||
if (el) el.value = '';
|
||||
});
|
||||
loadPolicies();
|
||||
}
|
||||
|
||||
// Save invoice (AJAX)
|
||||
function saveInvoice() {
|
||||
const agentId = getEl('agentSelect')?.value || '';
|
||||
const invoiceNo = getEl('invoiceNo')?.value || '';
|
||||
const invoiceDate = getEl('invoiceDate')?.value || '';
|
||||
const policyTillDate = getEl('policyTillDate')?.value || '';
|
||||
const invoiceID = '';
|
||||
|
||||
if (!agentId || agentId === '0') return toastr.warning('Please select an agent', 'Required');
|
||||
if (!invoiceDate) return toastr.warning('Please select invoice date', 'Required');
|
||||
if (selectedPolicies.size === 0) return toastr.warning('Please select at least one policy', 'Required');
|
||||
|
||||
|
||||
const selectedPolicyData = filteredPolicies.filter(p => selectedPolicies.has(String(p.id)));
|
||||
|
||||
let totalAmount = 0;
|
||||
const policies = selectedPolicyData.map(p => {
|
||||
let finalAmount = Number(p.commission || 0);
|
||||
totalAmount += finalAmount;
|
||||
return {
|
||||
policy_id: p.id,
|
||||
policy_no: p.policyNo,
|
||||
commission_amount: finalAmount
|
||||
};
|
||||
});
|
||||
|
||||
const invoiceData = {
|
||||
invoice_id: invoiceID,
|
||||
invoice_no: invoiceNo,
|
||||
agent_id: agentId,
|
||||
invoice_date: invoiceDate,
|
||||
invoice_amount: totalAmount,
|
||||
policies: policies
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: '<?= base_url('payout/invoices/save') ?>',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(invoiceData),
|
||||
contentType: 'application/json',
|
||||
success: function(response) {
|
||||
// toastr.success(`Invoice created successfully!<br>Invoice No: ${invoiceNo}`, 'Success');
|
||||
toastr.success(response.message, 'Success');
|
||||
window.location.href = '<?= base_url('payout/list') ?>';
|
||||
},
|
||||
error: function() {
|
||||
toastr.error(response.message, 'Invoice Error');
|
||||
// toastr.error('An error occurred while creating the invoice. Please try again.','Invoice Error');
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getExportFileName() {
|
||||
let d = new Date();
|
||||
let day = String(d.getDate()).padStart(2, '0');
|
||||
let month = String(d.getMonth() + 1).padStart(2, '0');
|
||||
let year = d.getFullYear();
|
||||
return `Invoice Policies List ${day}-${month}-${year}`;
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
@ -1368,7 +1368,7 @@
|
||||
let test_mail_list = $('#common_mail').val();
|
||||
|
||||
|
||||
let test_mail = test_mail_list.split(',')[0] ?? '';
|
||||
let test_mail = test_mail_list.split(',')[0] ?? ''; //REF:SVM
|
||||
|
||||
if (test_mail !== null || test_mail !== "") {
|
||||
|
||||
@ -1400,30 +1400,19 @@
|
||||
type: "POST",
|
||||
data:{
|
||||
template_id,
|
||||
test_mail,
|
||||
test_mail_list
|
||||
test_mail_list,
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
|
||||
console.log('Test mail send function response', res)
|
||||
console.log('Test mail send function response', res);
|
||||
|
||||
$('.loader').fadeOut();
|
||||
$('.loader-mask').delay(350).fadeOut('slow');
|
||||
|
||||
if (res.status == true) {
|
||||
|
||||
let respond = res.respond;
|
||||
console.log('Parsed respond', respond)
|
||||
|
||||
if (respond.status == 'success') {
|
||||
toastr.success(respond.message, 'SUCCESS')
|
||||
} else {
|
||||
toastr.warning(respond.message, 'WARNING')
|
||||
}
|
||||
|
||||
if (res.status === true) {
|
||||
toastr.success(res.message, 'SUCCESS');
|
||||
} else {
|
||||
toastr.warning('Failed to sent mail', 'WARNING')
|
||||
toastr.warning('Failed to send mail', 'WARNING');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
|
||||
@ -182,11 +182,18 @@
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<!-- <div class="row" style="padding-bottom: 10px;">
|
||||
<div class="row" style="padding-bottom: 10px;">
|
||||
<div class="col-6" style="align-self: center;">
|
||||
<h4 style="position: relative;">Payout List <span id="payout_title"></span></h4>
|
||||
<!-- <h4 style="position: relative;">Payout List <span id="payout_title"></span></h4> -->
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="col-5" style="align-self: right;"></div>
|
||||
<div class="col-1" style="align-self: right;">
|
||||
<button class="btn app-btn-primary mr-2" type="button"
|
||||
onclick="window.location.href='<?= base_url('payout/invoices?type=add') ?>'">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table data-custom-table-css="table" id="scroll-horizontal-datatable" class="table w-100 nowrap">
|
||||
<thead class="bg-light">
|
||||
@ -219,8 +226,8 @@
|
||||
<a href="javascript: void(0);" class="dropdown-toggle arrow-none btn btn-light btn-sm" data-toggle="dropdown"aria-expanded="false"><i class="mdi mdi-dots-horizontal"></i></a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" onclick="fetchUtrDetails(<?= $row['id'] ?>, '<?= $row['invoice_no'] ?>')"><i class="mdi mdi-bank-transfer mr-2 text-muted font-18 vertical-middle"></i>UTR</a>
|
||||
<a href="<?= base_url('payout/invoices/save?type="edit"') ?>" class="dropdown-item"><i class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit</a>
|
||||
<a href="<?= base_url('payout/invoices/save?type="adjustment"') ?>" class="dropdown-item"><i class="mdi mdi-tune mr-2 text-muted font-18 vertical-middle"></i>Adjustment</a>
|
||||
<a href="<?= base_url('payout/invoices?type=edit&id=' . $row['id']) ?>" class="dropdown-item"><i class="mdi mdi-pencil mr-2 text-muted font-18 vertical-middle"></i>Edit</a>
|
||||
<a href="<?= base_url('payout/invoices?type=adjustment&id=' . $row['id']) ?>" class="dropdown-item"><i class="mdi mdi-tune mr-2 text-muted font-18 vertical-middle"></i>Adjustment</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@ -489,7 +489,8 @@ table.dataTable thead th {
|
||||
if (ticketsTable.length) {
|
||||
ticketsTable.DataTable({
|
||||
scrollX: true,
|
||||
dom: "<'row'<'col-sm-1'f><'col-sm-11 text-right'B>>" + // Filter left, buttons right
|
||||
// dom: "<'row'<'col-sm-1'f><'col-sm-11 text-right'B>>" + // Filter left, buttons right
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>", // Pagination at the bottom, without page length selector
|
||||
buttons: [
|
||||
|
||||
@ -561,7 +561,8 @@ $(document).ready(function() {
|
||||
if (ticketsTable.length) {
|
||||
ticketsTable.DataTable({
|
||||
scrollX: true,
|
||||
dom: "<'row'<'col-sm-1'f><'col-sm-11 text-right'B>>" + // Filter left, buttons right
|
||||
// dom: "<'row'<'col-12'f><'col-sm-11 text-right'B>>" + // Filter left, buttons right
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>", // Pagination at the bottom, without page length selector
|
||||
buttons: [
|
||||
|
||||
@ -43,7 +43,7 @@ table.dataTable tbody td {
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<div class="table-responsive">
|
||||
<table data-custom-table-css="table" class="table mb-0 nowrap w-100 table-centered" cellspacing="0" id="tickets-table">
|
||||
<table data-custom-table-css="table" class="table mb-0 nowrap w-100 table-centered" cellspacing="0" id="thz-table">
|
||||
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
@ -315,7 +315,7 @@ table.dataTable tbody td {
|
||||
// Datatable document ready
|
||||
$(document).ready(function() {
|
||||
|
||||
var ticketsTable = $('#tickets-table');
|
||||
var ticketsTable = $('#thz-table');
|
||||
|
||||
if (ticketsTable.length) {
|
||||
ticketsTable.DataTable({
|
||||
@ -349,7 +349,7 @@ table.dataTable tbody td {
|
||||
orthogonal: 'sort'
|
||||
},
|
||||
className: 'app-btn-primary ',
|
||||
title: 'Tickets'
|
||||
title: 'Tickets',
|
||||
sheetName: 'Tickets',
|
||||
}
|
||||
]
|
||||
@ -374,6 +374,12 @@ table.dataTable tbody td {
|
||||
} else {
|
||||
console.error("Table not found.");
|
||||
}
|
||||
|
||||
$(document).on('click', '.datatable-clear-icon', function () {
|
||||
const input = $(this).closest('.datatable-search-wrapper').find('input');
|
||||
input.val('').trigger('input');
|
||||
$('#thz-table').DataTable().search('').draw();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
<!-- Search by Mobile Number -->
|
||||
<div>
|
||||
<h4 onclick="toggleAccordion('mobileAccordion')">
|
||||
Search by Mobile Number
|
||||
Search by Employee ID / Mobile Number
|
||||
<span class="arrow" id="mobileArrow">➤</span>
|
||||
</h4>
|
||||
<div class="accordion-content" id="mobileAccordion">
|
||||
|
||||
@ -1,3 +1,54 @@
|
||||
<style>
|
||||
/* ----- Modal Size: 25% width + 25% height ----- */
|
||||
#New_Ticket_modal .modal-dialog {
|
||||
width: 15%;
|
||||
max-width: 15%;
|
||||
height: 25%;
|
||||
max-height: 25%;
|
||||
margin: auto !important;
|
||||
}
|
||||
|
||||
|
||||
/* Make modal-content fill the dialog & allow flex layout */
|
||||
#New_Ticket_modal .modal-content {
|
||||
height: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Scroll only inside body */
|
||||
#New_Ticket_modal .modal-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* Footer visible always */
|
||||
#New_Ticket_modal .modal-footer {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* ----- Small Height Screens Fix ----- */
|
||||
@media (max-height: 300px) {
|
||||
|
||||
/* Make modal usable but still centered */
|
||||
#New_Ticket_modal .modal-dialog {
|
||||
width: 50%;
|
||||
max-width: 50%;
|
||||
height: 70%;
|
||||
max-height: 70%;
|
||||
}
|
||||
|
||||
/* Adjust body scroll area */
|
||||
#New_Ticket_modal .modal-body {
|
||||
max-height: calc(80vh - 120px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="modal fade" id="New_Ticket_modal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
|
||||
@ -15,6 +15,7 @@ table.dataTable tbody td {
|
||||
.col-12 {
|
||||
max-width: 98% !important;
|
||||
}
|
||||
.column-header {margin-right: 10px;}
|
||||
|
||||
.readonly-select {
|
||||
pointer-events: none;
|
||||
@ -74,14 +75,14 @@ table.dataTable tbody td {
|
||||
<table data-custom-table-css="table" id="scroll-horizontal-datatable" class="table w-100 nowrap">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="font-weight-medium">S.No </th>
|
||||
<th class="font-weight-medium">Owner Type </th>
|
||||
<th class="font-weight-medium">Owner Name </th>
|
||||
<th class="font-weight-medium">RC Book No </th>
|
||||
<th class="font-weight-medium">Vehicle No </th>
|
||||
<th class="font-weight-medium">Vehicle type </th>
|
||||
<th class="font-weight-medium">Description </th>
|
||||
<th class="font-weight-medium">Action </th>
|
||||
<th class="font-weight-medium"><div class="column-header">S.No </div></th>
|
||||
<th class="font-weight-medium"><div class="column-header">Owner Type </div></th>
|
||||
<th class="font-weight-medium"><div class="column-header">Owner Name </div></th>
|
||||
<th class="font-weight-medium"><div class="column-header">RC Book No </div></th>
|
||||
<th class="font-weight-medium"><div class="column-header">Vehicle No </div></th>
|
||||
<th class="font-weight-medium"><div class="column-header">Vehicle type </div></th>
|
||||
<th class="font-weight-medium"><div class="column-header">Description </div></th>
|
||||
<th class="font-weight-medium"><div class="column-header">Action </div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@ -465,9 +466,11 @@ $(document).ready(function() {
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#scroll-horizontal-datatable').DataTable({
|
||||
var ticketsTable = $('#scroll-horizontal-datatable');
|
||||
if (ticketsTable.length) {
|
||||
ticketsTable.DataTable({
|
||||
scrollX: true,
|
||||
dom: "<'row'<'col-sm-2'f><'col-sm-10 text-right'B>>" +
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
buttons: [
|
||||
@ -524,7 +527,10 @@ $(document).ready(function() {
|
||||
paging: true,
|
||||
pageLength: 10,
|
||||
order: [[0, 'desc']]
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.error("Table atet found.");
|
||||
}
|
||||
});
|
||||
|
||||
function getEditVehicleDetailsData(input) {
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
.dataTables_filter {
|
||||
position: absolute;
|
||||
}
|
||||
.column-header {margin-right: 10px;}
|
||||
</style>
|
||||
|
||||
<!-- End ADD and EDIT Page HTML -->
|
||||
@ -12,10 +13,10 @@
|
||||
<table data-custom-table-css="table" class="table table-sm m-0 table-centered dt-responsive nowrap w-100" cellspacing="a" id="user-table">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="font-weight-medium">S.No.</th>
|
||||
<th class="font-weight-medium">Vehicle Type</th>
|
||||
<th class="font-weight-medium"><div class="column-header">S.No.</div></th>
|
||||
<th class="font-weight-medium"><div class="column-header">Vehicle Type</div></th>
|
||||
<!-- <th class="font-weight-medium">Status</th> -->
|
||||
<th class="font-weight-medium">Action</th>
|
||||
<th class="font-weight-medium"><div class="column-header">Action</div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -94,7 +95,8 @@
|
||||
var ticketsTable = $('#user-table');
|
||||
ticketsTable.DataTable({
|
||||
scrollX: true,
|
||||
dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right
|
||||
// dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right
|
||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>", // Pagination at the bottom, without page length selector
|
||||
buttons: [
|
||||
|
||||
Loading…
Reference in New Issue
Block a user