From 447b86cad8ec15fd3949d741ab9beff0cbe21706 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Fri, 8 Aug 2025 15:33:42 +0000 Subject: [PATCH] Bug 40625: Fix duplicate cashup records via POST/REDIRECT/GET pattern The cashup functionality on both /pos/register.pl and /pos/registers.pl pages was using GET requests via anchor tags, which could cause duplicate cashup records when users refreshed the page after performing a cashup. This patch implements proper POST/REDIRECT/GET pattern with CSRF protection: 1. Convert anchor tag buttons to proper HTML forms with submit buttons 2. Add CSRF token protection to all cashup operations 3. Change operation from 'cashup' to 'cud-cashup' for CSRF compliance 4. Implement redirects after successful cashup operations to prevent resubmission 5. Update JavaScript to populate form fields instead of href attributes This ensures cashup operations follow HTTP semantics properly (POST for state changes) and prevents duplicate submissions while maintaining the existing user experience. Test plan: 1. Apply patch and restart services 2. Go to Point of Sale > Cash registers 3. Click "Record cashup" on any register - should open modal 4. Click "Confirm" - should perform cashup and reload page 5. Refresh the page - should NOT create duplicate cashup record 6. Go to Point of Sale > Register for individual register 7. Click "Record cashup" - should open modal 8. Click "Confirm" - should perform cashup and reload page 9. Refresh the page - should NOT create duplicate cashup record 10. On registers page, try "Cashup all" button - should work correctly 11. Verify all existing functionality (modals, summaries) still works 12. Check browser developer tools - all requests should be POST for cashup operations Signed-off-by: Owen Leonard Signed-off-by: Laura_Escamilla Signed-off-by: Lucas Gass (cherry picked from commit 26f1f91a13294a31ec7a0245aed848fe73918a9e) Signed-off-by: Paul Derscheid (cherry picked from commit d4d9a849b53517f5feeb62a39d8ce8a6fc202fa7) Signed-off-by: Fridolin Somers --- .../prog/en/modules/pos/register.tt | 26 +++-- .../prog/en/modules/pos/registers.tt | 99 +++++++++++-------- pos/register.pl | 9 +- pos/registers.pl | 9 +- 4 files changed, 88 insertions(+), 55 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 e449fef31c..828ac21e6d 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/pos/register.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/pos/register.tt @@ -286,8 +286,10 @@ - - + + + + + + + + + - - + + + - - + + + [% INCLUDE 'modals/cashup_summary.inc' %] @@ -223,7 +240,7 @@ var rfloat = button.data('float'); $('#floatc').text(rfloat); var rid = button.data('registerid'); - $('#cashup_confirm').attr("href", '/cgi-bin/koha/pos/registers.pl?op=cashup®isterid='+rid); + $('#cashup_registerid').val(rid); }); }); diff --git a/pos/register.pl b/pos/register.pl index 53bb5d2325..7ab0f887ff 100755 --- a/pos/register.pl +++ b/pos/register.pl @@ -104,7 +104,7 @@ else { $template->param( trange_t => $end, ); my $op = $input->param('op') // ''; - if ( $op eq 'cashup' ) { + if ( $op eq 'cud-cashup' ) { if ( $logged_in_user->has_permission( { cash_management => 'cashup' } ) ) { $cash_register->add_cashup( { @@ -112,8 +112,11 @@ else { amount => $cash_register->outstanding_accountlines->total } ); - } - else { + + # 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_cashup_permission => 1 ); } } diff --git a/pos/registers.pl b/pos/registers.pl index 7a9c3a1699..80f14b0778 100755 --- a/pos/registers.pl +++ b/pos/registers.pl @@ -55,7 +55,7 @@ else { } my $op = $input->param('op') // ''; -if ( $op eq 'cashup' ) { +if ( $op eq 'cud-cashup' ) { if ( $logged_in_user->has_permission( { cash_management => 'cashup' } ) ) { my $registerid = $input->param('registerid'); if ($registerid) { @@ -77,8 +77,11 @@ if ( $op eq 'cashup' ) { ); } } - } - else { + + # Redirect to prevent duplicate submissions (POST/REDIRECT/GET pattern) + print $input->redirect("/cgi-bin/koha/pos/registers.pl"); + exit; + } else { $template->param( error_cashup_permission => 1 ); } }