diff --git a/app/Controllers/Billing/Charges.php b/app/Controllers/Billing/Charges.php
index 9578e66..5a5fdb8 100644
--- a/app/Controllers/Billing/Charges.php
+++ b/app/Controllers/Billing/Charges.php
@@ -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',
diff --git a/app/Database/Migrations/2026-04-01-000030_CreateInvoiceItemUpdateTable.php b/app/Database/Migrations/2026-04-01-000030_CreateInvoiceItemUpdateTable.php
new file mode 100644
index 0000000..9702258
--- /dev/null
+++ b/app/Database/Migrations/2026-04-01-000030_CreateInvoiceItemUpdateTable.php
@@ -0,0 +1,113 @@
+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);
+ }
+}
\ No newline at end of file
diff --git a/app/Models/InvoiceModel.php b/app/Models/InvoiceModel.php
index 93941c2..835d5ed 100644
--- a/app/Models/InvoiceModel.php
+++ b/app/Models/InvoiceModel.php
@@ -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) {
diff --git a/app/Views/billing/charges/charges_invoice_show.php b/app/Views/billing/charges/charges_invoice_show.php
index 5ab2b04..f1672ab 100644
--- a/app/Views/billing/charges/charges_invoice_show.php
+++ b/app/Views/billing/charges/charges_invoice_show.php
@@ -32,92 +32,92 @@
-
-
- To
-
- = esc($patient_master[0]->p_fname ?? '') ?>
- = esc($patient_master[0]->p_relative ?? '') ?> : = esc($patient_master[0]->p_rname ?? '') ?>
- Gender : = esc($patient_master[0]->xgender ?? '') ?>
- Age : = esc($patient_master[0]->age ?? '') ?>
- Phone No : = esc($patient_master[0]->mphone1 ?? '') ?>
-
-
-
- Invoice #= esc($invoice_master[0]->invoice_code ?? '') ?>
- insurance_id ?? 0) > 1) : ?>
- Ins. Comp. := esc($insurance[0]->ins_company_name ?? '') ?>
- insurance_case_id ?? 0) > 0 && count($case_master) > 0) : ?>
- Org.Case No. := esc($case_master[0]->case_id_code ?? '') ?>
-
-
- Date : = esc($invoice_master[0]->inv_date ?? '') ?>
- Patient ID : = esc($patient_master[0]->p_code ?? '') ?>
- Refer By : = esc($invoice_master[0]->refer_by_other ?? '') ?>
-
-
-
+
+
+ To
+
+ = esc($patient_master[0]->p_fname ?? '') ?>
+ = esc($patient_master[0]->p_relative ?? '') ?> : = esc($patient_master[0]->p_rname ?? '') ?>
+ Gender : = esc($patient_master[0]->xgender ?? '') ?>
+ Age : = esc($patient_master[0]->age ?? '') ?>
+ Phone No : = esc($patient_master[0]->mphone1 ?? '') ?>
+
+
+
+ Invoice #= esc($invoice_master[0]->invoice_code ?? '') ?>
+ insurance_id ?? 0) > 1) : ?>
+ Ins. Comp. := esc($insurance[0]->ins_company_name ?? '') ?>
+ insurance_case_id ?? 0) > 0 && count($case_master) > 0) : ?>
+ Org.Case No. := esc($case_master[0]->case_id_code ?? '') ?>
+
+
+ Date : = esc($invoice_master[0]->inv_date ?? '') ?>
+ Patient ID : = esc($patient_master[0]->p_code ?? '') ?>
+ Refer By : = esc($invoice_master[0]->refer_by_other ?? '') ?>
+
+
+
-
+
@@ -139,7 +139,7 @@
-payment_status ?? 0) == 0 && $canChargePay) : ?>
+ 0 || (($pending_amount ?? 0) == 0 && ((int) ($invoice_master[0]->payment_status ?? 0) === 0))) ) : ?>
+
+ payment_mode ?? 0), [1, 2], true)) : ?>
+
+
+ Overpayment:
+ Amount Received: Rs. = number_format((float) ($paid_amount ?? 0), 2) ?> |
+ Net Amount: Rs. = number_format((float) ($invoice_master[0]->net_amount ?? 0), 2) ?> |
+ Return Amount Due: Rs. = number_format(abs((float) ($pending_amount ?? 0)), 2) ?>
+
+
+
+
+
+
+
+
@@ -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');
+ }
+ });
})();
diff --git a/app/Views/billing/charges/invoice_pdf_template.php b/app/Views/billing/charges/invoice_pdf_template.php
index 7da7c6b..06ecb4d 100644
--- a/app/Views/billing/charges/invoice_pdf_template.php
+++ b/app/Views/billing/charges/invoice_pdf_template.php
@@ -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')) {
- Amount received: = esc($currencyPrefix) ?> = number_format(0, 2) ?> | Balance Amount: = esc($currencyPrefix) ?> = number_format($netAmount, 2) ?> | Net Amount: = esc($currencyPrefix) ?> = number_format($netAmount, 2) ?>
+
+ Amount received: = esc($currencyPrefix) ?> = number_format($paidAmountPrint, 2) ?> | Return Amount: = esc($currencyPrefix) ?> = number_format(abs($balanceAmountPrint), 2) ?> | Net Amount: = esc($currencyPrefix) ?> = number_format($netAmount, 2) ?>
+
+ Amount received: = esc($currencyPrefix) ?> = number_format($paidAmountPrint, 2) ?> | Balance Amount: = esc($currencyPrefix) ?> = number_format($balanceAmountPrint, 2) ?> | Net Amount: = esc($currencyPrefix) ?> = number_format($netAmount, 2) ?>
+
Amount in Words: |