Bug 19915: (QA follow-up) Tidy up GetItemsForInventory.t
[koha.git] / t / db_dependent / Accounts.t
1 #!/usr/bin/perl
2
3 # Copyright 2015 BibLibre
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 3 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, see <http://www.gnu.org/licenses>.
18
19 use Modern::Perl;
20
21 use Test::More tests => 36;
22 use Test::MockModule;
23 use Test::Warn;
24
25 use t::lib::TestBuilder;
26 use t::lib::Mocks;
27
28 use Koha::Account;
29 use Koha::Account::Lines;
30 use Koha::Account::Offsets;
31 use Koha::Notice::Messages;
32 use Koha::Notice::Templates;
33 use Koha::DateUtils qw( dt_from_string );
34
35 BEGIN {
36     use_ok('C4::Accounts');
37     use_ok('Koha::Object');
38     use_ok('Koha::Patron');
39     use_ok('Data::Dumper');
40 }
41
42 can_ok( 'C4::Accounts',
43     qw(
44         getnextacctno
45         chargelostitem
46         manualinvoice
47         purge_zero_balance_fees )
48 );
49
50 my $schema  = Koha::Database->new->schema;
51 $schema->storage->txn_begin;
52 my $dbh = C4::Context->dbh;
53
54 my $builder = t::lib::TestBuilder->new;
55 my $library = $builder->build( { source => 'Branch' } );
56
57 $dbh->do(q|DELETE FROM accountlines|);
58 $dbh->do(q|DELETE FROM issues|);
59 $dbh->do(q|DELETE FROM borrowers|);
60
61 my $branchcode = $library->{branchcode};
62 my $borrower_number;
63
64 my $context = new Test::MockModule('C4::Context');
65 $context->mock( 'userenv', sub {
66     return {
67         flags  => 1,
68         id     => 'my_userid',
69         branch => $branchcode,
70     };
71 });
72 my $userenv_branchcode = $branchcode;
73
74 # Test chargelostitem
75 my $itemtype = $builder->build( { source => 'Itemtype' } );
76 my $item   = $builder->build( { source => 'Item', value => { itype => $itemtype->{itemtype} } } );
77 my $patron = $builder->build( { source => 'Borrower' } );
78 my $amount = '5.000000';
79 my $description = "Test fee!";
80 chargelostitem( $patron->{borrowernumber}, $item->{itemnumber}, $amount, $description );
81 my ($accountline) = Koha::Account::Lines->search(
82     {
83         borrowernumber => $patron->{borrowernumber}
84     }
85 );
86 is( $accountline->amount, $amount, 'Accountline amount set correctly for chargelostitem' );
87 is( $accountline->description, $description, 'Accountline description set correctly for chargelostitem' );
88 is( $accountline->branchcode, $branchcode, 'Accountline branchcode set correctly for chargelostitem' );
89 $dbh->do(q|DELETE FROM accountlines|);
90
91 # Test manualinvoice, reuse some of the vars from testing chargelostitem
92 my $type = 'L';
93 my $note = 'Test note!';
94 manualinvoice( $patron->{borrowernumber}, $item->{itemnumber}, $description, $type, $amount, $note );
95 ($accountline) = Koha::Account::Lines->search(
96     {
97         borrowernumber => $patron->{borrowernumber}
98     }
99 );
100 is( $accountline->accounttype, $type, 'Accountline type set correctly for manualinvoice' );
101 is( $accountline->amount, $amount, 'Accountline amount set correctly for manualinvoice' );
102 ok( $accountline->description =~ /^$description/, 'Accountline description set correctly for manualinvoice' );
103 is( $accountline->note, $note, 'Accountline note set correctly for manualinvoice' );
104 is( $accountline->branchcode, $branchcode, 'Accountline branchcode set correctly for manualinvoice' );
105
106 $dbh->do(q|DELETE FROM accountlines|);
107
108 # Testing purge_zero_balance_fees
109
110 # The 3rd value in the insert is 'days ago' --
111 # 0 => today
112 # 1 => yesterday
113 # etc.
114
115 my $sth = $dbh->prepare(
116     "INSERT INTO accountlines (
117          borrowernumber,
118          amountoutstanding,
119          date,
120          description
121      )
122      VALUES ( ?, ?, (select date_sub(CURRENT_DATE, INTERVAL ? DAY) ), ? )"
123 );
124
125 my $days = 5;
126
127 my @test_data = (
128     { amount => 0     , days_ago => 0         , description =>'purge_zero_balance_fees should not delete 0 balance fees with date today'                     , delete => 0 } ,
129     { amount => 0     , days_ago => $days - 1 , description =>'purge_zero_balance_fees should not delete 0 balance fees with date before threshold day'      , delete => 0 } ,
130     { amount => 0     , days_ago => $days     , description =>'purge_zero_balance_fees should not delete 0 balance fees with date on threshold day'          , delete => 0 } ,
131     { amount => 0     , days_ago => $days + 1 , description =>'purge_zero_balance_fees should delete 0 balance fees with date after threshold day'           , delete => 1 } ,
132     { amount => undef , days_ago => $days + 1 , description =>'purge_zero_balance_fees should delete NULL balance fees with date after threshold day'        , delete => 1 } ,
133     { amount => 5     , days_ago => $days - 1 , description =>'purge_zero_balance_fees should not delete fees with positive amout owed before threshold day'  , delete => 0 } ,
134     { amount => 5     , days_ago => $days     , description =>'purge_zero_balance_fees should not delete fees with positive amout owed on threshold day'      , delete => 0 } ,
135     { amount => 5     , days_ago => $days + 1 , description =>'purge_zero_balance_fees should not delete fees with positive amout owed after threshold day'   , delete => 0 } ,
136     { amount => -5    , days_ago => $days - 1 , description =>'purge_zero_balance_fees should not delete fees with negative amout owed before threshold day' , delete => 0 } ,
137     { amount => -5    , days_ago => $days     , description =>'purge_zero_balance_fees should not delete fees with negative amout owed on threshold day'     , delete => 0 } ,
138     { amount => -5    , days_ago => $days + 1 , description =>'purge_zero_balance_fees should not delete fees with negative amout owed after threshold day'  , delete => 0 }
139 );
140 my $categorycode = $builder->build({ source => 'Category' })->{categorycode};
141 my $borrower = Koha::Patron->new( { firstname => 'Test', surname => 'Patron', categorycode => $categorycode, branchcode => $branchcode } )->store();
142
143 for my $data ( @test_data ) {
144     $sth->execute($borrower->borrowernumber, $data->{amount}, $data->{days_ago}, $data->{description});
145 }
146
147 purge_zero_balance_fees( $days );
148
149 $sth = $dbh->prepare(
150             "select count(*) = 0 as deleted
151              from accountlines
152              where description = ?"
153        );
154
155 #
156 sub is_delete_correct {
157     my $should_delete = shift;
158     my $description = shift;
159     $sth->execute( $description );
160     my $test = $sth->fetchrow_hashref();
161     is( $test->{deleted}, $should_delete, $description )
162 }
163
164 for my $data  (@test_data) {
165     is_delete_correct( $data->{delete}, $data->{description});
166 }
167
168 $dbh->do(q|DELETE FROM accountlines|);
169
170 subtest "Koha::Account::pay tests" => sub {
171
172     plan tests => 14;
173
174     # Create a borrower
175     my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
176     my $branchcode   = $builder->build({ source => 'Branch' })->{ branchcode };
177
178     my $borrower = Koha::Patron->new( {
179         cardnumber => '1234567890',
180         surname => 'McFly',
181         firstname => 'Marty',
182     } );
183     $borrower->categorycode( $categorycode );
184     $borrower->branchcode( $branchcode );
185     $borrower->store;
186
187     my $account = Koha::Account->new({ patron_id => $borrower->id });
188
189     my $line1 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amountoutstanding => 100 })->store();
190     my $line2 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amountoutstanding => 200 })->store();
191
192     $sth = $dbh->prepare("SELECT count(*) FROM accountlines");
193     $sth->execute;
194     my $count = $sth->fetchrow_array;
195     is($count, 2, 'There is 2 lines as expected');
196
197     # There is $100 in the account
198     $sth = $dbh->prepare("SELECT amountoutstanding FROM accountlines WHERE borrowernumber=?");
199     my $amountoutstanding = $dbh->selectcol_arrayref($sth, {}, $borrower->borrowernumber);
200     my $amountleft = 0;
201     for my $line ( @$amountoutstanding ) {
202         $amountleft += $line;
203     }
204     is($amountleft, 300, 'The account has 300$ as expected' );
205
206     # We make a $20 payment
207     my $borrowernumber = $borrower->borrowernumber;
208     my $data = '20.00';
209     my $payment_note = '$20.00 payment note';
210     my $id = $account->pay( { amount => $data, note => $payment_note, payment_type => "TEST_TYPE" } );
211
212     my $accountline = Koha::Account::Lines->find( $id );
213     is( $accountline->payment_type, "TEST_TYPE", "Payment type passed into pay is set in account line correctly" );
214
215     # There is now $280 in the account
216     $sth = $dbh->prepare("SELECT amountoutstanding FROM accountlines WHERE borrowernumber=?");
217     $amountoutstanding = $dbh->selectcol_arrayref($sth, {}, $borrower->borrowernumber);
218     $amountleft = 0;
219     for my $line ( @$amountoutstanding ) {
220         $amountleft += $line;
221     }
222     is($amountleft, 280, 'The account has $280 as expected' );
223
224     # Is the payment note well registered
225     $sth = $dbh->prepare("SELECT note FROM accountlines WHERE borrowernumber=? ORDER BY accountlines_id DESC LIMIT 1");
226     $sth->execute($borrower->borrowernumber);
227     my $note = $sth->fetchrow_array;
228     is($note,'$20.00 payment note', '$20.00 payment note is registered');
229
230     # We make a -$30 payment (a NEGATIVE payment)
231     $data = '-30.00';
232     $payment_note = '-$30.00 payment note';
233     $account->pay( { amount => $data, note => $payment_note } );
234
235     # There is now $310 in the account
236     $sth = $dbh->prepare("SELECT amountoutstanding FROM accountlines WHERE borrowernumber=?");
237     $amountoutstanding = $dbh->selectcol_arrayref($sth, {}, $borrower->borrowernumber);
238     $amountleft = 0;
239     for my $line ( @$amountoutstanding ) {
240         $amountleft += $line;
241     }
242     is($amountleft, 310, 'The account has $310 as expected' );
243     # Is the payment note well registered
244     $sth = $dbh->prepare("SELECT note FROM accountlines WHERE borrowernumber=? ORDER BY accountlines_id DESC LIMIT 1");
245     $sth->execute($borrower->borrowernumber);
246     $note = $sth->fetchrow_array;
247     is($note,'-$30.00 payment note', '-$30.00 payment note is registered');
248
249     #We make a $150 payment ( > 1stLine )
250     $data = '150.00';
251     $payment_note = '$150.00 payment note';
252     $account->pay( { amount => $data, note => $payment_note } );
253
254     # There is now $160 in the account
255     $sth = $dbh->prepare("SELECT amountoutstanding FROM accountlines WHERE borrowernumber=?");
256     $amountoutstanding = $dbh->selectcol_arrayref($sth, {}, $borrower->borrowernumber);
257     $amountleft = 0;
258     for my $line ( @$amountoutstanding ) {
259         $amountleft += $line;
260     }
261     is($amountleft, 160, 'The account has $160 as expected' );
262
263     # Is the payment note well registered
264     $sth = $dbh->prepare("SELECT note FROM accountlines WHERE borrowernumber=? ORDER BY accountlines_id DESC LIMIT 1");
265     $sth->execute($borrower->borrowernumber);
266     $note = $sth->fetchrow_array;
267     is($note,'$150.00 payment note', '$150.00 payment note is registered');
268
269     #We make a $200 payment ( > amountleft )
270     $data = '200.00';
271     $payment_note = '$200.00 payment note';
272     $account->pay( { amount => $data, note => $payment_note } );
273
274     # There is now -$40 in the account
275     $sth = $dbh->prepare("SELECT amountoutstanding FROM accountlines WHERE borrowernumber=?");
276     $amountoutstanding = $dbh->selectcol_arrayref($sth, {}, $borrower->borrowernumber);
277     $amountleft = 0;
278     for my $line ( @$amountoutstanding ) {
279         $amountleft += $line;
280     }
281     is($amountleft, -40, 'The account has -$40 as expected, (credit situation)' );
282
283     # Is the payment note well registered
284     $sth = $dbh->prepare("SELECT note FROM accountlines WHERE borrowernumber=? ORDER BY accountlines_id DESC LIMIT 1");
285     $sth->execute($borrower->borrowernumber);
286     $note = $sth->fetchrow_array;
287     is($note,'$200.00 payment note', '$200.00 payment note is registered');
288
289     my $line3 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amountoutstanding => 42, accounttype => 'TEST' })->store();
290     my $payment_id = $account->pay( { lines => [$line3], amount => 42 } );
291     my $payment = Koha::Account::Lines->find( $payment_id );
292     is( $payment->amount(), '-42.000000', "Payment paid the specified fine" );
293     $line3 = Koha::Account::Lines->find( $line3->id );
294     is( $line3->amountoutstanding, '0.000000', "Specified fine is paid" );
295     is( $payment->branchcode, undef, 'branchcode passed, then undef' );
296 };
297
298 subtest "Koha::Account::pay particular line tests" => sub {
299
300     plan tests => 5;
301
302     # Create a borrower
303     my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
304     my $branchcode   = $builder->build({ source => 'Branch' })->{ branchcode };
305
306     my $borrower = Koha::Patron->new( {
307         cardnumber => 'kylemhall',
308         surname => 'Hall',
309         firstname => 'Kyle',
310     } );
311     $borrower->categorycode( $categorycode );
312     $borrower->branchcode( $branchcode );
313     $borrower->store;
314
315     my $account = Koha::Account->new({ patron_id => $borrower->id });
316
317     my $line1 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amountoutstanding => 1 })->store();
318     my $line2 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amountoutstanding => 2 })->store();
319     my $line3 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amountoutstanding => 3 })->store();
320     my $line4 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amountoutstanding => 4 })->store();
321
322     is( $account->balance(), 10, "Account balance is 10" );
323
324     $account->pay(
325         {
326             lines => [$line2, $line3, $line4],
327             amount => 4,
328         }
329     );
330
331     $_->_result->discard_changes foreach ( $line1, $line2, $line3, $line4 );
332
333     # Line1 is not paid at all, as it was not passed in the lines param
334     is( $line1->amountoutstanding, "1.000000", "Line 1 was not paid" );
335     # Line2 was paid in full, as it was the first in the lines list
336     is( $line2->amountoutstanding, "0.000000", "Line 2 was paid in full" );
337     # Line3 was paid partially, as the remaining balance did not cover it entirely
338     is( $line3->amountoutstanding, "1.000000", "Line 3 was paid to 1.00" );
339     # Line4 was not paid at all, as the payment was all used up by that point
340     is( $line4->amountoutstanding, "4.000000", "Line 4 was not paid" );
341 };
342
343 subtest "Koha::Account::pay writeoff tests" => sub {
344
345     plan tests => 5;
346
347     # Create a borrower
348     my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
349     my $branchcode   = $builder->build({ source => 'Branch' })->{ branchcode };
350
351     my $borrower = Koha::Patron->new( {
352         cardnumber => 'chelseahall',
353         surname => 'Hall',
354         firstname => 'Chelsea',
355     } );
356     $borrower->categorycode( $categorycode );
357     $borrower->branchcode( $branchcode );
358     $borrower->store;
359
360     my $account = Koha::Account->new({ patron_id => $borrower->id });
361
362     my $line = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amountoutstanding => 42 })->store();
363
364     is( $account->balance(), 42, "Account balance is 42" );
365
366     my $id = $account->pay(
367         {
368             lines  => [$line],
369             amount => 42,
370             type   => 'writeoff',
371         }
372     );
373
374     $line->_result->discard_changes();
375
376     is( $line->amountoutstanding, "0.000000", "Line was written off" );
377
378     my $writeoff = Koha::Account::Lines->find( $id );
379
380     is( $writeoff->accounttype, 'W', 'Type is correct' );
381     is( $writeoff->description, 'Writeoff', 'Description is correct' );
382     is( $writeoff->amount, '-42.000000', 'Amount is correct' );
383 };
384
385 subtest "More Koha::Account::pay tests" => sub {
386
387     plan tests => 8;
388
389     # Create a borrower
390     my $category   = $builder->build({ source => 'Category' })->{ categorycode };
391     my $branch     = $builder->build({ source => 'Branch' })->{ branchcode };
392     $branchcode = $branch;
393     my $borrowernumber = $builder->build({
394         source => 'Borrower',
395         value  => { categorycode => $category,
396                     branchcode   => $branch }
397     })->{ borrowernumber };
398
399     my $amount = 100;
400     my $accountline = $builder->build({ source => 'Accountline',
401         value  => { borrowernumber => $borrowernumber,
402                     amount => $amount,
403                     amountoutstanding => $amount }
404     });
405
406     my $rs = $schema->resultset('Accountline')->search({
407         borrowernumber => $borrowernumber
408     });
409
410     is( $rs->count(), 1, 'Accountline created' );
411
412     my $account = Koha::Account->new( { patron_id => $borrowernumber } );
413     my $line = Koha::Account::Lines->find( $accountline->{ accountlines_id } );
414     # make the full payment
415     $account->pay({ lines => [$line], amount => $amount, library_id => $branch, note => 'A payment note' });
416
417     my $offset = Koha::Account::Offsets->search({ debit_id => $accountline->{accountlines_id} })->next();
418     is( $offset->amount(), '-100.000000', 'Offset amount is -100.00' );
419     is( $offset->type(), 'Payment', 'Offset type is Payment' );
420
421     my $stat = $schema->resultset('Statistic')->search({
422         branch  => $branch,
423         type    => 'payment'
424     }, { order_by => { -desc => 'datetime' } })->next();
425
426     ok( defined $stat, "There's a payment log that matches the branch" );
427
428     SKIP: {
429         skip "No statistic logged", 4 unless defined $stat;
430
431         is( $stat->type, 'payment', "Correct statistic type" );
432         is( $stat->branch, $branch, "Correct branch logged to statistics" );
433         is( $stat->borrowernumber, $borrowernumber, "Correct borrowernumber logged to statistics" );
434         is( $stat->value+0, $amount, "Correct amount logged to statistics" );
435     }
436 };
437
438 subtest "Even more Koha::Account::pay tests" => sub {
439
440     plan tests => 8;
441
442     # Create a borrower
443     my $category   = $builder->build({ source => 'Category' })->{ categorycode };
444     my $branch     = $builder->build({ source => 'Branch' })->{ branchcode };
445     $branchcode = $branch;
446     my $borrowernumber = $builder->build({
447         source => 'Borrower',
448         value  => { categorycode => $category,
449                     branchcode   => $branch }
450     })->{ borrowernumber };
451
452     my $amount = 100;
453     my $partialamount = 60;
454     my $accountline = $builder->build({ source => 'Accountline',
455         value  => { borrowernumber => $borrowernumber,
456                     amount => $amount,
457                     amountoutstanding => $amount }
458     });
459
460     my $rs = $schema->resultset('Accountline')->search({
461         borrowernumber => $borrowernumber
462     });
463
464     is( $rs->count(), 1, 'Accountline created' );
465
466     my $account = Koha::Account->new( { patron_id => $borrowernumber } );
467     my $line = Koha::Account::Lines->find( $accountline->{ accountlines_id } );
468     # make the full payment
469     $account->pay({ lines => [$line], amount => $partialamount, library_id => $branch, note => 'A payment note' });
470
471     my $offset = Koha::Account::Offsets->search( { debit_id => $accountline->{ accountlines_id } } )->next();
472     is( $offset->amount, '-60.000000', 'Offset amount is -60.00' );
473     is( $offset->type, 'Payment', 'Offset type is payment' );
474
475     my $stat = $schema->resultset('Statistic')->search({
476         branch  => $branch,
477         type    => 'payment'
478     }, { order_by => { -desc => 'datetime' } })->next();
479
480     ok( defined $stat, "There's a payment log that matches the branch" );
481
482     SKIP: {
483         skip "No statistic logged", 4 unless defined $stat;
484
485         is( $stat->type, 'payment', "Correct statistic type" );
486         is( $stat->branch, $branch, "Correct branch logged to statistics" );
487         is( $stat->borrowernumber, $borrowernumber, "Correct borrowernumber logged to statistics" );
488         is( $stat->value+0, $partialamount, "Correct amount logged to statistics" );
489     }
490 };
491
492 subtest 'balance' => sub {
493     plan tests => 2;
494
495     my $patron = $builder->build({source => 'Borrower'});
496     $patron = Koha::Patrons->find( $patron->{borrowernumber} );
497     my $account = $patron->account;
498     is( $account->balance, 0, 'balance should return 0 if the patron does not have fines' );
499
500     my $accountline_1 = $builder->build(
501         {
502             source => 'Accountline',
503             value  => {
504                 borrowernumber    => $patron->borrowernumber,
505                 amount            => 42,
506                 amountoutstanding => 42
507             }
508         }
509     );
510     my $accountline_2 = $builder->build(
511         {
512             source => 'Accountline',
513             value  => {
514                 borrowernumber    => $patron->borrowernumber,
515                 amount            => -13,
516                 amountoutstanding => -13
517             }
518         }
519     );
520
521     my $balance = $patron->account->balance;
522     is( int($balance), 29, 'balance should return the correct value');
523
524     $patron->delete;
525 };
526
527 subtest "Koha::Account::chargelostitem tests" => sub {
528     plan tests => 40;
529
530     my $lostfine;
531     my $procfee;
532
533     my $itype_no_replace_no_fee = $builder->build({ source => 'Itemtype', value => {
534             rentalcharge => 0,
535             defaultreplacecost => undef,
536             processfee => undef,
537     }});
538     my $itype_replace_no_fee = $builder->build({ source => 'Itemtype', value => {
539             rentalcharge => 0,
540             defaultreplacecost => 16.32,
541             processfee => undef,
542     }});
543     my $itype_no_replace_fee = $builder->build({ source => 'Itemtype', value => {
544             rentalcharge => 0,
545             defaultreplacecost => undef,
546             processfee => 8.16,
547     }});
548     my $itype_replace_fee = $builder->build({ source => 'Itemtype', value => {
549             rentalcharge => 0,
550             defaultreplacecost => 4.08,
551             processfee => 2.04,
552     }});
553     my $cli_borrowernumber = $builder->build({ source => 'Borrower' })->{'borrowernumber'};
554     my $cli_itemnumber1 = $builder->build({ source => 'Item', value => { itype => $itype_no_replace_no_fee->{itemtype} } })->{'itemnumber'};
555     my $cli_itemnumber2 = $builder->build({ source => 'Item', value => { itype => $itype_replace_no_fee->{itemtype} } })->{'itemnumber'};
556     my $cli_itemnumber3 = $builder->build({ source => 'Item', value => { itype => $itype_no_replace_fee->{itemtype} } })->{'itemnumber'};
557     my $cli_itemnumber4 = $builder->build({ source => 'Item', value => { itype => $itype_replace_fee->{itemtype} } })->{'itemnumber'};
558
559     my $cli_issue_id_1 = $builder->build({ source => 'Issue', value => { borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber1 } })->{issue_id};
560     my $cli_issue_id_2 = $builder->build({ source => 'Issue', value => { borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber2 } })->{issue_id};
561     my $cli_issue_id_3 = $builder->build({ source => 'Issue', value => { borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber3 } })->{issue_id};
562     my $cli_issue_id_4 = $builder->build({ source => 'Issue', value => { borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber4 } })->{issue_id};
563
564     my $duck = Koha::Items->find({itemnumber=>$cli_itemnumber1});
565
566     t::lib::Mocks::mock_preference('item-level_itypes', '1');
567     t::lib::Mocks::mock_preference('useDefaultReplacementCost', '0');
568
569     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber1, 0, "Perdedor");
570     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber1, accounttype => 'L' });
571     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber1, accounttype => 'PF' });
572     ok( !$lostfine, "No lost fine if no replacementcost or default when pref off");
573     ok( !$procfee,  "No processing fee if no processing fee");
574     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber1, 6.12, "Perdedor");
575     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber1, accounttype => 'L' });
576     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber1, accounttype => 'PF' });
577     ok( $lostfine->amount == 6.12, "Lost fine equals replacementcost when pref off and no default set");
578     ok( !$procfee,  "No processing fee if no processing fee");
579     $lostfine->delete();
580
581     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber2, 0, "Perdedor");
582     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber2, accounttype => 'L' });
583     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber2, accounttype => 'PF' });
584     ok( !$lostfine, "No lost fine if no replacementcost but default set when pref off");
585     ok( !$procfee,  "No processing fee if no processing fee");
586     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber2, 6.12, "Perdedor");
587     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber2, accounttype => 'L' });
588     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber2, accounttype => 'PF' });
589     ok( $lostfine->amount == 6.12 , "Lost fine equals replacementcost when pref off and default set");
590     ok( !$procfee,  "No processing fee if no processing fee");
591     $lostfine->delete();
592
593     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber3, 0, "Perdedor");
594     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber3, accounttype => 'L' });
595     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber3, accounttype => 'PF' });
596     ok( !$lostfine, "No lost fine if no replacementcost and no default set when pref off");
597     ok( $procfee->amount == 8.16,  "Processing fee if processing fee");
598     is( $procfee->issue_id, $cli_issue_id_3, "Processing fee issue id is correct" );
599     $procfee->delete();
600     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber3, 6.12, "Perdedor");
601     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber3, accounttype => 'L' });
602     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber3, accounttype => 'PF' });
603     ok( $lostfine->amount == 6.12 , "Lost fine equals replacementcost when pref off and no default set");
604     ok( $procfee->amount == 8.16,  "Processing fee if processing fee");
605     is( $procfee->issue_id, $cli_issue_id_3, "Processing fee issue id is correct" );
606     $lostfine->delete();
607     $procfee->delete();
608
609     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber4, 0, "Perdedor");
610     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber4, accounttype => 'L' });
611     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber4, accounttype => 'PF' });
612     ok( !$lostfine, "No lost fine if no replacementcost but default set when pref off");
613     ok( $procfee->amount == 2.04,  "Processing fee if processing fee");
614     is( $procfee->issue_id, $cli_issue_id_4, "Processing fee issue id is correct" );
615     $procfee->delete();
616     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber4, 6.12, "Perdedor");
617     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber4, accounttype => 'L' });
618     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber4, accounttype => 'PF' });
619     ok( $lostfine->amount == 6.12 , "Lost fine equals replacementcost when pref off and default set");
620     ok( $procfee->amount == 2.04,  "Processing fee if processing fee");
621     is( $procfee->issue_id, $cli_issue_id_4, "Processing fee issue id is correct" );
622     $lostfine->delete();
623     $procfee->delete();
624
625     t::lib::Mocks::mock_preference('useDefaultReplacementCost', '1');
626
627     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber1, 0, "Perdedor");
628     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber1, accounttype => 'L' });
629     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber1, accounttype => 'PF' });
630     ok( !$lostfine, "No lost fine if no replacementcost or default when pref on");
631     ok( !$procfee,  "No processing fee if no processing fee");
632     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber1, 6.12, "Perdedor");
633     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber1, accounttype => 'L' });
634     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber1, accounttype => 'PF' });
635     is( $lostfine->amount, "6.120000", "Lost fine equals replacementcost when pref on and no default set");
636     ok( !$procfee,  "No processing fee if no processing fee");
637
638     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber2, 0, "Perdedor");
639     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber2, accounttype => 'L' });
640     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber2, accounttype => 'PF' });
641     is( $lostfine->amount(), "16.320000", "Lost fine is default if no replacementcost but default set when pref on");
642     ok( !$procfee,  "No processing fee if no processing fee");
643     $lostfine->delete();
644     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber2, 6.12, "Perdedor");
645     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber2, accounttype => 'L' });
646     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber2, accounttype => 'PF' });
647     is( $lostfine->amount, "6.120000" , "Lost fine equals replacementcost when pref on and default set");
648     ok( !$procfee,  "No processing fee if no processing fee");
649
650     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber3, 0, "Perdedor");
651     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber3, accounttype => 'L' });
652     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber3, accounttype => 'PF' });
653     ok( !$lostfine, "No lost fine if no replacementcost and default not set when pref on");
654     is( $procfee->amount, "8.160000",  "Processing fee if processing fee");
655     is( $procfee->issue_id, $cli_issue_id_3, "Processing fee issue id is correct" );
656     $procfee->delete();
657     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber3, 6.12, "Perdedor");
658     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber3, accounttype => 'L' });
659     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber3, accounttype => 'PF' });
660     is( $lostfine->amount, "6.120000", "Lost fine equals replacementcost when pref on and no default set");
661     is( $procfee->amount, "8.160000",  "Processing fee if processing fee");
662     is( $procfee->issue_id, $cli_issue_id_3, "Processing fee issue id is correct" );
663
664     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber4, 0, "Perdedor");
665     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber4, accounttype => 'L' });
666     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber4, accounttype => 'PF' });
667     is( $lostfine->amount, "4.080000", "Lost fine is default if no replacementcost but default set when pref on");
668     is( $procfee->amount, "2.040000",  "Processing fee if processing fee");
669     is( $procfee->issue_id, $cli_issue_id_4, "Processing fee issue id is correct" );
670     $lostfine->delete();
671     $procfee->delete();
672     C4::Accounts::chargelostitem( $cli_borrowernumber, $cli_itemnumber4, 6.12, "Perdedor");
673     $lostfine = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber4, accounttype => 'L' });
674     $procfee  = Koha::Account::Lines->find({ borrowernumber => $cli_borrowernumber, itemnumber => $cli_itemnumber4, accounttype => 'PF' });
675     is( $lostfine->amount, "6.120000", "Lost fine equals replacementcost when pref on and default set");
676     is( $procfee->amount, "2.040000",  "Processing fee if processing fee");
677     is( $procfee->issue_id, $cli_issue_id_4, "Processing fee issue id is correct" );
678
679     # Cleanup - this must be replaced with a transaction per subtest
680     Koha::Patrons->find($cli_borrowernumber)->checkouts->delete;
681 };
682
683 subtest "Koha::Account::non_issues_charges tests" => sub {
684     plan tests => 21;
685
686     my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
687
688     my $today  = dt_from_string;
689     my $res    = 3;
690     my $rent   = 5;
691     my $manual = 7;
692     Koha::Account::Line->new(
693         {
694             borrowernumber    => $patron->borrowernumber,
695             accountno         => 1,
696             date              => $today,
697             description       => 'a Res fee',
698             accounttype       => 'Res',
699             amountoutstanding => $res,
700         }
701     )->store;
702     Koha::Account::Line->new(
703         {
704             borrowernumber    => $patron->borrowernumber,
705             accountno         => 2,
706             date              => $today,
707             description       => 'a Rental fee',
708             accounttype       => 'Rent',
709             amountoutstanding => $rent,
710         }
711     )->store;
712     Koha::Account::Line->new(
713         {
714             borrowernumber    => $patron->borrowernumber,
715             accountno         => 3,
716             date              => $today,
717             description       => 'a Manual invoice fee',
718             accounttype       => 'Copie',
719             amountoutstanding => $manual,
720         }
721     )->store;
722     Koha::AuthorisedValue->new(
723         {
724             category         => 'MANUAL_INV',
725             authorised_value => 'Copie',
726             lib              => 'Fee for copie',
727         }
728     )->store;
729
730     my $account = $patron->account;
731
732     t::lib::Mocks::mock_preference( 'HoldsInNoissuesCharge',   0 );
733     t::lib::Mocks::mock_preference( 'RentalsInNoissuesCharge', 0 );
734     t::lib::Mocks::mock_preference( 'ManInvInNoissuesCharge',  0 );
735     my ( $total, $non_issues_charges ) = ( $account->balance, $account->non_issues_charges );
736     my $other_charges = $total - $non_issues_charges;
737     is(
738         $account->balance,
739         $res + $rent + $manual,
740         'Total charges should be Res + Rent + Manual'
741     );
742     is( $non_issues_charges, 0,
743         'If 0|0|0 there should not have non issues charges' );
744     is( $other_charges, 15, 'If 0|0|0 there should only have other charges' );
745
746     t::lib::Mocks::mock_preference( 'HoldsInNoissuesCharge',   0 );
747     t::lib::Mocks::mock_preference( 'RentalsInNoissuesCharge', 0 );
748     t::lib::Mocks::mock_preference( 'ManInvInNoissuesCharge',  1 );
749     ( $total, $non_issues_charges ) = ( $account->balance, $account->non_issues_charges );
750     $other_charges = $total - $non_issues_charges;
751     is(
752         $total,
753         $res + $rent + $manual,
754         'Total charges should be Res + Rent + Manual'
755     );
756     is( $non_issues_charges, $manual,
757         'If 0|0|1 Only Manual should be a non issue charge' );
758     is(
759         $other_charges,
760         $res + $rent,
761         'If 0|0|1 Res + Rent should be other charges'
762     );
763
764     t::lib::Mocks::mock_preference( 'HoldsInNoissuesCharge',   0 );
765     t::lib::Mocks::mock_preference( 'RentalsInNoissuesCharge', 1 );
766     t::lib::Mocks::mock_preference( 'ManInvInNoissuesCharge',  0 );
767     ( $total, $non_issues_charges ) = ( $account->balance, $account->non_issues_charges );
768     $other_charges = $total - $non_issues_charges;
769     is(
770         $total,
771         $res + $rent + $manual,
772         'Total charges should be Res + Rent + Manual'
773     );
774     is( $non_issues_charges, $rent,
775         'If 0|1|0 Only Rental should be a non issue charge' );
776     is(
777         $other_charges,
778         $res + $manual,
779         'If 0|1|0 Rent + Manual should be other charges'
780     );
781
782     t::lib::Mocks::mock_preference( 'HoldsInNoissuesCharge',   0 );
783     t::lib::Mocks::mock_preference( 'RentalsInNoissuesCharge', 1 );
784     t::lib::Mocks::mock_preference( 'ManInvInNoissuesCharge',  1 );
785     ( $total, $non_issues_charges ) = ( $account->balance, $account->non_issues_charges );
786     $other_charges = $total - $non_issues_charges;
787     is(
788         $total,
789         $res + $rent + $manual,
790         'Total charges should be Res + Rent + Manual'
791     );
792     is(
793         $non_issues_charges,
794         $rent + $manual,
795         'If 0|1|1 Rent + Manual should be non issues charges'
796     );
797     is( $other_charges, $res, 'If 0|1|1 there should only have other charges' );
798
799     t::lib::Mocks::mock_preference( 'HoldsInNoissuesCharge',   1 );
800     t::lib::Mocks::mock_preference( 'RentalsInNoissuesCharge', 0 );
801     t::lib::Mocks::mock_preference( 'ManInvInNoissuesCharge',  0 );
802     ( $total, $non_issues_charges ) = ( $account->balance, $account->non_issues_charges );
803     $other_charges = $total - $non_issues_charges;
804     is(
805         $total,
806         $res + $rent + $manual,
807         'Total charges should be Res + Rent + Manual'
808     );
809     is( $non_issues_charges, $res,
810         'If 1|0|0 Only Res should be non issues charges' );
811     is(
812         $other_charges,
813         $rent + $manual,
814         'If 1|0|0 Rent + Manual should be other charges'
815     );
816
817     t::lib::Mocks::mock_preference( 'HoldsInNoissuesCharge',   1 );
818     t::lib::Mocks::mock_preference( 'RentalsInNoissuesCharge', 1 );
819     t::lib::Mocks::mock_preference( 'ManInvInNoissuesCharge',  0 );
820     ( $total, $non_issues_charges ) = ( $account->balance, $account->non_issues_charges );
821     $other_charges = $total - $non_issues_charges;
822     is(
823         $total,
824         $res + $rent + $manual,
825         'Total charges should be Res + Rent + Manual'
826     );
827     is(
828         $non_issues_charges,
829         $res + $rent,
830         'If 1|1|0 Res + Rent should be non issues charges'
831     );
832     is( $other_charges, $manual,
833         'If 1|1|0 Only Manual should be other charges' );
834
835     t::lib::Mocks::mock_preference( 'HoldsInNoissuesCharge',   1 );
836     t::lib::Mocks::mock_preference( 'RentalsInNoissuesCharge', 1 );
837     t::lib::Mocks::mock_preference( 'ManInvInNoissuesCharge',  1 );
838     ( $total, $non_issues_charges ) = ( $account->balance, $account->non_issues_charges );
839     $other_charges = $total - $non_issues_charges;
840     is(
841         $total,
842         $res + $rent + $manual,
843         'Total charges should be Res + Rent + Manual'
844     );
845     is(
846         $non_issues_charges,
847         $res + $rent + $manual,
848         'If 1|1|1 Res + Rent + Manual should be non issues charges'
849     );
850     is( $other_charges, 0, 'If 1|1|1 there should not have any other charges' );
851 };
852
853 subtest "Koha::Account::non_issues_charges tests" => sub {
854     plan tests => 9;
855
856     my $patron = $builder->build_object(
857         {
858             class => "Koha::Patrons",
859             value => {
860                 firstname    => 'Test',
861                 surname      => 'Patron',
862                 categorycode => $categorycode,
863                 branchcode   => $branchcode
864             }
865         }
866     );
867
868     my $debit = Koha::Account::Line->new({ borrowernumber => $patron->id, date => '1900-01-01', amountoutstanding => 0 })->store();
869     my $credit = Koha::Account::Line->new({ borrowernumber => $patron->id, date => '1900-01-01', amountoutstanding => -5 })->store();
870     my $offset = Koha::Account::Offset->new({ credit_id => $credit->id, debit_id => $debit->id, type => 'Payment', amount => 0 })->store();
871     purge_zero_balance_fees( 1 );
872     my $debit_2 = Koha::Account::Lines->find( $debit->id );
873     my $credit_2 = Koha::Account::Lines->find( $credit->id );
874     ok( $debit_2, 'Debit was correctly not deleted when credit has balance' );
875     ok( $credit_2, 'Credit was correctly not deleted when credit has balance' );
876     is( Koha::Account::Lines->count({ borrowernumber => $patron->id }), 2, "The 2 account lines still exists" );
877
878     $debit = Koha::Account::Line->new({ borrowernumber => $patron->id, date => '1900-01-01', amountoutstanding => 5 })->store();
879     $credit = Koha::Account::Line->new({ borrowernumber => $patron->id, date => '1900-01-01', amountoutstanding => 0 })->store();
880     $offset = Koha::Account::Offset->new({ credit_id => $credit->id, debit_id => $debit->id, type => 'Payment', amount => 0 })->store();
881     purge_zero_balance_fees( 1 );
882     $debit_2 = $credit_2 = undef;
883     $debit_2 = Koha::Account::Lines->find( $debit->id );
884     $credit_2 = Koha::Account::Lines->find( $credit->id );
885     ok( $debit_2, 'Debit was correctly not deleted when debit has balance' );
886     ok( $credit_2, 'Credit was correctly not deleted when debit has balance' );
887     is( Koha::Account::Lines->count({ borrowernumber => $patron->id }), 2 + 2, "The 2 + 2 account lines still exists" );
888
889     $debit = Koha::Account::Line->new({ borrowernumber => $patron->id, date => '1900-01-01', amountoutstanding => 0 })->store();
890     $credit = Koha::Account::Line->new({ borrowernumber => $patron->id, date => '1900-01-01', amountoutstanding => 0 })->store();
891     $offset = Koha::Account::Offset->new({ credit_id => $credit->id, debit_id => $debit->id, type => 'Payment', amount => 0 })->store();
892     purge_zero_balance_fees( 1 );
893     $debit_2 = Koha::Account::Lines->find( $debit->id );
894     $credit_2 = Koha::Account::Lines->find( $credit->id );
895     ok( !$debit_2, 'Debit was correctly deleted' );
896     ok( !$credit_2, 'Credit was correctly deleted' );
897     is( Koha::Account::Lines->count({ borrowernumber => $patron->id }), 2 + 2, "The 2 + 2 account lines still exists, the last 2 have been deleted ok" );
898 };
899
900 subtest "Koha::Account::Line::void tests" => sub {
901
902     plan tests => 15;
903
904     # Create a borrower
905     my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
906     my $branchcode   = $builder->build({ source => 'Branch' })->{ branchcode };
907
908     my $borrower = Koha::Patron->new( {
909         cardnumber => 'dariahall',
910         surname => 'Hall',
911         firstname => 'Daria',
912     } );
913     $borrower->categorycode( $categorycode );
914     $borrower->branchcode( $branchcode );
915     $borrower->store;
916
917     my $account = Koha::Account->new({ patron_id => $borrower->id });
918
919     my $line1 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amount => 10, amountoutstanding => 10 })->store();
920     my $line2 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amount => 20, amountoutstanding => 20 })->store();
921
922     is( $account->balance(), 30, "Account balance is 30" );
923     is( $line1->amountoutstanding, 10, 'First fee has amount outstanding of 10' );
924     is( $line2->amountoutstanding, 20, 'Second fee has amount outstanding of 20' );
925
926     my $id = $account->pay(
927         {
928             lines  => [$line1, $line2],
929             amount => 30,
930         }
931     );
932
933     my $account_payment = Koha::Account::Lines->find( $id );
934
935     is( $account->balance(), 0, "Account balance is 0" );
936
937     $line1->_result->discard_changes();
938     $line2->_result->discard_changes();
939     is( $line1->amountoutstanding+0, 0, 'First fee has amount outstanding of 0' );
940     is( $line2->amountoutstanding+0, 0, 'Second fee has amount outstanding of 0' );
941
942     my $ret = $account_payment->void();
943
944     is( ref($ret), 'Koha::Account::Line', 'Void returns the account line' );
945     is( $account->balance(), 30, "Account balance is again 30" );
946
947     $account_payment->_result->discard_changes();
948     $line1->_result->discard_changes();
949     $line2->_result->discard_changes();
950
951     is( $account_payment->accounttype, 'VOID', 'Voided payment accounttype is VOID' );
952     is( $account_payment->amount+0, 0, 'Voided payment amount is 0' );
953     is( $account_payment->amountoutstanding+0, 0, 'Voided payment amount outstanding is 0' );
954
955     is( $line1->amountoutstanding+0, 10, 'First fee again has amount outstanding of 10' );
956     is( $line2->amountoutstanding+0, 20, 'Second fee again has amount outstanding of 20' );
957
958     # Accountlines that are not credits should be un-voidable
959     my $line1_pre = $line1->unblessed();
960     $ret = $line1->void();
961     $line1->_result->discard_changes();
962     my $line1_post = $line1->unblessed();
963     is( $ret, undef, 'Attempted void on non-credit returns undef' );
964     is_deeply( $line1_pre, $line1_post, 'Non-credit account line cannot be voided' )
965 };
966
967 subtest "Koha::Account::Offset credit & debit tests" => sub {
968
969     plan tests => 4;
970
971     # Create a borrower
972     my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
973     my $branchcode   = $builder->build({ source => 'Branch' })->{ branchcode };
974
975     my $borrower = Koha::Patron->new( {
976         cardnumber => 'kyliehall',
977         surname => 'Hall',
978         firstname => 'Kylie',
979     } );
980     $borrower->categorycode( $categorycode );
981     $borrower->branchcode( $branchcode );
982     $borrower->store;
983
984     my $account = Koha::Account->new({ patron_id => $borrower->id });
985
986     my $line1 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amount => 10, amountoutstanding => 10 })->store();
987     my $line2 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amount => 20, amountoutstanding => 20 })->store();
988
989     my $id = $account->pay(
990         {
991             lines  => [$line1, $line2],
992             amount => 30,
993         }
994     );
995
996     # Test debit and credit methods for Koha::Account::Offset
997     my $account_offset = Koha::Account::Offsets->find( { credit_id => $id, debit_id => $line1->id } );
998     is( $account_offset->debit->id, $line1->id, "Koha::Account::Offset->debit gets correct accountline" );
999     is( $account_offset->credit->id, $id, "Koha::Account::Offset->credit gets correct accountline" );
1000
1001     $account_offset = Koha::Account::Offset->new(
1002         {
1003             credit_id => undef,
1004             debit_id  => undef,
1005             type      => 'Payment',
1006             amount    => 0,
1007         }
1008     )->store();
1009
1010     is( $account_offset->debit, undef, "Koha::Account::Offset->debit returns undef if no associated debit" );
1011     is( $account_offset->credit, undef, "Koha::Account::Offset->credit returns undef if no associated credit" );
1012 };
1013
1014 subtest "Payment notice tests" => sub {
1015
1016     plan tests => 8;
1017
1018     Koha::Account::Lines->delete();
1019     Koha::Patrons->delete();
1020     Koha::Notice::Messages->delete();
1021     # Create a borrower
1022     my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
1023     my $branchcode   = $builder->build({ source => 'Branch' })->{ branchcode };
1024
1025     my $borrower = Koha::Patron->new(
1026         {
1027             cardnumber   => 'chelseahall',
1028             surname      => 'Hall',
1029             firstname    => 'Chelsea',
1030             email        => 'chelsea@example.com',
1031             categorycode => $categorycode,
1032             branchcode   => $branchcode,
1033         }
1034     )->store();
1035
1036     my $account = Koha::Account->new({ patron_id => $borrower->id });
1037
1038     my $line = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amountoutstanding => 27 })->store();
1039
1040     my $letter = Koha::Notice::Templates->find( { code => 'ACCOUNT_PAYMENT' } );
1041     $letter->content('[%- USE Price -%]A payment of [% credit.amount * -1 | $Price %] has been applied to your account.');
1042     $letter->store();
1043
1044     t::lib::Mocks::mock_preference('UseEmailReceipts', '0');
1045
1046     my $id = $account->pay( { amount => 1 } );
1047     is( Koha::Notice::Messages->search()->count(), 0, 'Notice for payment not sent if UseEmailReceipts is disabled' );
1048
1049     $id = $account->pay( { amount => 1, type => 'writeoff' } );
1050     is( Koha::Notice::Messages->search()->count(), 0, 'Notice for writeoff not sent if UseEmailReceipts is disabled' );
1051
1052     t::lib::Mocks::mock_preference('UseEmailReceipts', '1');
1053
1054     $id = $account->pay( { amount => 12 } );
1055     my $notice = Koha::Notice::Messages->search()->next();
1056     is( $notice->subject, 'Account payment', 'Notice subject is correct for payment' );
1057     is( $notice->letter_code, 'ACCOUNT_PAYMENT', 'Notice letter code is correct for payment' );
1058     is( $notice->content, 'A payment of 12.00 has been applied to your account.', 'Notice content is correct for payment' );
1059     $notice->delete();
1060
1061     $letter = Koha::Notice::Templates->find( { code => 'ACCOUNT_WRITEOFF' } );
1062     $letter->content('[%- USE Price -%]A writeoff of [% credit.amount * -1 | $Price %] has been applied to your account.');
1063     $letter->store();
1064
1065     $id = $account->pay( { amount => 13, type => 'writeoff' } );
1066     $notice = Koha::Notice::Messages->search()->next();
1067     is( $notice->subject, 'Account writeoff', 'Notice subject is correct for payment' );
1068     is( $notice->letter_code, 'ACCOUNT_WRITEOFF', 'Notice letter code is correct for writeoff' );
1069     is( $notice->content, 'A writeoff of 13.00 has been applied to your account.', 'Notice content is correct for writeoff' );
1070 };
1071
1072 1;