Fix billing invoice balance/payment flow and deduction UI

This commit is contained in:
DevSoftTech Server 2026-04-01 07:23:29 +05:30
parent 3088d1fc85
commit fe1156b68a
5 changed files with 311 additions and 104 deletions

View File

@ -841,6 +841,43 @@ class Charges extends BaseController
]);
}
if ($mode === 5) {
// Cash return — patient overpaid (negative pending amount)
$returnAmount = $paidAmount - $netAmount;
if ($returnAmount <= 0) {
return $this->response->setJSON([
'update' => 0,
'error_text' => 'No overpayment to return.',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$paydata = [
'payment_mode' => (int) ($invoice['payment_mode'] ?? 1),
'payof_type' => '2',
'payof_id' => $invId,
'payof_code' => $invoice['invoice_code'] ?? '',
'credit_debit' => '1',
'amount' => $returnAmount,
'payment_date' => date('Y-m-d H:i:s'),
'remark' => 'Cash Return - Overpayment after item removal',
'update_by' => $userNameInfo . ' [' . $userId . ']',
'update_by_id' => $userId,
'insert_code' => $spid,
];
$paymentModel->insertPayment($paydata);
$invoiceModel->updateInvoiceFinal($invId);
return $this->response->setJSON([
'update' => 1,
'showcontent' => 'Cash Return',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
return $this->response->setJSON([
'update' => 0,
'error_text' => 'Invalid payment request',

View File

@ -0,0 +1,113 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateInvoiceItemUpdateTable extends Migration
{
public function up(): void
{
$this->forge->addField([
'audit_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'id' => [
'type' => 'INT',
'constraint' => 11,
'null' => true,
],
'inv_master_id' => [
'type' => 'INT',
'constraint' => 11,
'null' => true,
'default' => 0,
],
'item_type' => [
'type' => 'INT',
'constraint' => 11,
'null' => true,
'default' => 0,
],
'item_id' => [
'type' => 'INT',
'constraint' => 11,
'null' => true,
'default' => 0,
],
'item_name' => [
'type' => 'VARCHAR',
'constraint' => 50,
'null' => true,
'default' => '0',
],
'item_rate' => [
'type' => 'DECIMAL',
'constraint' => '10,2',
'null' => true,
'default' => '0.00',
],
'ins_id' => [
'type' => 'INT',
'constraint' => 11,
'null' => true,
'default' => 0,
],
'org_code' => [
'type' => 'VARCHAR',
'constraint' => 50,
'null' => true,
],
'item_added_date' => [
'type' => 'DATETIME',
'null' => true,
],
'item_qty' => [
'type' => 'DECIMAL',
'constraint' => '10,2',
'null' => true,
'default' => '0.00',
],
'item_amount' => [
'type' => 'DECIMAL',
'constraint' => '10,2',
'null' => true,
'default' => '0.00',
],
'item_amount1' => [
'type' => 'DECIMAL',
'constraint' => '10,2',
'null' => true,
'default' => '0.00',
],
'update_by_id' => [
'type' => 'INT',
'constraint' => 11,
'null' => true,
],
'update_by' => [
'type' => 'VARCHAR',
'constraint' => 100,
'null' => true,
],
'update_action' => [
'type' => 'VARCHAR',
'constraint' => 20,
'null' => true,
],
]);
$this->forge->addKey('audit_id', true);
$this->forge->addKey('id');
$this->forge->addKey('inv_master_id');
$this->forge->createTable('invoice_item_update', true);
}
public function down(): void
{
$this->forge->dropTable('invoice_item_update', true);
}
}

View File

@ -60,11 +60,22 @@ class InvoiceModel
$row['update_by'] = $userName;
$row['update_action'] = '1';
if (! $this->db->table('invoice_item_update')->insert($row)) {
return false;
if ($this->db->tableExists('invoice_item_update')) {
$auditTableFields = $this->db->getFieldNames('invoice_item_update');
if (! empty($auditTableFields)) {
$row = array_intersect_key($row, array_flip($auditTableFields));
}
if (! $this->db->table('invoice_item_update')->insert($row)) {
return false;
}
}
$this->db->table('invoice_item')->where('id', $id)->delete();
if ($this->db->affectedRows() < 1) {
return false;
}
$this->recalculateInvoice((int) $row['inv_master_id']);
return true;
@ -135,10 +146,19 @@ class InvoiceModel
->getRowArray();
$paidAmount = (float) ($paidRow['paid_amount'] ?? 0);
$correctionCrdr = (int) ($invoice['correction_crdr'] ?? 1);
$netAmount = $grossTotal - $discountAmount;
$netAmount += $correctionAmount;
if ($correctionAmount > 0) {
if ($correctionCrdr === 0) {
$netAmount += $correctionAmount; // Add-type correction increases net
} else {
$netAmount -= $correctionAmount; // Return-type correction reduces net
}
}
$balanceAmount = $netAmount - $paidAmount;
$paymentPart = ($balanceAmount > 0 && $paidAmount > 0) ? 1 : 0;
$paymentMode = (int) ($invoice['payment_mode'] ?? 0);
$paymentStatus = ($paymentMode === 3 || $paymentMode === 4) ? 1 : ($balanceAmount <= 0 ? 1 : 0);
$this->db->table('invoice_master')
->where('id', $invoiceId)
@ -148,6 +168,7 @@ class InvoiceModel
'payment_part_received' => $paidAmount,
'payment_part_balance' => $balanceAmount,
'payment_part' => $paymentPart,
'payment_status' => $paymentStatus,
]);
if ($insuranceCaseId > 0) {

View File

@ -32,92 +32,92 @@
<section class="invoice">
<div class="card">
<div class="card-body p-3">
<div class="row invoice-info mb-3">
<div class="col-sm-6 invoice-col">
To
<address>
<strong><?= esc($patient_master[0]->p_fname ?? '') ?></strong><br>
<?= esc($patient_master[0]->p_relative ?? '') ?> : <?= esc($patient_master[0]->p_rname ?? '') ?><br>
Gender : <?= esc($patient_master[0]->xgender ?? '') ?><br>
Age : <?= esc($patient_master[0]->age ?? '') ?><br>
Phone No : <?= esc($patient_master[0]->mphone1 ?? '') ?>
</address>
</div>
<div class="col-sm-6 invoice-col">
<b>Invoice #<?= esc($invoice_master[0]->invoice_code ?? '') ?></b><br>
<?php if (($invoice_master[0]->insurance_id ?? 0) > 1) : ?>
<strong> Ins. Comp. :</strong><?= esc($insurance[0]->ins_company_name ?? '') ?><br>
<?php if (($invoice_master[0]->insurance_case_id ?? 0) > 0 && count($case_master) > 0) : ?>
<strong> Org.Case No. :</strong><?= esc($case_master[0]->case_id_code ?? '') ?><br>
<?php endif; ?>
<?php endif; ?>
<b>Date :</b> <?= esc($invoice_master[0]->inv_date ?? '') ?><br>
<b>Patient ID :</b> <?= esc($patient_master[0]->p_code ?? '') ?><br>
<b>Refer By :</b> <?= esc($invoice_master[0]->refer_by_other ?? '') ?><br>
<input type="hidden" value="<?= esc($invoice_master[0]->id ?? 0) ?>" id="invoice_id" name="invoice_id" />
</div>
</div>
<div class="row invoice-info mb-3">
<div class="col-sm-6 invoice-col">
To
<address>
<strong><?= esc($patient_master[0]->p_fname ?? '') ?></strong><br>
<?= esc($patient_master[0]->p_relative ?? '') ?> : <?= esc($patient_master[0]->p_rname ?? '') ?><br>
Gender : <?= esc($patient_master[0]->xgender ?? '') ?><br>
Age : <?= esc($patient_master[0]->age ?? '') ?><br>
Phone No : <?= esc($patient_master[0]->mphone1 ?? '') ?>
</address>
</div>
<div class="col-sm-6 invoice-col">
<b>Invoice #<?= esc($invoice_master[0]->invoice_code ?? '') ?></b><br>
<?php if (($invoice_master[0]->insurance_id ?? 0) > 1) : ?>
<strong> Ins. Comp. :</strong><?= esc($insurance[0]->ins_company_name ?? '') ?><br>
<?php if (($invoice_master[0]->insurance_case_id ?? 0) > 0 && count($case_master) > 0) : ?>
<strong> Org.Case No. :</strong><?= esc($case_master[0]->case_id_code ?? '') ?><br>
<?php endif; ?>
<?php endif; ?>
<b>Date :</b> <?= esc($invoice_master[0]->inv_date ?? '') ?><br>
<b>Patient ID :</b> <?= esc($patient_master[0]->p_code ?? '') ?><br>
<b>Refer By :</b> <?= esc($invoice_master[0]->refer_by_other ?? '') ?><br>
<input type="hidden" value="<?= esc($invoice_master[0]->id ?? 0) ?>" id="invoice_id" name="invoice_id" />
</div>
</div>
<div class="row">
<div class="col-md-12">
<table class="table table-striped table-responsive mb-0">
<tr>
<th style="width: 10px">#</th>
<th>Charges Group</th>
<th>Charge Name</th>
<th>Rate</th>
<th>Qty</th>
<th>Amount</th>
<th></th>
</tr>
<?php $srno = 1; foreach ($invoiceDetails as $row) : ?>
<tr>
<td><?= $srno ?></td>
<td><?= esc($row->group_desc ?? '') ?></td>
<td><?= esc($row->item_name ?? '') ?></td>
<td><?= esc($row->item_rate ?? '') ?></td>
<td><?= esc($row->item_qty ?? '') ?></td>
<td><?= esc($row->item_amount ?? '') ?></td>
<td></td>
</tr>
<?php $srno++; endforeach; ?>
<tr>
<th style="width: 10px">#</th>
<th></th>
<th></th>
<th></th>
<th>Gross Total</th>
<th><?= esc($invoice_master[0]->total_amount ?? '') ?></th>
<th></th>
</tr>
<?php if (($invoice_master[0]->payment_status ?? 0) == 0) : ?>
<tr>
<th style="width: 10px">#</th>
<th>Deduction</th>
<th colspan=3><input class="form-control" name="input_dis_desc" id="input_dis_desc" placeholder="Ded. Desc." value="<?= esc($invoice_master[0]->discount_desc ?? '') ?>" type="text" /></th>
<th><input style="width: 100px" class="form-control" name="input_dis_amt" id="input_dis_amt" placeholder="Amount" value="<?= esc($invoice_master[0]->discount_amount ?? '') ?>" type="text" /></th>
<th><button type="button" class="btn btn-primary" id="btn_update_ded">Update</button></th>
</tr>
<?php else : ?>
<tr>
<th style="width: 10px">#</th>
<th>Deduction</th>
<th colspan=3><?= esc($invoice_master[0]->discount_desc ?? '') ?></th>
<th><?= esc($invoice_master[0]->discount_amount ?? '') ?></th>
<th></th>
</tr>
<?php endif; ?>
<tr>
<th style="width: 10px">#</th>
<th colspan="2"></th>
<th></th>
<th>Net Amount</th>
<th><?= esc($invoice_master[0]->net_amount ?? '') ?></th>
<th></th>
</tr>
</table>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table class="table table-striped table-responsive mb-0">
<tr>
<th style="width: 10px">#</th>
<th>Charges Group</th>
<th>Charge Name</th>
<th>Rate</th>
<th>Qty</th>
<th>Amount</th>
<th></th>
</tr>
<?php $srno = 1; foreach ($invoiceDetails as $row) : ?>
<tr>
<td><?= $srno ?></td>
<td><?= esc($row->group_desc ?? '') ?></td>
<td><?= esc($row->item_name ?? '') ?></td>
<td><?= esc($row->item_rate ?? '') ?></td>
<td><?= esc($row->item_qty ?? '') ?></td>
<td><?= esc($row->item_amount ?? '') ?></td>
<td></td>
</tr>
<?php $srno++; endforeach; ?>
<tr>
<th style="width: 10px">#</th>
<th></th>
<th></th>
<th></th>
<th>Gross Total</th>
<th><?= esc($invoice_master[0]->total_amount ?? '') ?></th>
<th></th>
</tr>
<?php if (($invoice_master[0]->payment_status ?? 0) == 0) : ?>
<tr>
<th style="width: 10px">#</th>
<th>Deduction</th>
<th colspan=3><input class="form-control" name="input_dis_desc" id="input_dis_desc" placeholder="Ded. Desc." value="<?= esc($invoice_master[0]->discount_desc ?? '') ?>" type="text" /></th>
<th><input style="width: 100px" class="form-control" name="input_dis_amt" id="input_dis_amt" placeholder="Amount" value="<?= esc($invoice_master[0]->discount_amount ?? '') ?>" type="text" /></th>
<th><button type="button" class="btn btn-primary" id="btn_update_ded">Update</button></th>
</tr>
<?php else : ?>
<tr>
<th style="width: 10px">#</th>
<th>Deduction</th>
<th colspan=3><?= esc($invoice_master[0]->discount_desc ?? '') ?></th>
<th><?= esc($invoice_master[0]->discount_amount ?? '') ?></th>
<th></th>
</tr>
<?php endif; ?>
<tr>
<th style="width: 10px">#</th>
<th colspan="2"></th>
<th></th>
<th>Net Amount</th>
<th><?= esc($invoice_master[0]->net_amount ?? '') ?></th>
<th></th>
</tr>
</table>
</div>
</div>
</div>
</div>
</section>
@ -139,7 +139,7 @@
</div>
</div>
<?php if (($invoice_master[0]->payment_status ?? 0) == 0 && $canChargePay) : ?>
<?php if ($canChargePay && (($pending_amount ?? 0) > 0 || (($pending_amount ?? 0) == 0 && ((int) ($invoice_master[0]->payment_status ?? 0) === 0))) ) : ?>
<div class="card">
<div class="card-body p-3">
<div class="row mt-2">
@ -316,26 +316,34 @@
<th style="width: 10px">#</th>
<th>Deduction</th>
<th colspan=3>
<input class="form-control" name="input_corr_desc" id="input_corr_desc" placeholder="Correction Desc." value="<?= esc($invoice_master[0]->correction_remark ?? '') ?>" type="text" />
<input class="form-control" name="input_dis_desc" id="input_dis_desc" placeholder="Deduction Desc." value="<?= esc($invoice_master[0]->discount_desc ?? '') ?>" type="text" />
</th>
<th>
<input style="width: 100px" class="form-control" name="input_corr_amt" id="input_corr_amt" placeholder="Amount" value="<?= esc($invoice_master[0]->correction_amount ?? '') ?>" type="text" />
<input style="width: 100px" class="form-control" name="input_dis_amt" id="input_dis_amt" placeholder="Amount" value="<?= esc($invoice_master[0]->discount_amount ?? '') ?>" type="text" />
</th>
<th>
<div class="form-check">
<input class="form-check-input" type="radio" name="optionsRadios_crdr" id="corr_return" value="1" checked>
<label class="form-check-label" for="corr_return">Return</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="optionsRadios_crdr" id="corr_add" value="0">
<label class="form-check-label" for="corr_add">Add</label>
</div>
</th>
<th><button type="button" class="btn btn-primary" id="btn_update_corr">Update Correction</button></th>
<th><button type="button" class="btn btn-primary" id="btn_update_ded">Update Discount</button></th>
</tr>
</table>
</div>
<?php endif; ?>
<?php if ($canChargePay && ($pending_amount ?? 0) < 0 && in_array((int) ($invoice_master[0]->payment_mode ?? 0), [1, 2], true)) : ?>
<hr />
<div class="alert alert-warning mb-2">
<strong>Overpayment:</strong>
Amount Received: <strong>Rs. <?= number_format((float) ($paid_amount ?? 0), 2) ?></strong> &nbsp;|&nbsp;
Net Amount: <strong>Rs. <?= number_format((float) ($invoice_master[0]->net_amount ?? 0), 2) ?></strong> &nbsp;|&nbsp;
Return Amount Due: <strong>Rs. <?= number_format(abs((float) ($pending_amount ?? 0)), 2) ?></strong>
</div>
<div class="row g-2 align-items-center">
<div class="col-md-auto">
<button type="button" class="btn btn-warning" id="btn_return_cash">
Process Cash Return - Rs. <?= number_format(abs((float) ($pending_amount ?? 0)), 2) ?>
</button>
</div>
<div class="col-md-auto jsReturnError text-danger"></div>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
@ -532,5 +540,26 @@ $(document).ready(function(){
}
}, 'json');
});
$('#btn_return_cash').click(function() {
var csrf = getCsrfPair();
var spid = $('#spid').val();
if (confirm('Are you sure you want to process a cash return to the patient?')) {
$('#btn_return_cash').prop('disabled', true);
$.post('<?= base_url('billing/charges/confirm-payment') ?>', {
"mode": "5",
"inv_id": $('#invoice_id').val(),
"spid": spid,
[csrf.name]: csrf.value
}, function(data) {
updateCsrf(data);
if (data.update == 0) {
$('div.jsReturnError').html(data.error_text);
$('#btn_return_cash').prop('disabled', false);
} else {
load_form('<?= base_url('billing/charges/show') ?>/' + $('#invoice_id').val());
}
}, 'json');
}
});
})();
</script>

View File

@ -24,7 +24,7 @@ if ($hospitalNameLen > 42) {
$invoiceCode = (string) ($invoice->invoice_code ?? '');
$invoiceDate = (string) ($invoice->inv_date ?? '');
$printedOn = date('d-m-Y h:i:s A');
$invoiceStatus = ((int) ($invoice->invoice_status ?? 0) === 1) ? 'Paid' : (((int) ($invoice->invoice_status ?? 0) === 2) ? 'Cancelled' : 'Pending');
$invoiceStatus = ((int) ($invoice->invoice_status ?? 0) === 2) ? 'Cancelled' : (((int) ($invoice->payment_status ?? 0) === 1) ? 'Paid' : 'Pending');
$currencyPrefix = 'Rs.';
$grossAmount = (float) ($invoice->total_amount ?? 0);
$discount = (float) ($invoice->discount_amount ?? 0);
@ -58,6 +58,9 @@ $qrParts = [
$qrContent = implode(' | ', array_filter($qrParts, static fn ($v) => trim((string) $v) !== ''));
$qrPackageAvailable = class_exists('\\Mpdf\\QrCode\\QrCode');
$paidAmountPrint = (float) ($invoice->payment_part_received ?? 0);
$balanceAmountPrint = (float) ($invoice->payment_part_balance ?? ($netAmount - $paidAmountPrint));
/**
* Convert numbers to words (basic implementation for Indian system)
* Wrapped with function_exists to prevent redeclaration when rendering multiple times
@ -250,7 +253,11 @@ if (!function_exists('convertNumberToWords')) {
<tr>
<td class="totals-left">
<div class="payment-details">
<div><span class="label">Amount received:</span> <?= esc($currencyPrefix) ?> <?= number_format(0, 2) ?> | <span class="label">Balance Amount:</span> <?= esc($currencyPrefix) ?> <?= number_format($netAmount, 2) ?> | <span class="label">Net Amount:</span> <?= esc($currencyPrefix) ?> <?= number_format($netAmount, 2) ?></div>
<?php if ($balanceAmountPrint < 0) : ?>
<div><span class="label">Amount received:</span> <?= esc($currencyPrefix) ?> <?= number_format($paidAmountPrint, 2) ?> | <span class="label" style="color:#c00;">Return Amount:</span> <span style="color:#c00;"><?= esc($currencyPrefix) ?> <?= number_format(abs($balanceAmountPrint), 2) ?></span> | <span class="label">Net Amount:</span> <?= esc($currencyPrefix) ?> <?= number_format($netAmount, 2) ?></div>
<?php else : ?>
<div><span class="label">Amount received:</span> <?= esc($currencyPrefix) ?> <?= number_format($paidAmountPrint, 2) ?> | <span class="label">Balance Amount:</span> <?= esc($currencyPrefix) ?> <?= number_format($balanceAmountPrint, 2) ?> | <span class="label">Net Amount:</span> <?= esc($currencyPrefix) ?> <?= number_format($netAmount, 2) ?></div>
<?php endif; ?>
<div style="margin-top:6px;"><span class="amount-words">Amount in Words: </span><?php
$amountInWords = convertNumberToWords((int)$netAmount);
echo esc($amountInWords) . ' Only';