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