Bugzilla – Attachment 185290 Details for
Bug 40445
Point of Sale reconciliation input during daily summaries
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 40445: Add unit tests for cashup reconciliation functionality
Bug-40445-Add-unit-tests-for-cashup-reconciliation.patch (text/plain), 17.64 KB, created by
Martin Renvoize (ashimema)
on 2025-08-08 17:29:10 UTC
(
hide
)
Description:
Bug 40445: Add unit tests for cashup reconciliation functionality
Filename:
MIME Type:
Creator:
Martin Renvoize (ashimema)
Created:
2025-08-08 17:29:10 UTC
Size:
17.64 KB
patch
obsolete
>From b68b347e5a48ace20e11184968d02e79ecbee404 Mon Sep 17 00:00:00 2001 >From: Martin Renvoize <martin.renvoize@openfifth.co.uk> >Date: Fri, 8 Aug 2025 13:18:59 +0100 >Subject: [PATCH] Bug 40445: Add unit tests for cashup reconciliation > functionality > >This patch adds comprehensive unit tests for the cashup reconciliation system: > >- Tests for balanced cashups (no surplus/deficit accountlines created) >- Tests for surplus scenarios with CASHUP_SURPLUS credit creation >- Tests for deficit scenarios with CASHUP_DEFICIT debit creation >- Tests for user note handling and storage in reconciliation accountlines >- Tests for transaction integrity ensuring atomicity >- Tests for note sanitization (whitespace trimming, empty note handling) > >The tests ensure that: >- Reconciliation accountlines are only created when there's a discrepancy >- Surplus/deficit amounts are calculated and stored correctly >- User notes are properly combined with system reconciliation details >- Database transactions maintain consistency >- Account types are linked correctly to cash registers >--- > t/db_dependent/Koha/Cash/Register.t | 435 +++++++++++++++++++++++++++- > 1 file changed, 434 insertions(+), 1 deletion(-) > >diff --git a/t/db_dependent/Koha/Cash/Register.t b/t/db_dependent/Koha/Cash/Register.t >index ee4f2fc0652..4267e94babd 100755 >--- a/t/db_dependent/Koha/Cash/Register.t >+++ b/t/db_dependent/Koha/Cash/Register.t >@@ -20,11 +20,14 @@ > use Modern::Perl; > > use Test::NoWarnings; >-use Test::More tests => 5; >+use Test::More tests => 6; > > use Test::Exception; > > use Koha::Database; >+use Koha::Account; >+use Koha::Account::CreditTypes; >+use Koha::Account::DebitTypes; > > use t::lib::TestBuilder; > >@@ -312,3 +315,433 @@ subtest 'cashup' => sub { > > $schema->storage->txn_rollback; > }; >+ >+subtest 'cashup_reconciliation' => sub { >+ plan tests => 5; >+ >+ $schema->storage->txn_begin; >+ >+ # Ensure required account types for reconciliation exist (they should already exist from mandatory data) >+ use Koha::Account::CreditTypes; >+ use Koha::Account::DebitTypes; >+ >+ my $surplus_credit_type = Koha::Account::CreditTypes->find( { code => 'CASHUP_SURPLUS' } ); >+ if ( !$surplus_credit_type ) { >+ $surplus_credit_type = $builder->build_object( >+ { >+ class => 'Koha::Account::CreditTypes', >+ value => { >+ code => 'CASHUP_SURPLUS', >+ description => 'Cash register surplus found during cashup', >+ can_be_added_manually => 0, >+ credit_number_enabled => 0, >+ is_system => 1, >+ archived => 0, >+ } >+ } >+ ); >+ } >+ >+ my $deficit_debit_type = Koha::Account::DebitTypes->find( { code => 'CASHUP_DEFICIT' } ); >+ if ( !$deficit_debit_type ) { >+ $deficit_debit_type = $builder->build_object( >+ { >+ class => 'Koha::Account::DebitTypes', >+ value => { >+ code => 'CASHUP_DEFICIT', >+ description => 'Cash register deficit found during cashup', >+ can_be_invoiced => 0, >+ can_be_sold => 0, >+ default_amount => undef, >+ is_system => 1, >+ archived => 0, >+ restricts_checkouts => 0, >+ } >+ } >+ ); >+ } >+ >+ my $register = $builder->build_object( { class => 'Koha::Cash::Registers' } ); >+ my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ >+ # Create some outstanding accountlines to establish expected amount >+ my $accountline1 = $builder->build_object( >+ { >+ class => 'Koha::Account::Lines', >+ value => { >+ register_id => $register->id, >+ borrowernumber => $patron->id, >+ amount => -10.00, # Credit (payment) >+ credit_type_code => 'PAYMENT', >+ debit_type_code => undef, >+ } >+ } >+ ); >+ my $accountline2 = $builder->build_object( >+ { >+ class => 'Koha::Account::Lines', >+ value => { >+ register_id => $register->id, >+ borrowernumber => $patron->id, >+ amount => -5.00, # Credit (payment) >+ credit_type_code => 'PAYMENT', >+ debit_type_code => undef, >+ } >+ } >+ ); >+ >+ my $expected_amount = $register->outstanding_accountlines->total; # Should be -15.00 >+ >+ subtest 'balanced_cashup' => sub { >+ plan tests => 3; >+ >+ # Test exact match - no surplus/deficit accountlines should be created >+ my $amount = abs($expected_amount); # 15.00 actual matches 15.00 expected >+ >+ my $cashup = $register->add_cashup( >+ { >+ manager_id => $patron->id, >+ amount => $amount >+ } >+ ); >+ >+ ok( $cashup, 'Cashup created successfully' ); >+ is( sprintf( '%.0f', $cashup->amount ), sprintf( '%.0f', $amount ), 'Cashup amount matches actual amount' ); >+ >+ # Check no surplus/deficit accountlines were created >+ my $reconciliation_lines = Koha::Account::Lines->search( >+ { >+ register_id => $register->id, >+ '-or' => [ >+ { credit_type_code => 'CASHUP_SURPLUS' }, >+ { debit_type_code => 'CASHUP_DEFICIT' } >+ ] >+ } >+ ); >+ >+ is( $reconciliation_lines->count, 0, 'No reconciliation accountlines created for balanced cashup' ); >+ }; >+ >+ subtest 'surplus_cashup' => sub { >+ plan tests => 7; >+ >+ $schema->storage->txn_begin; >+ >+ my $register2 = $builder->build_object( { class => 'Koha::Cash::Registers' } ); >+ my $accountline3 = $builder->build_object( >+ { >+ class => 'Koha::Account::Lines', >+ value => { >+ register_id => $register2->id, >+ borrowernumber => $patron->id, >+ amount => -20.00, # Credit (payment) >+ credit_type_code => 'PAYMENT', >+ debit_type_code => undef, >+ } >+ } >+ ); >+ >+ my $expected = abs( $register2->outstanding_accountlines->total ); # 20.00 >+ my $actual = 25.00; # 5.00 surplus >+ my $surplus = $actual - $expected; >+ >+ my $cashup = $register2->add_cashup( >+ { >+ manager_id => $patron->id, >+ amount => $actual >+ } >+ ); >+ >+ ok( $cashup, 'Surplus cashup created successfully' ); >+ is( sprintf( '%.0f', $cashup->amount ), sprintf( '%.0f', $actual ), 'Cashup amount matches actual amount' ); >+ >+ # Check surplus accountline was created >+ my $surplus_lines = Koha::Account::Lines->search( >+ { >+ register_id => $register2->id, >+ credit_type_code => 'CASHUP_SURPLUS' >+ } >+ ); >+ >+ is( $surplus_lines->count, 1, 'One surplus accountline created' ); >+ >+ my $surplus_line = $surplus_lines->next; >+ is( >+ sprintf( '%.0f', $surplus_line->amount ), sprintf( '%.0f', -$surplus ), >+ 'Surplus amount is correct (negative for credit)' >+ ); >+ >+ # Note should be undef for surplus without user note >+ is( $surplus_line->note, undef, 'No note for surplus without user reconciliation note' ); >+ >+ # Test surplus with user note >+ my $register_with_note = $builder->build_object( { class => 'Koha::Cash::Registers' } ); >+ my $accountline_with_note = $builder->build_object( >+ { >+ class => 'Koha::Account::Lines', >+ value => { >+ register_id => $register_with_note->id, >+ borrowernumber => $patron->id, >+ amount => -10.00, >+ credit_type_code => 'PAYMENT', >+ debit_type_code => undef, >+ } >+ } >+ ); >+ >+ my $cashup_with_note = $register_with_note->add_cashup( >+ { >+ manager_id => $patron->id, >+ amount => 15.00, # 5.00 surplus >+ reconciliation_note => 'Found extra \x{00A3}5 under the till drawer' # £5 in UTF-8 >+ } >+ ); >+ >+ my $surplus_with_note = Koha::Account::Lines->search( >+ { >+ register_id => $register_with_note->id, >+ credit_type_code => 'CASHUP_SURPLUS' >+ } >+ )->next; >+ >+ like( >+ $surplus_with_note->note, qr/Found extra .+5 under the till drawer/, >+ 'User note included in surplus accountline' >+ ); >+ is( >+ $surplus_with_note->note, 'Found extra \x{00A3}5 under the till drawer', >+ 'Only user note stored (no base reconciliation info)' >+ ); >+ >+ $schema->storage->txn_rollback; >+ }; >+ >+ subtest 'deficit_cashup' => sub { >+ plan tests => 7; >+ >+ $schema->storage->txn_begin; >+ >+ my $register3 = $builder->build_object( { class => 'Koha::Cash::Registers' } ); >+ my $accountline4 = $builder->build_object( >+ { >+ class => 'Koha::Account::Lines', >+ value => { >+ register_id => $register3->id, >+ borrowernumber => $patron->id, >+ amount => -30.00, # Credit (payment) >+ credit_type_code => 'PAYMENT', >+ debit_type_code => undef, >+ } >+ } >+ ); >+ >+ my $expected = abs( $register3->outstanding_accountlines->total ); # 30.00 >+ my $actual = 25.00; # 5.00 deficit >+ my $deficit = $expected - $actual; >+ >+ my $cashup = $register3->add_cashup( >+ { >+ manager_id => $patron->id, >+ amount => $actual >+ } >+ ); >+ >+ ok( $cashup, 'Deficit cashup created successfully' ); >+ is( sprintf( '%.0f', $cashup->amount ), sprintf( '%.0f', $actual ), 'Cashup amount matches actual amount' ); >+ >+ # Check deficit accountline was created >+ my $deficit_lines = Koha::Account::Lines->search( >+ { >+ register_id => $register3->id, >+ debit_type_code => 'CASHUP_DEFICIT' >+ } >+ ); >+ >+ is( $deficit_lines->count, 1, 'One deficit accountline created' ); >+ >+ my $deficit_line = $deficit_lines->next; >+ is( >+ sprintf( '%.0f', $deficit_line->amount ), sprintf( '%.0f', $deficit ), >+ 'Deficit amount is correct (positive for debit)' >+ ); >+ >+ # Note should be undef for deficit without user note >+ is( $deficit_line->note, undef, 'No note for deficit without user reconciliation note' ); >+ >+ # Test deficit with user note >+ my $register_deficit_note = $builder->build_object( { class => 'Koha::Cash::Registers' } ); >+ my $accountline_deficit_note = $builder->build_object( >+ { >+ class => 'Koha::Account::Lines', >+ value => { >+ register_id => $register_deficit_note->id, >+ borrowernumber => $patron->id, >+ amount => -20.00, >+ credit_type_code => 'PAYMENT', >+ debit_type_code => undef, >+ } >+ } >+ ); >+ >+ my $cashup_deficit_note = $register_deficit_note->add_cashup( >+ { >+ manager_id => $patron->id, >+ amount => 15.00, # 5.00 deficit >+ reconciliation_note => 'Till was short, possibly due to incorrect change given' >+ } >+ ); >+ >+ my $deficit_with_note = Koha::Account::Lines->search( >+ { >+ register_id => $register_deficit_note->id, >+ debit_type_code => 'CASHUP_DEFICIT' >+ } >+ )->next; >+ >+ like( >+ $deficit_with_note->note, qr/Till was short, possibly due to incorrect change given/, >+ 'User note included in deficit accountline' >+ ); >+ is( >+ $deficit_with_note->note, 'Till was short, possibly due to incorrect change given', >+ 'Only user note stored (no base reconciliation info)' >+ ); >+ >+ $schema->storage->txn_rollback; >+ }; >+ >+ subtest 'transaction_integrity' => sub { >+ plan tests => 4; >+ >+ $schema->storage->txn_begin; >+ >+ my $register4 = $builder->build_object( { class => 'Koha::Cash::Registers' } ); >+ my $accountline5 = $builder->build_object( >+ { >+ class => 'Koha::Account::Lines', >+ value => { >+ register_id => $register4->id, >+ borrowernumber => $patron->id, >+ amount => -10.00, >+ credit_type_code => 'PAYMENT', >+ debit_type_code => undef, >+ } >+ } >+ ); >+ >+ my $initial_accountline_count = Koha::Account::Lines->search( { register_id => $register4->id } )->count; >+ >+ my $initial_action_count = $register4->cashups->count; >+ >+ # Test successful transaction >+ my $cashup = $register4->add_cashup( >+ { >+ manager_id => $patron->id, >+ amount => 15.00 # Creates surplus >+ } >+ ); >+ >+ # Check both cashup action and surplus accountline were created >+ is( $register4->cashups->count, $initial_action_count + 1, 'Cashup action created' ); >+ >+ my $final_accountline_count = Koha::Account::Lines->search( { register_id => $register4->id } )->count; >+ >+ is( $final_accountline_count, $initial_accountline_count + 1, 'Surplus accountline created' ); >+ >+ # Verify the new accountline is the surplus >+ my $surplus_line = Koha::Account::Lines->search( >+ { >+ register_id => $register4->id, >+ credit_type_code => 'CASHUP_SURPLUS' >+ } >+ )->next; >+ >+ ok( $surplus_line, 'Surplus accountline exists' ); >+ is( $surplus_line->register_id, $register4->id, 'Surplus linked to correct register' ); >+ >+ $schema->storage->txn_rollback; >+ }; >+ >+ subtest 'note_handling' => sub { >+ plan tests => 2; >+ >+ $schema->storage->txn_begin; >+ >+ my $register_note_test = $builder->build_object( { class => 'Koha::Cash::Registers' } ); >+ my $accountline_note_test = $builder->build_object( >+ { >+ class => 'Koha::Account::Lines', >+ value => { >+ register_id => $register_note_test->id, >+ borrowernumber => $patron->id, >+ amount => -10.00, >+ credit_type_code => 'PAYMENT', >+ debit_type_code => undef, >+ } >+ } >+ ); >+ >+ # Test balanced cashup with note (should not create surplus/deficit) >+ my $balanced_cashup = $register_note_test->add_cashup( >+ { >+ manager_id => $patron->id, >+ amount => 10.00, # Balanced >+ reconciliation_note => 'This note should be ignored for balanced cashup' >+ } >+ ); >+ >+ my $balanced_reconciliation_lines = Koha::Account::Lines->search( >+ { >+ register_id => $register_note_test->id, >+ '-or' => [ >+ { credit_type_code => 'CASHUP_SURPLUS' }, >+ { debit_type_code => 'CASHUP_DEFICIT' } >+ ] >+ } >+ ); >+ >+ is( >+ $balanced_reconciliation_lines->count, 0, >+ 'No reconciliation accountlines created for balanced cashup with note' >+ ); >+ >+ # Test empty/whitespace note handling >+ my $register_empty_note = $builder->build_object( { class => 'Koha::Cash::Registers' } ); >+ my $accountline_empty_note = $builder->build_object( >+ { >+ class => 'Koha::Account::Lines', >+ value => { >+ register_id => $register_empty_note->id, >+ borrowernumber => $patron->id, >+ amount => -10.00, >+ credit_type_code => 'PAYMENT', >+ debit_type_code => undef, >+ } >+ } >+ ); >+ >+ my $empty_note_cashup = $register_empty_note->add_cashup( >+ { >+ manager_id => $patron->id, >+ amount => 12.00, # 2.00 surplus >+ reconciliation_note => ' ' # Whitespace only >+ } >+ ); >+ >+ my $empty_note_surplus = Koha::Account::Lines->search( >+ { >+ register_id => $register_empty_note->id, >+ credit_type_code => 'CASHUP_SURPLUS' >+ } >+ )->next; >+ >+ is( >+ $empty_note_surplus->note, undef, >+ 'No note stored when user note is empty/whitespace' >+ ); >+ >+ $schema->storage->txn_rollback; >+ }; >+ >+ $schema->storage->txn_rollback; >+}; >-- >2.50.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 40445
:
185289
| 185290 |
185291
|
185292