From 8dba205b9aa8105af499c09a826e372c9bb44160 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Wed, 22 Apr 2026 10:21:37 +0100 Subject: [PATCH] Bug 41819: (follow-up) Reject 'AC' refunds for anonymous transactions server-side The previous patch hides the 'Account credit' option in the refund modal when the accountline has no borrowernumber, but that is a UI convenience only. A crafted POST (or a stale page) could still submit refund_type=AC against an anonymous accountline, which would create a REFUND credit with no patron to attach it to and leave the register unbalanced because the payout branch is skipped. Add a server-side guard in pos/register.pl that refuses the operation and surfaces a new error_refund_anonymous_ac flag, rendered in pos/register.tt in the same style as the other refund errors. Test plan: 1. Apply the patch 2. Log in as a user with cash_management/anonymous_refund permission 3. Record an anonymous sale against a register 4. Using the browser devtools (or curl), POST to pos/register.pl with op=cud-refund, refund_type=AC and the accountline id of the sale 5. => Confirm the request is rejected and the "Account credit refunds are not available for anonymous transactions" message is shown 6. => Confirm no REFUND accountline was created and the register balance is unchanged 7. Repeat the POST with refund_type=CASH (or any non-AC type) and confirm the refund + payout are created normally Signed-off-by: Lisette Scheer Signed-off-by: Lucas Gass --- .../prog/en/modules/pos/register.tt | 4 ++ pos/register.pl | 62 +++++++++++-------- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/pos/register.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/pos/register.tt index 3260b11cb0..b95c89295d 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/pos/register.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/pos/register.tt @@ -52,6 +52,10 @@
You do not have permission to perform refund actions.
[% END %] + [% IF ( error_refund_anonymous_ac ) %] +
Account credit refunds are not available for anonymous transactions. Please choose a different refund type.
+ [% END %] + [% IF ( CAN_user_cash_management_cashup ) %]
diff --git a/pos/register.pl b/pos/register.pl index bcf0b7b347..ac101f72c3 100755 --- a/pos/register.pl +++ b/pos/register.pl @@ -126,39 +126,47 @@ if ( !$registers->count ) { my $refund_type = $input->param('refund_type'); my $accountline = Koha::Account::Lines->find($accountline_id); - $schema->txn_do( - sub { - my $refund = $accountline->reduce( - { - reduction_type => 'REFUND', - branch => $library_id, - staff_id => $logged_in_user->id, - interface => 'intranet', - amount => $amount - } - ); + # Refuse 'AC' (Account credit) refunds for anonymous accountlines: + # there is no patron account to credit, so the refund would be + # orphaned and the register would be left unbalanced. + if ( $refund_type eq 'AC' && !$accountline->borrowernumber ) { + $template->param( error_refund_anonymous_ac => 1 ); + } else { + $schema->txn_do( + sub { - # Only create a payout if refund_type is not 'AC' (Account Credit) - # When 'AC' is selected, the refund stays as credit on the patron's account - unless ( $refund_type eq 'AC' ) { - my $payout = $refund->payout( + my $refund = $accountline->reduce( { - payout_type => $refund_type, - branch => $library_id, - staff_id => $logged_in_user->id, - cash_register => $cash_register->id, - interface => 'intranet', - amount => $amount + reduction_type => 'REFUND', + branch => $library_id, + staff_id => $logged_in_user->id, + interface => 'intranet', + amount => $amount } ); - } - } - ); - # Redirect to prevent duplicate submissions (POST/REDIRECT/GET pattern) - print $input->redirect( "/cgi-bin/koha/pos/register.pl?registerid=" . $registerid ); - exit; + # Only create a payout if refund_type is not 'AC' (Account Credit) + # When 'AC' is selected, the refund stays as credit on the patron's account + unless ( $refund_type eq 'AC' ) { + my $payout = $refund->payout( + { + payout_type => $refund_type, + branch => $library_id, + staff_id => $logged_in_user->id, + cash_register => $cash_register->id, + interface => 'intranet', + amount => $amount + } + ); + } + } + ); + + # Redirect to prevent duplicate submissions (POST/REDIRECT/GET pattern) + print $input->redirect( "/cgi-bin/koha/pos/register.pl?registerid=" . $registerid ); + exit; + } } else { $template->param( error_refund_permission => 1 ); }