3 # Copyright 2020 Koha Development team
5 # This file is part of Koha
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
21 use Test::More tests => 3;
25 use t::lib::TestBuilder;
27 my $builder = t::lib::TestBuilder->new;
28 my $schema = Koha::Database->new->schema;
30 subtest 'manager' => sub {
33 $schema->storage->txn_begin;
35 my $manager = $builder->build_object( { class => 'Koha::Patrons' } );
36 my $cashup = $builder->build_object(
38 class => 'Koha::Cash::Register::Cashups',
39 value => { manager_id => $manager->borrowernumber },
43 is( ref( $cashup->manager ),
45 'Koha::Cash::Register::Cashup->manager should return a Koha::Patron' );
47 is( $cashup->manager->id, $manager->id,
48 'Koha::Cash::Register::Cashup->manager returns the correct Koha::Patron'
51 $schema->storage->txn_rollback;
55 subtest 'register' => sub {
58 $schema->storage->txn_begin;
61 $builder->build_object( { class => 'Koha::Cash::Registers' } );
62 my $cashup = $builder->build_object(
64 class => 'Koha::Cash::Register::Cashups',
65 value => { register_id => $register->id },
70 ref( $cashup->register ),
71 'Koha::Cash::Register',
72 'Koha::Cash::Register::Cashup->register should return a Koha::Cash::Register'
75 is( $cashup->register->id, $register->id,
76 'Koha::Cash::Register::Cashup->register returns the correct Koha::Cash::Register'
79 $schema->storage->txn_rollback;
83 subtest 'summary' => sub {
86 $schema->storage->txn_begin;
89 $builder->build_object( { class => 'Koha::Cash::Registers' } );
90 my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
91 my $manager = $builder->build_object( { class => 'Koha::Patrons' } );
92 my $account = $patron->account;
93 my $expected_total = 0;
94 my $expected_income_total = 0;
95 my $expected_income_grouped = [];
96 my $expected_payout_total = 0;
97 my $expected_payout_grouped = [];
99 # Transaction 1 (Fine (1.00) + Payment (-1.00))
100 my $fine1 = $account->add_debit(
107 $fine1->date( \'NOW() - INTERVAL 20 MINUTE' )->store;
109 my $payment1 = $account->pay(
111 cash_register => $register->id,
113 credit_type => 'PAYMENT',
117 $payment1 = Koha::Account::Lines->find( $payment1->{payment_id} );
118 $payment1->date( \'NOW() - INTERVAL 15 MINUTE' )->store;
119 $expected_income_total += '1.00';
121 # Overdue of 1.0 fully paid
122 unshift @{$expected_income_grouped},
124 debit_type_code => 'OVERDUE',
126 debit_type => { description => 'Overdue fine' }
129 # Transaction 2 (Account (1.00) + Lost (0.50) + Payment (-1.50))
130 my $account1 = $account->add_debit(
137 $account1->date( \'NOW() - INTERVAL 13 MINUTE' )->store;
138 my $lost1 = $account->add_debit(
145 $lost1->date( \'NOW() - INTERVAL 13 MINUTE' )->store;
146 my $payment2 = $account->pay(
148 cash_register => $register->id,
150 credit_type => 'PAYMENT',
151 lines => [ $account1, $lost1 ]
154 $payment2 = Koha::Account::Lines->find( $payment2->{payment_id} );
155 $payment2->date( \'NOW() - INTERVAL 13 MINUTE' )->store;
156 $expected_income_total += '1.5';
158 # Lost charge of 0.5 fully paid
159 unshift @{$expected_income_grouped},
161 debit_type_code => 'LOST',
163 debit_type => { description => 'Lost item' }
166 # Account fee of 1.0 fully paid
167 unshift @{$expected_income_grouped},
169 debit_type_code => 'ACCOUNT',
171 debit_type => { description => 'Account creation fee' }
174 # Transaction 3 (Refund (-0.50) + Payout (0.50))
175 $lost1->discard_changes;
176 my $refund1 = $lost1->reduce(
179 reduction_type => 'REFUND',
183 $refund1->date( \'NOW() - INTERVAL 13 MINUTE' )->store;
185 my $payout1 = $refund1->payout(
187 cash_register => $register->id,
189 payout_type => 'CASH',
190 interface => 'intranet',
191 staff_id => $manager->borrowernumber,
192 branch => $manager->branchcode
195 $payout1->date( \'NOW() - INTERVAL 13 MINUTE' )->store;
196 $expected_payout_total += '0.5';
198 # Lost fee of 0.50 fully refunded
199 unshift @{$expected_payout_grouped},
203 'description' => 'A refund applied to a patrons fine'
205 'credit_type_code' => 'REFUND'
208 $expected_total += $expected_income_total;
209 $expected_total -= $expected_payout_total;
213 $register->add_cashup( { manager_id => $manager->id, amount => '2.00' } );
215 my $summary = $cashup1->summary;
217 is( $summary->{from_date}, undef, "from_date is undefined if there is only one recorded" );
218 is( $summary->{to_date}, $cashup1->timestamp, "to_date equals cashup timestamp" );
219 is( ref( $summary->{income_grouped} ), 'ARRAY', "income_grouped contains an arrayref" );
220 is( scalar @{ $summary->{income_grouped} }, 3, "income_grouped contains 3 transactions" );
221 is_deeply( $summary->{income_grouped}, $expected_income_grouped, "income_grouped arrayref is correct" );
222 is( $summary->{income_total}, $expected_income_total, "income_total is correct" );
224 is( ref( $summary->{payout_grouped} ), 'ARRAY', "payout_grouped contains an arrayref" );
225 is( scalar @{ $summary->{payout_grouped} }, 1, "payout_grouped contains 1 transaction" );
226 is_deeply( $summary->{payout_grouped}, $expected_payout_grouped, "payout_grouped arrayref is correct" );
227 is( $summary->{payout_total}, $expected_payout_total, "payout_total is correct" );
228 is( $summary->{total}, $expected_total,"total equals expected_total" );
230 # Backdate cashup1 so we can add a new cashup to check 'previous'
231 $cashup1->timestamp( \'NOW() - INTERVAL 12 MINUTE' )->store();
232 $cashup1->discard_changes;
234 $expected_income_total = 0;
235 $expected_income_grouped = [];
236 $expected_payout_total = 0;
237 $expected_payout_grouped = [];
239 # Transaction 4 ( Fine (2.75) + Partial payment (-2.00) )
240 my $fine2 = $account->add_debit(
247 $fine2->date( \'NOW() - INTERVAL 10 MINUTE' )->store;
249 my $payment3 = $account->pay(
251 cash_register => $register->id,
253 credit_type => 'PAYMENT',
257 $payment3 = Koha::Account::Lines->find( $payment3->{payment_id} );
258 $payment3->date( \'NOW() - INTERVAL 10 MINUTE' )->store;
259 $expected_income_total += '2.00';
261 unshift @{$expected_income_grouped},
263 debit_type_code => 'OVERDUE',
264 total => '-2.000000' * -1,
265 debit_type => { 'description' => 'Overdue fine' }
268 $expected_total += $expected_income_total;
269 $expected_total -= $expected_payout_total;
273 $register->add_cashup( { manager_id => $manager->id, amount => '2.00' } );
275 $summary = $cashup2->summary;
277 is( $summary->{from_date}, $cashup1->timestamp, "from_date returns the timestamp of the previous cashup cashup" );
278 is( $summary->{to_date}, $cashup2->timestamp, "to_date equals cashup timestamp" );
279 is( ref( $summary->{income_grouped} ), 'ARRAY', "income_grouped contains Koha::Account::Lines" );
280 is( scalar @{ $summary->{income_grouped} }, 1, "income_grouped contains 1 transaction" );
281 is_deeply( $summary->{income_grouped}, $expected_income_grouped, "income_grouped arrayref is correct for partial payment" );
282 is( ref( $summary->{payout_grouped} ), 'ARRAY', "payout_grouped contains Koha::Account::Lines" );
283 is( scalar @{ $summary->{payout_grouped} }, 0, "payout_grouped contains 0 transactions" );
284 is_deeply( $summary->{payout_grouped}, $expected_payout_grouped, "payout_grouped arrayref is correct" );
285 is( $summary->{total}, $expected_total, "total equals expected_total" );
287 # Backdate cashup2 so we can add a new cashup to check
288 $cashup2->timestamp( \'NOW() - INTERVAL 6 MINUTE' )->store();
289 $cashup2->discard_changes;
291 $expected_income_total = 0;
292 $expected_income_grouped = [];
293 $expected_payout_total = 0;
294 $expected_payout_grouped = [];
296 # Transaction 5 (Refund (-1) + Payout (1))
297 $account1->discard_changes;
298 my $refund2 = $account1->reduce(
301 reduction_type => 'REFUND',
305 $refund2->date( \'NOW() - INTERVAL 3 MINUTE' )->store;
307 my $payout2 = $refund2->payout(
309 cash_register => $register->id,
311 payout_type => 'CASH',
312 interface => 'intranet',
313 staff_id => $manager->borrowernumber,
314 branch => $manager->branchcode
317 $payout2->date( \'NOW() - INTERVAL 3 MINUTE' )->store;
318 $expected_payout_total += '1.00';
320 # Account fee of 1.00 fully refunded (Across cashup boundary)
321 unshift @{$expected_payout_grouped},
325 'description' => 'A refund applied to a patrons fine'
327 'credit_type_code' => 'REFUND'
330 $expected_total += $expected_income_total;
331 $expected_total -= $expected_payout_total;
335 $register->add_cashup( { manager_id => $manager->id, amount => '2.00' } );
337 $summary = $cashup3->summary;
339 is( $summary->{from_date}, $cashup2->timestamp, "from_date returns the timestamp of the previous cashup cashup" );
340 is( $summary->{to_date}, $cashup3->timestamp, "to_date equals cashup timestamp" );
341 is( ref( $summary->{income_grouped} ), 'ARRAY', "income_grouped contains Koha::Account::Lines" );
342 is( scalar @{ $summary->{income_grouped} }, 0, "income_grouped contains 1 transaction" );
343 is_deeply( $summary->{income_grouped}, $expected_income_grouped, "income_grouped arrayref is correct for partial payment" );
344 is( ref( $summary->{payout_grouped} ), 'ARRAY', "payout_grouped contains Koha::Account::Lines" );
345 is( scalar @{ $summary->{payout_grouped} }, 1, "payout_grouped contains 0 transactions" );
346 is_deeply( $summary->{payout_grouped}, $expected_payout_grouped, "payout_grouped arrayref is correct" );
347 is( $summary->{total}, $expected_total, "total equals expected_total" );
349 $schema->storage->txn_rollback;