nhance/app/Views/invoice_policy_mapping.php

1150 lines
49 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>
<style>
.invoice-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
}
#invoiceModal .modal-body {
max-height: 520px; /* adjust as needed */
overflow-y: auto;
}
</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 for="pos"> POS <span class="text-danger"></span></label>
<select class="form-control" id="posSelect" name="pos_id" onchange="loadPolicies()" required>
<option value="">Select pos</option>
<?php
if(isset($pos_list) && count($pos_list)) {
foreach($pos_list as $pos): ?>
<option value="<?= $pos['id'] ?>"
<?= isset($invoice['pos_id']) && $invoice['pos_id'] == $pos['id'] ? 'selected' : '' ?>>
<?= $pos['name'] ?> - <?= $pos['pos_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 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>
<div class="summary-item">
<button type="button" class="btn-icon mr-1 text-warning" onclick="fetchPreviewInvoiceDetails(<?= isset($invoice['id']) ? $invoice['id'] : '' ?>, '<?= isset($invoice['invoice_no']) ? $invoice['invoice_no'] : '' ?>')" data-toggle="tooltip" title="Preview Invoice"><i class="mdi mdi-eye"></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>
<!-- Invoice Preview Modal -->
<div class="modal fade" id="invoiceModal" tabindex="-1" role="dialog" aria-hidden="true" aria-modal="true" data-bs-backdrop="static">
<div class="modal-dialog modal-full-width">
<div class="modal-content">
<div class="modal-header" style="background-color: gainsboro;">
<h5 class="modal-title" id="invoiceModalLabel">
Invoice Preview <span id="invoice_heading"></span>
</h5>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body" id="invoice_modal_body">
<input type="hidden" id="row_invoice_id">
<!-- Action Buttons -->
<div class="invoice-actions">
<button class="btn btn-success btn-sm" onclick="downloadInvoicePdf()"><i class="mdi mdi-download"></i> Download PDF</button>
<!-- <button class="btn btn-info btn-sm" onclick="printInvoice()"><i class="mdi mdi-printer"></i> Print </button> -->
</div>
<hr>
<!-- Invoice Preview Container -->
<div id="invoicePreview"></div>
</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/POS/date ----
function filterPolicies() {
const fromDate = getEl('fromDate')?.value || '';
const toDate = getEl('toDate')?.value || '';
// const agentId = getEl('agentSelect')?.value || '';
const posId = getEl('posSelect')?.value || '';
const policyTillDate = getEl('policyTillDate')?.value || '';
// if (!agentId || agentId === '0') return toastr.warning('Please select an agent', 'Required');
if (!posId || posId === '0') return toastr.warning('Please select an POS', 'Required');
filteredPolicies = window.allPolicies.filter(p => {
// if (String(p.agentId) !== String(agentId)) return false;
// if (String(p.posId) !== String(posId)) 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 posId = getEl('posSelect')?.value || '';
console.log(':) POS --> ',posId);
console.log(window.allPolicies);
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 (!posId || posId === '0') {
list.innerHTML = `<tr><td colspan="${colspan}" class="text-center text-danger">Please select a POS</td></tr>`;
updateSummary();
reloadDataTable();
return;
}
// Filter policies for the selected POS
filteredPolicies = window.allPolicies.filter(p => {
if (String(p.posId) !== String(posId)) 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')
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');
const posId = getEl('posSelect')?.value || '';
if (!posId) return toastr.warning('Please select an POS', '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);
return String(p.posId) === String(posId) && !existingPolicyIds.has(p.id);
});
if (newPolicies.length === 0) {
// toastr.info('No more policies available for this agent.', 'Information');
toastr.info('No more policies available for this POS.', '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 posId = getEl('posSelect')?.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 (!posId || posId === '0') return toastr.warning('Please select an pos', '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,
partner_policy_id : p.partner_policy_id
};
});
const invoiceData = {
invoice_id: invoiceID,
invoice_no: invoiceNo,
// agent_id: agentId,
invoice_date: invoiceDate,
invoice_amount: totalAmount,
policies: policies,
pos_id: posId
};
// 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');
const posSelectEl = getEl('posSelect');
// Set max date (today)
if (invoiceDateEl) invoiceDateEl.max = today;
if (policyTillDateEl) policyTillDateEl.max = today;
if (invoiceType == 'add') {
// -------- ADD MODE --------
// if (agentSelectEl) agentSelectEl.value = '';
if (posSelectEl) posSelectEl.value = '';
if (invoiceDateEl) invoiceDateEl.required = true;
if (policyTillDateEl) policyTillDateEl.required = true;
// if (agentSelectEl) agentSelectEl.required = true;
if (posSelectEl) posSelectEl.required = true;
// Enable Select2
// $('#agentSelect').prop('disabled', false).select2();
$('#posSelect').prop('disabled', false).select2();
}
else {
// -------- EDIT,ADJUSTMENT MODE --------
// if (agentSelectEl) agentSelectEl.readOnly = true;
if (posSelectEl) posSelectEl.readOnly = true;
// Disable Select2 (readonly)
// $('#agentSelect').prop('disabled', true);
$('#posSelect').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();
// });
// }
const posEl = getEl('posSelect');
if (posEl) {
posEl.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();
if (posEl && posEl.value && posEl.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>`;
if (body) body.innerHTML = `<tr><td colspan="${colspan}" class="text-center">Please select a POS</td></tr>`;
reloadDataTable();
}
});
</script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const freeze = <?= isset($freeze_edit) ? (int)$freeze_edit : 0 ?>;
const payout_status = <?= isset($payout_status) ? (int)$payout_status : 0 ?>;
const type = '<?= isset($type) ? $type : '' ?>';
// Determine if we should freeze edits
const shouldFreeze =
(freeze > 0 && type === 'view') ||
(freeze > 0 && type === 'edit') ||
(payout_status === 2 && (type === 'adjustment' || type === 'edit'));
if (shouldFreeze) {
console.log("Freeze edit active");
// Disable checkboxes and adjustment inputs after DataTable draw
$('#policy_table').on('draw.dt', function() {
document.querySelectorAll('.CB').forEach(cb => cb.disabled = true);
document.querySelectorAll('.policy-adjustment').forEach(input => input.disabled = true);
});
// Hide summary bar action buttons
document.querySelectorAll('.S, .C, .M').forEach(btn => btn.style.display = 'none');
}
});
</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.policy_no + ' (' + 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();
// let INV = $("#invoiceNo").val() || '';
return `Payouts (${day}-${month}-${year})`;
}
function downloadInvoicePdf(){
let invoice_id = $('#invoiceID').val();
if(!invoice_id){
return toastr.warning('No data found', 'Warning');
}
window.location.href = '<?= base_url('payout/invoices/downloadPdf/') ?>' + invoice_id
}
function fetchPreviewInvoiceDetails(invoice_id, invoice_no){
if (!invoice_id) {
toastr.warning("Invoice Id not found!", "WARNING");
return false;
}
let heading_text = ' - ( ' + invoice_no + ' )';
$('#invoicePreview').empty();
$('#invoice_heading').text(heading_text);
$('#invoicePreview').append('<div class="text-center p-5"><div class="spinner-border text-primary" role="status"><span class="sr-only">Loading...</span></div></div>');
var myModal = new bootstrap.Modal(document.getElementById('invoiceModal'));
myModal.show();
let url = '<?= base_url('payout/invoices/preview') ?>';
// Data to send in the AJAX request
let requestData = {
invoice_id: invoice_id,
};
// Send AJAX request
sendAjaxRequestForGlobal(url, 'GET', requestData, function(response) {
console.log('Data fetched successfully:', response);
$('#invoicePreview').empty();
$('#invoice_heading').text(heading_text);
if (response.status == true) {
$('#invoicePreview').append(response.data);
$('#row_invoice_id').val(invoice_id);
}else{
$('#invoicePreview').append('<div class="text-center p-5 text-muted">No data found</div>');
}
}, function(xhr, status, error) {
console.error('Error fetching data:', error);
console.error(xhr.responseText);
// toastr.error('An error occurred while fetching the data.', 'ERROR');
$('#invoicePreview').empty();
$('#invoicePreview').append('<div class="text-center p-5 text-muted">No data found</div>');
});
}
</script>
<?php if(isset($type) && $type === 'edit'): ?>
<script>
$(document).ready(function(){
$(".dt-buttons").prepend(`
<button class="btn btn-primary mr-2 M" onclick="showMorePolicies()" title="More Policies">
More Policies
</button>
`);
})
</script>
<?php endif; ?>