feat: add payment editing and fix discharge templates

This commit is contained in:
DevSoftTech Server 2026-08-10 14:48:33 +05:30
parent 681ad44a69
commit 4ca463cc1e
11 changed files with 690 additions and 7 deletions

View File

@ -157,6 +157,7 @@ class AuthGroups extends ShieldAuthGroups
'finance.bank.deposit.create' => 'Can create bank deposit register entries',
'finance.bank.audit' => 'Can mark bank transaction audit status',
'finance.bank.statement.update' => 'Can mark updated in bank statement status',
'finance.payment.edit' => 'Can correct payment mode, amount, or recorded user',
'finance.compliance.view' => 'Can view finance compliance reports',
'finance.doctor_payout.manage' => 'Can manage doctor payouts and agreements',
'finance.vendor.manage' => 'Can manage finance vendors',
@ -378,6 +379,7 @@ class AuthGroups extends ShieldAuthGroups
'finance.cash.accounts.verify',
'finance.bank.audit',
'finance.bank.statement.update',
'finance.payment.edit',
],
'user' => [],
'beta' => [

View File

@ -292,6 +292,16 @@ $routes->post('Payment_Medical/change_to_cash', 'Payment_Medical::change_to_cash
$routes->post('Payment_Medical/update_amount', 'Payment_Medical::update_amount');
$routes->post('Payment_Medical/change_user', 'Payment_Medical::change_user');
$paymentEditFilter = ['filter' => 'permission:finance.payment.edit'];
$routes->get('Payment', 'Payment::index', $paymentEditFilter);
$routes->post('Payment/payment_record', 'Payment::paymentRecord', $paymentEditFilter);
$routes->post('Payment/change_to_bank', 'Payment::changeToBank', $paymentEditFilter);
$routes->post('Payment/change_to_cash', 'Payment::changeToCash', $paymentEditFilter);
$routes->post('Payment/change_user', 'Payment::changeUser', $paymentEditFilter);
$routes->post('Payment/change_amount', 'Payment::changeAmount', $paymentEditFilter);
$routes->post('Payment/update_amount', 'Payment::changeAmount', $paymentEditFilter);
$routes->get('finance/payment-edit', 'Payment::index', $paymentEditFilter);
// Finance & Accounting SOP - Phase 1
$routes->get('Finance', 'Finance::index');
$routes->get('Finance/index', 'Finance::index');

View File

@ -498,9 +498,6 @@ class Ipd_discharge extends BaseController
private function buildDischargeSectionTokenVars(string $content, array $panelData): array
{
$full = trim($content);
if ($full === '') {
return [];
}
$ipd = $panelData['ipd_info'] ?? null;
$dischargeStatus = $this->getDischargeStatusText($ipd);
@ -1114,7 +1111,7 @@ class Ipd_discharge extends BaseController
$patientCode = html_entity_decode((string) ($tokenVars['UHID'] ?? ''), ENT_QUOTES, 'UTF-8');
$ipdCode = html_entity_decode((string) ($tokenVars['IPD_CODE'] ?? ''), ENT_QUOTES, 'UTF-8');
if ($this->shouldUseContentOnlyTemplate($content, $templateHtml)) {
if ($requestedTemplateId === null && $this->shouldUseContentOnlyTemplate($content, $templateHtml)) {
$templateHtml = '{{CONTENT}}';
}

264
app/Controllers/Payment.php Normal file
View File

@ -0,0 +1,264 @@
<?php
namespace App\Controllers;
use App\Models\PaymentModel;
use RuntimeException;
use Throwable;
class Payment extends BaseController
{
private PaymentModel $paymentModel;
public function __construct()
{
$this->paymentModel = new PaymentModel();
}
public function index(): string
{
return view('payment/payment_search');
}
public function paymentRecord()
{
$paymentId = (int) preg_replace('/\D+/', '', (string) $this->request->getPost('txtsearch'));
if ($paymentId <= 0) {
return $this->searchResponse('<div class="alert alert-warning">Enter a valid Payment ID.</div>', 422);
}
$payment = $this->paymentModel->findPayment($paymentId);
if ($payment === null) {
return $this->searchResponse('<div class="alert alert-warning">Payment record not found.</div>', 404);
}
return $this->searchResponse($this->renderPayment($payment));
}
public function changeToBank()
{
$paymentId = (int) $this->request->getPost('pay_id');
$sourceId = (int) $this->request->getPost('cbo_pay_type');
$transactionId = trim((string) $this->request->getPost('input_card_tran'));
$source = $this->findBankSource($sourceId);
if ($source === null || $transactionId === '') {
return $this->error('Bank/online source and transaction reference are required.', 422);
}
$label = (string) $source['label'];
return $this->applyCorrection($paymentId, [
'payment_mode' => 2,
'pay_bank_id' => $sourceId,
'insert_code' => (string) $sourceId,
'card_bank' => $label,
'card_tran_id' => $transactionId,
'update_remark' => 'Changed to Bank/Online',
], 2, 'Payment mode changed to BANK [' . $label . '], reference ' . $transactionId);
}
public function changeToCash()
{
$paymentId = (int) $this->request->getPost('pay_id');
return $this->applyCorrection($paymentId, [
'payment_mode' => 1,
'pay_bank_id' => 0,
'card_bank' => '',
'card_remark' => '',
'cust_card' => '',
'card_tran_id' => '',
'bankcard_machine' => '',
'insert_code' => '',
'update_remark' => 'Changed to Cash',
], 1, 'Payment mode changed to CASH');
}
public function changeUser()
{
$paymentId = (int) $this->request->getPost('pay_id');
$userId = (int) $this->request->getPost('user_list');
$user = $this->db->table('users')->select('id,username')->where('id', $userId)->where('active', 1)->get(1)->getRowArray();
if ($user === null) {
return $this->error('Select a valid active user.', 422);
}
$payment = $this->paymentModel->findPayment($paymentId);
$oldUser = trim((string) ($payment['update_by'] ?? '-'));
$newUser = (string) $user['username'] . '[' . $userId . ']';
return $this->applyCorrection($paymentId, [
'update_by_id' => $userId,
'update_by' => $newUser,
'update_remark' => 'Payment user corrected',
], 3, 'Payment user changed from ' . $oldUser . ' to ' . $newUser);
}
public function changeAmount()
{
$paymentId = (int) $this->request->getPost('pay_id');
$rawAmount = trim((string) $this->request->getPost('change_value'));
if (! is_numeric($rawAmount) || (float) $rawAmount <= 0) {
return $this->error('Enter a valid amount greater than zero.', 422);
}
$payment = $this->paymentModel->findPayment($paymentId);
$oldAmount = number_format((float) ($payment['amount'] ?? 0), 2, '.', '');
$newAmount = number_format((float) $rawAmount, 2, '.', '');
return $this->applyCorrection($paymentId, [
'amount' => $newAmount,
'update_remark' => 'Payment amount corrected',
], 4, 'Payment amount changed from ' . $oldAmount . ' to ' . $newAmount);
}
private function applyCorrection(int $paymentId, array $changes, int $updateType, string $remark)
{
if ($paymentId <= 0) {
return $this->error('Payment record not found.', 404);
}
$reason = trim((string) $this->request->getPost('correction_reason'));
if ($reason === '') {
return $this->error('Correction reason is required.', 422);
}
try {
$payment = $this->paymentModel->correctPayment($paymentId, $changes, $updateType, $this->actorName(), $remark . '. Reason: ' . $reason);
return $this->response->setJSON([
'status' => 1,
'message' => 'Payment updated successfully.',
'html' => $this->renderPayment($payment),
'csrf_hash' => csrf_hash(),
]);
} catch (RuntimeException $e) {
$status = str_contains(strtolower($e->getMessage()), 'not found') ? 404 : 409;
return $this->error($e->getMessage(), $status);
} catch (Throwable $e) {
log_message('error', 'Payment correction failed: {message}', ['message' => $e->getMessage()]);
return $this->error('Unable to update the payment record.', 500);
}
}
private function renderPayment(array $payment): string
{
$context = $this->paymentContext($payment);
return view('payment/payment_edit', [
'payment' => $payment,
'context' => $context,
'users' => $this->db->table('users')->select('id,username')->where('active', 1)->orderBy('username')->get()->getResultArray(),
'bank_sources' => $this->bankSources(),
]);
}
private function paymentContext(array $payment): array
{
$type = (int) ($payment['payof_type'] ?? 0);
$referenceId = (int) ($payment['payof_id'] ?? 0);
$context = [
'type' => [1 => 'OPD', 2 => 'OPD Charge', 3 => 'Organization Charge', 4 => 'IPD Payment'][$type] ?? 'Payment',
'code' => (string) ($payment['payof_code'] ?? ''),
'patient' => '',
];
$tableMap = [
1 => ['opd_master', 'opd_id', ['opd_code'], ['p_id'], ['p_name', 'P_name']],
2 => ['invoice_master', 'id', ['invoice_code'], ['attach_id'], ['inv_name']],
3 => ['organization_case_master', 'id', ['case_id_code'], ['p_id'], ['p_name', 'insurance_company_name']],
4 => ['ipd_master', 'id', ['ipd_code'], ['p_id'], ['P_name', 'p_name']],
];
if (! isset($tableMap[$type])) {
return $context;
}
[$table, $key, $codeColumns, $patientColumns, $nameColumns] = $tableMap[$type];
if (! $this->db->tableExists($table)) {
return $context;
}
$row = $this->db->table($table)->where($key, $referenceId)->get(1)->getRowArray();
if ($row === null) {
return $context;
}
$context['code'] = $this->firstValue($row, $codeColumns, $context['code']);
$context['patient'] = $this->firstValue($row, $nameColumns);
$patientId = (int) $this->firstValue($row, $patientColumns, '0');
if ($context['patient'] === '' && $patientId > 0) {
foreach (['patient_master', 'patient_master_exten'] as $patientTable) {
if (! $this->db->tableExists($patientTable)) {
continue;
}
$patient = $this->db->table($patientTable)->where('id', $patientId)->get(1)->getRowArray();
if ($patient !== null) {
$context['patient'] = trim((string) ($patient['p_fname'] ?? '') . ' ' . (string) ($patient['p_lname'] ?? ''));
break;
}
}
}
return $context;
}
private function bankSources(): array
{
if (! $this->db->tableExists('hospital_bank_payment_source') || ! $this->db->tableExists('hospital_bank')) {
return [];
}
return $this->db->table('hospital_bank_payment_source s')
->select("s.id, CONCAT(COALESCE(s.pay_type, ''), ' [', COALESCE(b.bank_name, ''), ']') AS label", false)
->join('hospital_bank b', 'b.id = s.bank_id', 'left')
->orderBy('s.pay_type')
->get()
->getResultArray();
}
private function findBankSource(int $sourceId): ?array
{
foreach ($this->bankSources() as $source) {
if ((int) $source['id'] === $sourceId) {
return $source;
}
}
return null;
}
private function firstValue(array $row, array $columns, string $default = ''): string
{
foreach ($columns as $column) {
if (isset($row[$column]) && trim((string) $row[$column]) !== '') {
return trim((string) $row[$column]);
}
}
return $default;
}
private function actorName(): string
{
$user = auth()->user();
return $user ? ((string) ($user->username ?? 'user') . '[' . (int) $user->id . ']') : 'system';
}
private function searchResponse(string $body, int $status = 200)
{
return $this->response
->setStatusCode($status)
->setHeader('X-CSRF-TOKEN', csrf_hash())
->setBody($body);
}
private function error(string $message, int $status)
{
return $this->response->setStatusCode($status)->setJSON([
'status' => 0,
'message' => $message,
'csrf_hash' => csrf_hash(),
]);
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace App\Database\Migrations;
use App\Libraries\RoleRegistry;
use CodeIgniter\Database\Migration;
class AddFinancePaymentEditPermission extends Migration
{
private const PERMISSION = 'finance.payment.edit';
public function up()
{
if (! $this->tableExists('auth_roles') || ! $this->tableExists('auth_role_permissions')) {
return;
}
$now = date('Y-m-d H:i:s');
foreach (['superadmin', 'admin', 'developer', 'accounts_officer'] as $alias) {
$role = $this->db->table('auth_roles')->select('id')->where('alias', $alias)->get(1)->getRowArray();
if ($role === null) {
continue;
}
$exists = $this->db->table('auth_role_permissions')
->where('role_id', (int) $role['id'])
->where('permission', self::PERMISSION)
->countAllResults();
if ($exists === 0) {
$this->db->table('auth_role_permissions')->insert([
'role_id' => (int) $role['id'],
'permission' => self::PERMISSION,
'created_at' => $now,
]);
}
}
(new RoleRegistry($this->db))->publish();
}
public function down()
{
if ($this->tableExists('auth_role_permissions')) {
$this->db->table('auth_role_permissions')->where('permission', self::PERMISSION)->delete();
}
if ($this->tableExists('auth_permissions_users')) {
$this->db->table('auth_permissions_users')->where('permission', self::PERMISSION)->delete();
}
if ($this->tableExists('auth_roles')) {
(new RoleRegistry($this->db))->publish();
}
}
private function tableExists(string $table): bool
{
return $this->db->query('SHOW TABLES LIKE ' . $this->db->escape($table))->getRowArray() !== null;
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class BackfillFinancePaymentEditUserPermission extends Migration
{
private const PERMISSION = 'finance.payment.edit';
public function up()
{
if (! $this->tableExists('auth_permissions_users')) {
return;
}
$eligiblePermissions = [
'finance.cash.accounts.accept',
'finance.cash.accounts.verify',
'finance.bank.audit',
'users.manage-admins',
];
$userIds = array_unique(array_column(
$this->db->table('auth_permissions_users')
->select('user_id')
->whereIn('permission', $eligiblePermissions)
->get()
->getResultArray(),
'user_id'
));
foreach ($userIds as $userId) {
$exists = $this->db->table('auth_permissions_users')
->where('user_id', (int) $userId)
->where('permission', self::PERMISSION)
->countAllResults();
if ($exists === 0) {
$this->db->table('auth_permissions_users')->insert([
'user_id' => (int) $userId,
'permission' => self::PERMISSION,
'created_at' => date('Y-m-d H:i:s'),
]);
}
}
}
public function down()
{
if ($this->tableExists('auth_permissions_users')) {
$this->db->table('auth_permissions_users')->where('permission', self::PERMISSION)->delete();
}
}
private function tableExists(string $table): bool
{
return $this->db->query('SHOW TABLES LIKE ' . $this->db->escape($table))->getRowArray() !== null;
}
}

View File

@ -3,14 +3,15 @@
namespace App\Models;
use CodeIgniter\Database\BaseConnection;
use RuntimeException;
class PaymentModel
{
private BaseConnection $db;
public function __construct()
public function __construct(?BaseConnection $db = null)
{
$this->db = db_connect();
$this->db = $db ?? db_connect();
}
public function insertPayment(array $data): int
@ -24,4 +25,59 @@ class PaymentModel
{
return (bool) $this->db->table('payment_history')->where('id', $id)->update($data);
}
public function findPayment(int $id): ?array
{
return $this->db->table('payment_history')->where('id', $id)->get(1)->getRowArray();
}
public function assertEditable(array $payment): void
{
$cashStatus = strtolower(trim((string) ($payment['cash_submission_status'] ?? 'open')));
if ($cashStatus !== '' && $cashStatus !== 'open') {
throw new RuntimeException('This payment is already included in a cash submission and cannot be edited.');
}
if ((int) ($payment['cash_submission_scroll_id'] ?? 0) > 0) {
throw new RuntimeException('This payment is linked to a cash submission and cannot be edited.');
}
$bankStatus = strtolower(trim((string) ($payment['bank_reconcile_status'] ?? '')));
if ($bankStatus !== '' || (int) ($payment['bank_statement_entry_id'] ?? 0) > 0 || (int) ($payment['bank_settlement_entry_id'] ?? 0) > 0) {
throw new RuntimeException('This payment is already reconciled with the bank and cannot be edited.');
}
}
public function correctPayment(int $id, array $changes, int $updateType, string $actor, string $remark): array
{
$payment = $this->findPayment($id);
if ($payment === null) {
throw new RuntimeException('Payment record not found.');
}
$this->assertEditable($payment);
$fields = $this->db->getFieldNames('payment_history') ?? [];
$changes = array_intersect_key($changes, array_flip($fields));
if ($changes === []) {
throw new RuntimeException('No supported payment fields were provided.');
}
if (! $this->db->tableExists('payment_history_log')) {
throw new RuntimeException('Payment audit log table is unavailable.');
}
$this->db->transStart();
$this->db->table('payment_history')->where('id', $id)->update($changes);
$this->db->table('payment_history_log')->insert([
'pay_id' => $id,
'update_type' => $updateType,
'update_by' => mb_substr($actor, 0, 50),
'update_remark' => mb_substr($remark, 0, 200),
]);
$this->db->transComplete();
if (! $this->db->transStatus()) {
throw new RuntimeException('Unable to update the payment record.');
}
return $this->findPayment($id) ?? $payment;
}
}

View File

@ -1,3 +1,4 @@
<div id="maindiv">
<div class="card">
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
<h3 class="card-title mb-0">Users</h3>
@ -167,4 +168,5 @@
});
});
})();
</script>
</script>
</div>

View File

@ -92,16 +92,19 @@ if ($user && method_exists($user, 'can')) {
|| $user->can('finance.bank.deposit.create')
|| $user->can('finance.bank.audit')
|| $user->can('finance.bank.statement.update')
|| $user->can('finance.payment.edit')
|| $user->can('finance.*');
}
$canFinanceBilling = false;
$canFinanceAccounts = false;
$canFinanceBank = false;
$canFinancePaymentEdit = false;
if ($user && method_exists($user, 'can')) {
$canFinanceBilling = $user->can('finance.cash.billing.submit') || $user->can('finance.*');
$canFinanceAccounts = $user->can('finance.cash.accounts.accept') || $user->can('finance.cash.accounts.verify') || $user->can('finance.*');
$canFinanceBank = $user->can('finance.bank.deposit.create') || $user->can('finance.bank.audit') || $user->can('finance.bank.statement.update') || $user->can('finance.*');
$canFinancePaymentEdit = $user->can('finance.payment.edit') || $user->can('finance.*');
}
$canDoctorWork = false;
@ -294,6 +297,14 @@ if (! $canChargesSettings && $user && method_exists($user, 'inGroup')) {
</a>
</li>
<?php } ?>
<?php if ($canFinancePaymentEdit) { ?>
<li class="nav-item">
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Payment') ?>','Payment Edit')">
<i class="bi bi-pencil-square"></i>
<span>Payment Edit</span>
</a>
</li>
<?php } ?>
<?php if ($canFinanceBank) { ?>
<li class="nav-item">
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Finance/bank_audit') ?>','Bank Payment Audit')">

View File

@ -0,0 +1,101 @@
<?php
$payment = isset($payment) && is_array($payment) ? $payment : [];
$context = isset($context) && is_array($context) ? $context : [];
$users = isset($users) && is_array($users) ? $users : [];
$bank_sources = isset($bank_sources) && is_array($bank_sources) ? $bank_sources : [];
$paymentId = (int) ($payment['id'] ?? 0);
$paymentMode = (int) ($payment['payment_mode'] ?? 0);
$updateById = (int) ($payment['update_by_id'] ?? 0);
$bankSourceId = (int) ($payment['pay_bank_id'] ?? 0);
$cashStatus = strtolower(trim((string) ($payment['cash_submission_status'] ?? 'open')));
$bankStatus = strtolower(trim((string) ($payment['bank_reconcile_status'] ?? '')));
$locked = ($cashStatus !== '' && $cashStatus !== 'open')
|| (int) ($payment['cash_submission_scroll_id'] ?? 0) > 0
|| $bankStatus !== ''
|| (int) ($payment['bank_statement_entry_id'] ?? 0) > 0
|| (int) ($payment['bank_settlement_entry_id'] ?? 0) > 0;
?>
<div class="card border-primary">
<div class="card-body pt-3">
<div class="d-flex justify-content-between align-items-start gap-3 mb-3">
<div>
<h5 class="mb-1">Payment ID: <?= esc((string) $paymentId) ?></h5>
<div class="text-muted small"><?= esc((string) ($payment['payment_date'] ?? $payment['insert_time'] ?? '')) ?></div>
</div>
<span class="badge <?= $paymentMode === 1 ? 'bg-success' : 'bg-primary' ?>"><?= $paymentMode === 1 ? 'CASH' : 'BANK / ONLINE' ?></span>
</div>
<div class="row g-3 mb-3 small">
<div class="col-md-3"><strong>Invoice Type</strong><br><?= esc((string) ($context['type'] ?? '-')) ?></div>
<div class="col-md-3"><strong>Invoice / Visit No.</strong><br><?= esc((string) ($context['code'] ?? '-')) ?></div>
<div class="col-md-3"><strong>Patient</strong><br><?= esc((string) ($context['patient'] ?? '-')) ?></div>
<div class="col-md-3"><strong>Amount</strong><br>Rs. <?= esc(number_format((float) ($payment['amount'] ?? 0), 2)) ?></div>
</div>
<?php if ($locked): ?>
<div class="alert alert-warning">This payment is already submitted or bank-reconciled. Corrections are locked to preserve the finance audit trail.</div>
<?php endif; ?>
<input type="hidden" id="payment-edit-id" value="<?= esc((string) $paymentId) ?>">
<div id="payment-edit-action-message"></div>
<div class="mb-3">
<label for="payment-edit-reason" class="form-label">Correction Reason <span class="text-danger">*</span></label>
<input class="form-control" id="payment-edit-reason" maxlength="120" placeholder="Why is this payment being corrected?" <?= $locked ? 'disabled' : '' ?> required>
</div>
<div class="border rounded p-3 mb-3">
<h6>Payment Mode</h6>
<?php if ($paymentMode === 1): ?>
<div class="row g-2 align-items-end">
<div class="col-md-5">
<label for="payment-edit-bank-source" class="form-label">Bank / Online Source</label>
<select class="form-select" id="payment-edit-bank-source" <?= $locked ? 'disabled' : '' ?>>
<option value="">Select source</option>
<?php foreach ($bank_sources as $source): ?>
<option value="<?= esc((string) $source['id']) ?>"><?= esc((string) $source['label']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label for="payment-edit-transaction" class="form-label">Transaction ID / Reference</label>
<input class="form-control" id="payment-edit-transaction" maxlength="100" <?= $locked ? 'disabled' : '' ?>>
</div>
<div class="col-md-3 d-grid"><button class="btn btn-primary" id="payment-edit-to-bank" <?= $locked ? 'disabled' : '' ?>>Change to Bank/Online</button></div>
</div>
<?php else: ?>
<div class="row g-2 align-items-end">
<div class="col-md-6"><strong>Source:</strong> <?= esc((string) ($payment['card_bank'] ?? 'Bank/Online')) ?><br><strong>Reference:</strong> <?= esc((string) ($payment['card_tran_id'] ?? '-')) ?></div>
<div class="col-md-3 d-grid"><button class="btn btn-primary" id="payment-edit-to-cash" <?= $locked ? 'disabled' : '' ?>>Change to Cash</button></div>
</div>
<?php endif; ?>
</div>
<div class="border rounded p-3 mb-3">
<h6>Recorded User</h6>
<div class="row g-2 align-items-end">
<div class="col-md-6">
<label for="payment-edit-user" class="form-label">User</label>
<select class="form-select" id="payment-edit-user" <?= $locked ? 'disabled' : '' ?>>
<?php foreach ($users as $user): ?>
<option value="<?= esc((string) $user['id']) ?>" <?= (int) $user['id'] === $updateById ? 'selected' : '' ?>><?= esc((string) $user['username']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3 d-grid"><button class="btn btn-primary" id="payment-edit-change-user" <?= $locked ? 'disabled' : '' ?>>Change User</button></div>
</div>
</div>
<div class="border rounded p-3">
<h6>Payment Amount</h6>
<div class="row g-2 align-items-end">
<div class="col-md-4">
<label for="payment-edit-amount" class="form-label">Correct Amount</label>
<input class="form-control" type="number" min="0.01" step="0.01" id="payment-edit-amount" value="<?= esc(number_format((float) ($payment['amount'] ?? 0), 2, '.', '')) ?>" <?= $locked ? 'disabled' : '' ?>>
</div>
<div class="col-md-3 d-grid"><button class="btn btn-primary" id="payment-edit-change-amount" <?= $locked ? 'disabled' : '' ?>>Change Amount</button></div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,124 @@
<div class="card mb-3">
<div class="card-body py-3">
<h5 class="card-title mb-3">Payment Edit</h5>
<form id="payment-edit-search-form" method="post">
<?= csrf_field() ?>
<div class="row g-2 align-items-end">
<div class="col-md-10">
<label for="payment-edit-search-id" class="form-label">Payment ID</label>
<input class="form-control" type="number" min="1" step="1" id="payment-edit-search-id" name="txtsearch" placeholder="Enter Payment ID" required>
</div>
<div class="col-md-2 d-grid">
<button type="submit" class="btn btn-primary"><i class="bi bi-search me-1"></i>Search</button>
</div>
</div>
</form>
</div>
</div>
<div id="payment-edit-result"></div>
<script>
(function () {
if (!window.jQuery) {
return;
}
const $doc = window.jQuery(document);
let actionInFlight = false;
let csrfHash = '<?= esc(csrf_hash()) ?>';
const csrfToken = '<?= esc(csrf_token()) ?>';
function showResult(html) {
window.jQuery('#payment-edit-result').html(html);
}
function showError(xhr) {
const message = xhr.responseJSON && xhr.responseJSON.message
? xhr.responseJSON.message
: 'Unable to update payment record.';
window.jQuery('#payment-edit-action-message').html('<div class="alert alert-danger mb-3">' + window.jQuery('<div>').text(message).html() + '</div>');
if (xhr.responseJSON && xhr.responseJSON.csrf_hash) {
csrfHash = xhr.responseJSON.csrf_hash;
}
}
function captureSearchToken(xhr) {
const nextToken = xhr.getResponseHeader('X-CSRF-TOKEN');
if (nextToken) {
csrfHash = nextToken;
window.jQuery('#payment-edit-search-form input[name="' + csrfToken + '"]').val(nextToken);
}
}
function correctionPayload(extra) {
return Object.assign({
pay_id: window.jQuery('#payment-edit-id').val(),
correction_reason: window.jQuery('#payment-edit-reason').val(),
[csrfToken]: csrfHash
}, extra || {});
}
function updatePayment(url, payload, confirmation) {
if (actionInFlight || !confirm(confirmation)) {
return;
}
actionInFlight = true;
window.jQuery('#payment-edit-action-message').html('<div class="alert alert-info mb-3">Updating payment...</div>');
window.jQuery.post(url, payload)
.done(function (response) {
if (response.csrf_hash) {
csrfHash = response.csrf_hash;
}
showResult('<div class="alert alert-success">' + window.jQuery('<div>').text(response.message).html() + '</div>' + response.html);
})
.fail(showError)
.always(function () {
actionInFlight = false;
});
}
$doc.off('submit.paymentGeneralEdit', '#payment-edit-search-form')
.on('submit.paymentGeneralEdit', '#payment-edit-search-form', function (event) {
event.preventDefault();
showResult('<div class="alert alert-info">Loading payment...</div>');
window.jQuery.post('<?= base_url('Payment/payment_record') ?>', window.jQuery(this).serialize())
.done(function (html, textStatus, xhr) {
captureSearchToken(xhr);
showResult(html);
})
.fail(function (xhr) {
captureSearchToken(xhr);
showResult(xhr.responseText || '<div class="alert alert-danger">Unable to find payment.</div>');
});
});
$doc.off('click.paymentGeneralEdit', '#payment-edit-to-bank')
.on('click.paymentGeneralEdit', '#payment-edit-to-bank', function () {
updatePayment('<?= base_url('Payment/change_to_bank') ?>', correctionPayload({
cbo_pay_type: window.jQuery('#payment-edit-bank-source').val(),
input_card_tran: window.jQuery('#payment-edit-transaction').val()
}), 'Change this payment to Bank/Online?');
});
$doc.off('click.paymentGeneralEdit', '#payment-edit-to-cash')
.on('click.paymentGeneralEdit', '#payment-edit-to-cash', function () {
updatePayment('<?= base_url('Payment/change_to_cash') ?>', correctionPayload(), 'Change this payment to Cash?');
});
$doc.off('click.paymentGeneralEdit', '#payment-edit-change-user')
.on('click.paymentGeneralEdit', '#payment-edit-change-user', function () {
updatePayment('<?= base_url('Payment/change_user') ?>', correctionPayload({
user_list: window.jQuery('#payment-edit-user').val()
}), 'Change the user recorded for this payment?');
});
$doc.off('click.paymentGeneralEdit', '#payment-edit-change-amount')
.on('click.paymentGeneralEdit', '#payment-edit-change-amount', function () {
updatePayment('<?= base_url('Payment/change_amount') ?>', correctionPayload({
change_value: window.jQuery('#payment-edit-amount').val()
}), 'Change the amount recorded for this payment?');
});
})();
</script>