FIX_EXPENSE_RELATED_ISSUE
This commit is contained in:
parent
6e8b64b543
commit
95e109199c
@ -34,8 +34,7 @@ class ExpenseController extends AdminController
|
|||||||
try {
|
try {
|
||||||
$data['tab_name'] = 'Expense';
|
$data['tab_name'] = 'Expense';
|
||||||
$data['page_name'] = 'Expense';
|
$data['page_name'] = 'Expense';
|
||||||
$descriptionPattern = '/^[a-zA-Z0-9\s\.\,\-\(\)\/\&\+]+$/u';
|
$descriptionPattern = '/^[a-zA-Z0-9\s\.\,\-\(\)\/\&\+\'"]+$/u';
|
||||||
|
|
||||||
// Raw GET filters
|
// Raw GET filters
|
||||||
$rawFilters = $this->request->getGet() ?? [];
|
$rawFilters = $this->request->getGet() ?? [];
|
||||||
$rawFilters = is_array($rawFilters) ? $rawFilters : [];
|
$rawFilters = is_array($rawFilters) ? $rawFilters : [];
|
||||||
@ -97,6 +96,7 @@ class ExpenseController extends AdminController
|
|||||||
// Clients for dropdown
|
// Clients for dropdown
|
||||||
$data['clients'] = $this->clientModel
|
$data['clients'] = $this->clientModel
|
||||||
->select('id, client_name, short_name')
|
->select('id, client_name, short_name')
|
||||||
|
->where('client_type', 1)
|
||||||
->where('is_active', 1)
|
->where('is_active', 1)
|
||||||
->orderBy('client_name', 'ASC')
|
->orderBy('client_name', 'ASC')
|
||||||
->findAll();
|
->findAll();
|
||||||
@ -228,11 +228,12 @@ class ExpenseController extends AdminController
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
'amount' => [
|
'amount' => [
|
||||||
'rules' => 'required|numeric|greater_than_equal_to[0]',
|
'rules' => 'required|numeric|greater_than_equal_to[0]|less_than_equal_to[100000000]',
|
||||||
'errors' => [
|
'errors' => [
|
||||||
'required' => 'Amount is required',
|
'required' => 'Amount is required',
|
||||||
'numeric' => 'Amount must be numeric',
|
'numeric' => 'Amount must be numeric',
|
||||||
'greater_than_equal_to' => 'Amount cannot be negative',
|
'greater_than_equal_to' => 'Amount cannot be negative',
|
||||||
|
'less_than_equal_to' => 'Amount cannot be greater than 100 Cr.',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1,3 +1,35 @@
|
|||||||
|
<style>
|
||||||
|
|
||||||
|
.truncate {
|
||||||
|
max-width: 80px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#expense-desc-popup {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 9999;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
max-width: 320px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
<div class="row" id="expense_module">
|
<div class="row" id="expense_module">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@ -80,7 +112,18 @@
|
|||||||
|
|
||||||
<div class="form-group col-md-4">
|
<div class="form-group col-md-4">
|
||||||
<label for="amount">Amount <span class="text-danger">*</span></label>
|
<label for="amount">Amount <span class="text-danger">*</span></label>
|
||||||
<input type="number" step="0.01" min="0" class="form-control" id="amount" name="amount" placeholder="Enter amount" value="<?= isset($filters['amount']) ? esc($filters['amount']) : ''; ?>" required>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
min="0"
|
||||||
|
max="100000000"
|
||||||
|
class="form-control"
|
||||||
|
id="amount"
|
||||||
|
name="amount"
|
||||||
|
placeholder="Enter amount"
|
||||||
|
value="<?= isset($filters['amount']) ? esc($filters['amount']) : ''; ?>"
|
||||||
|
required
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group col-md-4">
|
<div class="form-group col-md-4">
|
||||||
@ -126,7 +169,16 @@
|
|||||||
<?= ! empty($row['short_name']) ? ' (' . esc($row['short_name']) . ')' : ''; ?>
|
<?= ! empty($row['short_name']) ? ' (' . esc($row['short_name']) . ')' : ''; ?>
|
||||||
</td>
|
</td>
|
||||||
<td><?= esc($row['policy_no'] ?? ''); ?></td>
|
<td><?= esc($row['policy_no'] ?? ''); ?></td>
|
||||||
<td><?= esc($row['description'] ?? ''); ?></td>
|
<td class="desc-cell">
|
||||||
|
<i
|
||||||
|
class="mdi mdi-information-outline text-muted expense-desc-icon"
|
||||||
|
style="cursor: pointer;"
|
||||||
|
data-description="<?= esc($row['description'] ?? ''); ?>"
|
||||||
|
></i>
|
||||||
|
<span class="truncate">
|
||||||
|
<?= esc($row['description'] ?? ''); ?>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
<td><?= esc($row['approved_by_name'] ?? ''); ?></td>
|
<td><?= esc($row['approved_by_name'] ?? ''); ?></td>
|
||||||
<td><?= number_format((float) ($row['amount'] ?? 0), 2); ?></td>
|
<td><?= number_format((float) ($row['amount'] ?? 0), 2); ?></td>
|
||||||
<td>
|
<td>
|
||||||
@ -213,8 +265,13 @@
|
|||||||
|
|
||||||
if (!amount) {
|
if (!amount) {
|
||||||
errors.push('Amount is required.');
|
errors.push('Amount is required.');
|
||||||
} else if (isNaN(amount) || Number(amount) < 0) {
|
} else {
|
||||||
errors.push('Amount must be a non-negative number.');
|
const amountNum = Number(amount);
|
||||||
|
if (isNaN(amountNum) || amountNum < 0) {
|
||||||
|
errors.push('Amount must be a non-negative number.');
|
||||||
|
} else if (amountNum > 100000000) {
|
||||||
|
errors.push('Amount cannot be greater than 100 Cr.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!expenseDate) {
|
if (!expenseDate) {
|
||||||
@ -261,8 +318,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (amount) {
|
if (amount) {
|
||||||
if (isNaN(amount) || Number(amount) < 0) {
|
const amountNum = Number(amount);
|
||||||
|
if (isNaN(amountNum) || amountNum < 0) {
|
||||||
errors.push('Amount filter must be a non-negative number.');
|
errors.push('Amount filter must be a non-negative number.');
|
||||||
|
} else if (amountNum > 100000000) {
|
||||||
|
errors.push('Amount filter cannot be greater than 100 Cr.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,10 +490,12 @@
|
|||||||
|
|
||||||
window.expenseDatePicker = flatpickr('#expense_date', {
|
window.expenseDatePicker = flatpickr('#expense_date', {
|
||||||
dateFormat: 'd-m-Y',
|
dateFormat: 'd-m-Y',
|
||||||
allowInput: true
|
allowInput: false,
|
||||||
|
maxDate: new Date(),
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#expense-table').DataTable({
|
$('#expense-table').DataTable({
|
||||||
|
scrollX: true,
|
||||||
dom: "<'row'<'col-12 d-flex justify-content-between align-items-center'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'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-12'tr>>" +
|
||||||
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
"<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>",
|
||||||
@ -450,7 +512,18 @@
|
|||||||
title: 'Expense List',
|
title: 'Expense List',
|
||||||
className: 'app-btn-primary ',
|
className: 'app-btn-primary ',
|
||||||
exportOptions: {
|
exportOptions: {
|
||||||
columns: ':not(:last-child)'
|
columns: ':not(:last-child)',
|
||||||
|
format: {
|
||||||
|
body: function (data, row, column, node) {
|
||||||
|
// Column index 2 is "Policy No" (S.No=0, Client=1, Policy No=2)
|
||||||
|
if (column === 2) {
|
||||||
|
// Prefix with apostrophe to force Excel treat as text and preserve long numbers
|
||||||
|
var text = $(node).text ? $(node).text() : data;
|
||||||
|
return "'" + text;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -461,7 +534,16 @@
|
|||||||
className: 'app-btn-primary ',
|
className: 'app-btn-primary ',
|
||||||
exportOptions: {
|
exportOptions: {
|
||||||
orthogonal: 'sort',
|
orthogonal: 'sort',
|
||||||
columns: ':not(:last-child)'
|
columns: ':not(:last-child)',
|
||||||
|
format: {
|
||||||
|
body: function (data, row, column, node) {
|
||||||
|
if (column === 2) {
|
||||||
|
var text = $(node).text ? $(node).text() : data;
|
||||||
|
return "'" + text;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -581,6 +663,34 @@
|
|||||||
$('#btnResetExpense, #btnCancelExpense').on('click', function () {
|
$('#btnResetExpense, #btnCancelExpense').on('click', function () {
|
||||||
resetExpenseForm();
|
resetExpenseForm();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Show small popup near the info icon on hover (no tooltip)
|
||||||
|
let $popup = $('#expense-desc-popup');
|
||||||
|
if (!$popup.length) {
|
||||||
|
$popup = $('<div id="expense-desc-popup"></div>').appendTo('body');
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).on('mouseenter', '.expense-desc-icon', function () {
|
||||||
|
const desc = $(this).data('description') || '';
|
||||||
|
if (!desc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$popup.text(desc);
|
||||||
|
|
||||||
|
const offset = $(this).offset();
|
||||||
|
const iconHeight = $(this).outerHeight() || 16;
|
||||||
|
|
||||||
|
$popup.css({
|
||||||
|
top: offset.top + iconHeight + 4,
|
||||||
|
left: offset.left,
|
||||||
|
display: 'block'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('mouseleave', '.expense-desc-icon', function () {
|
||||||
|
$popup.hide();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user