Bug 31196: Remove 'default_value_for_mod_marc-' clear_from_cache calls
[koha.git] / t / db_dependent / Overdues.t
1 #!/usr/bin/perl;
2
3 use Modern::Perl;
4 use Test::More tests => 17;
5 use Test::Warn;
6
7 use C4::Context;
8 use Koha::Database;
9 use Koha::Libraries;
10
11 use t::lib::Mocks;
12 use t::lib::TestBuilder;
13
14 use_ok('C4::Overdues', qw( GetOverdueMessageTransportTypes GetBranchcodesWithOverdueRules UpdateFine ));
15 can_ok('C4::Overdues', 'GetOverdueMessageTransportTypes');
16 can_ok('C4::Overdues', 'GetBranchcodesWithOverdueRules');
17
18 my $schema = Koha::Database->new->schema;
19 my $builder = t::lib::TestBuilder->new;
20
21 $schema->storage->txn_begin;
22 my $dbh = C4::Context->dbh;
23
24 $dbh->do(q|DELETE FROM letter|);
25 $dbh->do(q|DELETE FROM message_queue|);
26 $dbh->do(q|DELETE FROM message_transport_types|);
27 $dbh->do(q|DELETE FROM overduerules|);
28 $dbh->do(q|DELETE FROM overduerules_transport_types|);
29
30 $dbh->do(q|
31     INSERT INTO message_transport_types( message_transport_type ) VALUES ('email'), ('phone'), ('print'), ('sms')
32 |);
33
34 $dbh->do(q|
35     INSERT INTO overduerules ( overduerules_id, branchcode, categorycode ) VALUES
36     (1, 'CPL', 'PT'),
37     (2, 'CPL', 'YA'),
38     (3, '', 'PT'),
39     (4, '', 'YA')
40 |);
41
42 $dbh->do(q|INSERT INTO overduerules_transport_types (overduerules_id, letternumber, message_transport_type) VALUES
43     (1, 1, 'email'),
44     (1, 2, 'sms'),
45     (1, 3, 'email'),
46     (2, 3, 'print'),
47     (3, 1, 'email'),
48     (3, 2, 'email'),
49     (3, 2, 'sms'),
50     (3, 3, 'sms'),
51     (3, 3, 'email'),
52     (3, 3, 'print'),
53     (4, 2, 'sms')
54 |);
55
56 my $mtts;
57
58 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('CPL', 'PT');
59 is( $mtts, undef, 'GetOverdueMessageTransportTypes: returns undef if no letternumber given' );
60
61 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('CPL', undef, 1);
62 is( $mtts, undef, 'GetOverdueMessageTransportTypes: returns undef if no categorycode given' );
63
64 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('CPL');
65 is( $mtts, undef, 'GetOverdueMessageTransportTypes: returns undef if no letternumber and categorycode given' );
66
67 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('CPL', 'PT', 1);
68 is_deeply( $mtts, ['email'], 'GetOverdueMessageTransportTypes: first overdue is by email for PT (CPL)' );
69
70 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('CPL', 'PT', 2);
71 is_deeply( $mtts, ['sms'], 'GetOverdueMessageTransportTypes: second overdue is by sms for PT (CPL)' );
72
73 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('CPL', 'PT', 3);
74 is_deeply( $mtts, ['email'], 'GetOverdueMessageTransportTypes: third overdue is by email for PT (CPL)' );
75
76 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('', 'PT', 1);
77 is_deeply( $mtts, ['email'], 'GetOverdueMessageTransportTypes: first overdue is by email for PT (default)' );
78
79 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('', 'PT', 2);
80 is_deeply( $mtts, ['email', 'sms'], 'GetOverdueMessageTransportTypes: second overdue is by email and sms for PT (default)' );
81
82 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('', 'PT', 3);
83 is_deeply( $mtts, ['print', 'sms', 'email'], 'GetOverdueMessageTransportTypes: third overdue is by print, sms and email for PT (default). With print in first.' );
84
85 # Test GetBranchcodesWithOverdueRules
86 $dbh->do(q|DELETE FROM overduerules|);
87 $dbh->do(q|
88     INSERT INTO overduerules
89         ( branchcode,categorycode, delay1,letter1,debarred1, delay2,letter2,debarred2, delay3,letter3,debarred3 )
90         VALUES
91         ( '', '', 1, 'LETTER_CODE1', 1, 5, 'LETTER_CODE2', 1, 10, 'LETTER_CODE3', 1 )
92 |);
93
94 my @branchcodes = map { $_->branchcode } Koha::Libraries->search->as_list;
95
96 my @overdue_branches = C4::Overdues::GetBranchcodesWithOverdueRules();
97 is_deeply( [ sort @overdue_branches ], [ sort @branchcodes ], 'If a default rule exists, all branches should be returned' );
98
99 $dbh->do(q|
100     INSERT INTO overduerules
101         ( branchcode,categorycode, delay1,letter1,debarred1, delay2,letter2,debarred2, delay3,letter3,debarred3 )
102         VALUES
103         ( 'CPL', '', 1, 'LETTER_CODE1', 1, 5, 'LETTER_CODE2', 1, 10, 'LETTER_CODE3', 1 )
104 |);
105
106 @overdue_branches = C4::Overdues::GetBranchcodesWithOverdueRules();
107 is_deeply( [ sort @overdue_branches ], [ sort @branchcodes ], 'If a default rule exists and a specific rule exists, all branches should be returned' );
108
109 $dbh->do(q|DELETE FROM overduerules|);
110 $dbh->do(q|
111     INSERT INTO overduerules
112         ( branchcode,categorycode, delay1,letter1,debarred1, delay2,letter2,debarred2, delay3,letter3,debarred3 )
113         VALUES
114         ( 'CPL', '', 1, 'LETTER_CODE1', 1, 5, 'LETTER_CODE2', 1, 10, 'LETTER_CODE3', 1 )
115 |);
116
117 @overdue_branches = C4::Overdues::GetBranchcodesWithOverdueRules();
118 is_deeply( \@overdue_branches, ['CPL'] , 'If only a specific rule exist, only 1 branch should be returned' );
119
120 $dbh->do(q|DELETE FROM overduerules|);
121 $dbh->do(q|
122     INSERT INTO overduerules
123         ( branchcode,categorycode, delay1,letter1,debarred1, delay2,letter2,debarred2, delay3,letter3,debarred3 )
124         VALUES
125         ( 'CPL', '', 1, 'LETTER_CODE1_CPL', 1, 5, 'LETTER_CODE2_CPL', 1, 10, 'LETTER_CODE3_CPL', 1 ),
126         ( 'MPL', '', 1, 'LETTER_CODE1_MPL', 1, 5, 'LETTER_CODE2_MPL', 1, 10, 'LETTER_CODE3_MPL', 1 )
127 |);
128
129 @overdue_branches = C4::Overdues::GetBranchcodesWithOverdueRules();
130 is_deeply( \@overdue_branches, ['CPL', 'MPL'] , 'If only 2 specific rules exist, 2 branches should be returned' );
131
132 $schema->storage->txn_rollback;
133
134 subtest 'UpdateFine tests' => sub {
135
136     plan tests => 75;
137
138     $schema->storage->txn_begin;
139
140     t::lib::Mocks::mock_preference( 'MaxFine', '100' );
141
142     my $patron    = $builder->build_object( { class => 'Koha::Patrons' } );
143     my $item1     = $builder->build_sample_item();
144     my $item2     = $builder->build_sample_item();
145     my $checkout1 = $builder->build_object(
146         {
147             class => 'Koha::Checkouts',
148             value => { itemnumber => $item1->itemnumber, borrowernumber => $patron->id }
149         }
150     );
151     my $checkout2 = $builder->build_object(
152         {
153             class => 'Koha::Checkouts',
154             value => { itemnumber => $item2->itemnumber, borrowernumber => $patron->id }
155         }
156     );
157
158     # Try to add 0 amount fine
159     UpdateFine(
160         {
161             issue_id       => $checkout1->issue_id,
162             itemnumber     => $item1->itemnumber,
163             borrowernumber => $patron->borrowernumber,
164             amount         => '0',
165             due            => $checkout1->date_due
166         }
167     );
168
169     my $fines = Koha::Account::Lines->search(
170         { borrowernumber => $patron->borrowernumber } );
171     is( $fines->count, 0, "No fine added when amount is 0" );
172     # Total : Outstanding : MaxFine
173     #   0   :      0      :   100
174
175     # Add fine 1 - First Item Overdue
176     UpdateFine(
177         {
178             issue_id       => $checkout1->issue_id,
179             itemnumber     => $item1->itemnumber,
180             borrowernumber => $patron->borrowernumber,
181             amount         => '50',
182             due            => $checkout1->date_due
183         }
184     );
185
186     $fines = Koha::Account::Lines->search(
187         { borrowernumber => $patron->borrowernumber } );
188     is( $fines->count, 1, "Fine added when amount is greater than 0" );
189     my $fine = $fines->next;
190     is( $fine->amount+0, 50, "Fine amount correctly set to 50" );
191     is( $fine->amountoutstanding+0, 50, "Fine amountoutstanding correctly set to 50" );
192     is( $fine->issue_id, $checkout1->issue_id, "Fine is associated with the correct issue" );
193     is( $fine->itemnumber, $checkout1->itemnumber, "Fine is associated with the correct item" );
194     # Total : Outstanding : MaxFine
195     #  50   :     50      :   100
196
197     # Increase fine 1 - First Item Overdue
198     UpdateFine(
199         {
200             issue_id       => $checkout1->issue_id,
201             itemnumber     => $item1->itemnumber,
202             borrowernumber => $patron->borrowernumber,
203             amount         => '80',
204             due            => $checkout1->date_due
205         }
206     );
207
208     $fines = Koha::Account::Lines->search(
209         { borrowernumber => $patron->borrowernumber } );
210     is( $fines->count, 1, "Existing fine updated" );
211     $fine = $fines->next;
212     is( $fine->amount+0, 80, "Fine amount correctly updated to 80" );
213     is( $fine->amountoutstanding+0, 80, "Fine amountoutstanding correctly updated to 80" );
214     # Total : Outstanding : MaxFine
215     #  80   :     80      :   100
216
217     # Add fine 2 - Second Item Overdue
218     UpdateFine(
219         {
220             issue_id       => $checkout2->issue_id,
221             itemnumber     => $item2->itemnumber,
222             borrowernumber => $patron->borrowernumber,
223             amount         => '30',
224             due            => $checkout2->date_due
225         }
226     );
227
228     $fines = Koha::Account::Lines->search(
229         { borrowernumber => $patron->borrowernumber },
230         { order_by       => { '-asc' => 'accountlines_id' } }
231     );
232     is( $fines->count,        2,    "New fine added for second checkout" );
233     $fine = $fines->next;
234     is( $fine->amount+0, 80, "First fine amount unchanged" );
235     is( $fine->amountoutstanding+0, 80, "First fine amountoutstanding unchanged" );
236     my $fine2 = $fines->next;
237     is( $fine2->amount+0, 20, "Second fine capped at '20' by MaxFine" );
238     is( $fine2->amountoutstanding+0, 20, "Second fine amountoutstanding capped at '20' by MaxFine" );
239     is( $fine2->issue_id, $checkout2->issue_id, "Second fine is associated with the correct issue" );
240     is( $fine2->itemnumber, $checkout2->itemnumber, "Second fine is associated with the correct item" );
241     is( $fine->amount + $fine2->amount, '100', "Total fines = 100" );
242     is( $fine->amountoutstanding + $fine2->amountoutstanding, '100', "Total outstanding = 100" );
243     # Total : Outstanding : MaxFine
244     #  100  :     100     :   100
245
246     # A day passes, the item is still overdue, update fine is called again
247     # we don't expect to increase above MaxFine of 100
248     UpdateFine(
249         {
250             issue_id       => $checkout2->issue_id,
251             itemnumber     => $item2->itemnumber,
252             borrowernumber => $patron->borrowernumber,
253             amount         => '40',
254             due            => $checkout2->date_due
255         }
256     );
257
258     $fines = Koha::Account::Lines->search(
259         { borrowernumber => $patron->borrowernumber },
260         { order_by       => { '-asc' => 'accountlines_id' } }
261     );
262     is( $fines->count,        2,    "Existing fine updated for second checkout, no new fine added" );
263     $fine = $fines->next;
264     is( $fine->amount+0, 80, "First fine amount unchanged" );
265     is( $fine->amountoutstanding+0, 80, "First fine amountoutstanding unchanged" );
266     $fine2 = $fines->next;
267     is( $fine2->amount+0, 20, "Second fine capped at '20' by MaxFine" );
268     is( $fine2->amountoutstanding+0, 20, "Second fine amountoutstanding capped at '20' by MaxFine" );
269     is( $fine2->issue_id, $checkout2->issue_id, "Second fine is associated with the correct issue" );
270     is( $fine2->itemnumber, $checkout2->itemnumber, "Second fine is associated with the correct item" );
271     is( $fine->amount + $fine2->amount, '100', "Total fines = 100" );
272     is( $fine->amountoutstanding + $fine2->amountoutstanding, '100', "Total outstanding = 100" );
273     # Total : Outstanding : MaxFine
274     #  100  :     100     :   100
275
276     # Partial pay fine 1
277     $fine->amountoutstanding(50)->store;
278     # Total : Outstanding : MaxFine
279     #  100  :     50      :   100
280
281     # Increase fine 2 - Second item overdue
282     UpdateFine(
283         {
284             issue_id       => $checkout2->issue_id,
285             itemnumber     => $item2->itemnumber,
286             borrowernumber => $patron->borrowernumber,
287             amount         => '30',
288             due            => $checkout2->date_due
289         }
290     );
291
292     $fines = Koha::Account::Lines->search(
293         { borrowernumber => $patron->borrowernumber },
294         { order_by       => { '-asc' => 'accountlines_id' } }
295     );
296     is( $fines->count,        2,    "Still two fines after second checkout update" );
297     $fine = $fines->next;
298     is( $fine->amount+0, 80, "First fine amount unchanged" );
299     is( $fine->amountoutstanding+0, 50, "First fine amountoutstanding unchanged" );
300     $fine2 = $fines->next;
301     is( $fine2->amount+0, 30, "Second fine increased after partial payment of first" );
302     is( $fine2->amountoutstanding+0, 30, "Second fine amountoutstanding increased after partial payment of first" );
303     is( $fine->amount + $fine2->amount, '110', "Total fines = 100" );
304     is( $fine->amountoutstanding + $fine2->amountoutstanding, '80', "Total outstanding = 80" );
305     # Total : Outstanding : MaxFine
306     #  110  :     80      :   100
307
308     # Fix fine 1 - First item renewed
309     $fine->status('RETURNED')->store;
310
311     # Add fine 3 - First item second overdue
312     UpdateFine(
313         {
314             issue_id       => $checkout1->issue_id,
315             itemnumber     => $item1->itemnumber,
316             borrowernumber => $patron->borrowernumber,
317             amount         => '30',
318             due            => $checkout1->date_due
319         }
320     );
321
322     $fines = Koha::Account::Lines->search(
323         { borrowernumber => $patron->borrowernumber },
324         { order_by       => { '-asc' => 'accountlines_id' } }
325     );
326     is( $fines->count,        3,    "Third fine added for overdue renewal" );
327     $fine = $fines->next;
328     is( $fine->amount+0, 80, "First fine amount unchanged" );
329     is( $fine->amountoutstanding+0, 50, "First fine amountoutstanding unchanged" );
330     $fine2 = $fines->next;
331     is( $fine2->amount+0, 30, "Second fine amount unchanged" );
332     is( $fine2->amountoutstanding+0, 30, "Second fine amountoutstanding unchanged" );
333     my $fine3 = $fines->next;
334     is( $fine3->amount+0, 20, "Third fine amount capped due to MaxFine" );
335     is( $fine3->amountoutstanding+0, 20, "Third fine amountoutstanding capped at '20' by MaxFine" );
336     is( $fine3->issue_id, $checkout1->issue_id, "Third fine is associated with the correct issue" );
337     is( $fine3->itemnumber, $checkout1->itemnumber, "Third fine is associated with the correct item" );
338     is( $fine->amount + $fine2->amount + $fine3->amount, '130', "Total fines = 130" );
339     is( $fine->amountoutstanding + $fine2->amountoutstanding + $fine3->amountoutstanding, '100', "Total outstanding = 100" );
340     # Total : Outstanding : MaxFine
341     #  130  :     100     :   100
342
343     # Payoff accruing fine and ensure next increment doesn't create a new one (bug #24146)
344     $fine3->amountoutstanding('0')->store;
345     is( $fine->amount + $fine2->amount + $fine3->amount, '130', "Total fines = 130" );
346     is( $fine->amountoutstanding + $fine2->amountoutstanding + $fine3->amountoutstanding, '80', "Total outstanding = 80" );
347     # Total : Outstanding : MaxFine
348     #  130  :      80     :   100
349
350     # Increase fine 3 - First item, second overdue increase
351     UpdateFine(
352         {
353             issue_id       => $checkout1->issue_id,
354             itemnumber     => $item1->itemnumber,
355             borrowernumber => $patron->borrowernumber,
356             amount         => '50',
357             due            => $checkout1->date_due
358         }
359     );
360
361     $fines = Koha::Account::Lines->search(
362         { borrowernumber => $patron->borrowernumber },
363         { order_by       => { '-asc' => 'accountlines_id' } }
364     );
365     is( $fines->count,        3,    "Still three fines after third checkout update" );
366     $fine = $fines->next;
367     is( $fine->amount+0, 80, "First fine amount unchanged" );
368     is( $fine->amountoutstanding+0, 50, "First fine amountoutstanding unchanged" );
369     $fine2 = $fines->next;
370     is( $fine2->amount+0, 30, "Second fine amount unchanged" );
371     is( $fine2->amountoutstanding+0, 30, "Second fine amountoutstanding unchanged" );
372     $fine3 = $fines->next;
373     is( $fine3->amount+0, 40, "Third fine amount capped due to MaxFine" );
374     is( $fine3->amountoutstanding+0, 20, "Third fine amountoutstanding increased ..." );
375     is( $fine3->issue_id, $checkout1->issue_id, "Third fine is associated with the correct issue" );
376     is( $fine3->itemnumber, $checkout1->itemnumber, "Third fine is associated with the correct item" );
377     is( $fine->amount + $fine2->amount + $fine3->amount, '150', "Total fines = 150" );
378     is( $fine->amountoutstanding + $fine2->amountoutstanding + $fine3->amountoutstanding, '100', "Total outstanding = 100" );
379     # Total : Outstanding : MaxFine
380     #  150  :      100     :   100
381
382     # FIXME: Add test to check whether sundry/manual charges are included within MaxFine.
383     # FIXME: Add test to ensure other charges are not included within MaxFine.
384
385     # Disable MaxFine
386     t::lib::Mocks::mock_preference( 'MaxFine', '0' );
387     UpdateFine(
388         {
389             issue_id       => $checkout1->issue_id,
390             itemnumber     => $item1->itemnumber,
391             borrowernumber => $patron->borrowernumber,
392             amount         => '50',
393             due            => $checkout1->date_due
394         }
395     );
396
397     $fines = Koha::Account::Lines->search(
398         { borrowernumber => $patron->borrowernumber },
399         { order_by       => { '-asc' => 'accountlines_id' } }
400     );
401     is( $fines->count,        3,    "Still only three fines after MaxFine cap removed" );
402     $fine = $fines->next;
403     is( $fine->amount+0, 80, "First fine amount unchanged" );
404     $fine2 = $fines->next;
405     is( $fine2->amount+0, 30, "Second fine amount unchanged" );
406     $fine3 = $fines->next;
407     is( $fine3->amount+0, 50, "Third fine increased now MaxFine cap is disabled" );
408     is( $fine3->amountoutstanding+0, 30, "Third fine increased now MaxFine cap is disabled" );
409
410     # If somehow the fine should be reduced, we changed rules or checkout date or something
411     UpdateFine(
412         {
413             issue_id       => $checkout1->issue_id,
414             itemnumber     => $item1->itemnumber,
415             borrowernumber => $patron->borrowernumber,
416             amount         => '30',
417             due            => $checkout1->date_due
418         }
419     );
420
421     $fines = Koha::Account::Lines->search(
422         { borrowernumber => $patron->borrowernumber },
423         { order_by       => { '-asc' => 'accountlines_id' } }
424     );
425     is( $fines->count,        3,    "Still only three fines after MaxFine cap removed and third fine altered" );
426     $fine = $fines->next;
427     is( $fine->amount+0, 80, "First fine amount unchanged" );
428     $fine2 = $fines->next;
429     is( $fine2->amount+0, 30, "Second fine amount unchanged" );
430     $fine3 = $fines->next;
431     is( $fine3->amount+0, 30, "Third fine reduced" );
432     is( $fine3->amountoutstanding+0, 10, "Third fine amount outstanding is reduced" );
433
434     # Ensure maxfine calculations work correctly for floats (bug #25127)
435     # 7.2 (maxfine) - 7.2 (total_amount_other) != 8.88178419700125e-16 (😢)
436     t::lib::Mocks::mock_preference( 'MaxFine', '7.2' );
437     my $patron_1   = $builder->build_object( { class => 'Koha::Patrons' } );
438     my $item_1     = $builder->build_sample_item();
439     my $item_2     = $builder->build_sample_item();
440     my $checkout_1 = $builder->build_object(
441         {
442             class => 'Koha::Checkouts',
443             value => {
444                 itemnumber     => $item_1->itemnumber,
445                 borrowernumber => $patron_1->id
446             }
447         }
448     );
449     my $checkout_2 = $builder->build_object(
450         {
451             class => 'Koha::Checkouts',
452             value => {
453                 itemnumber     => $item_2->itemnumber,
454                 borrowernumber => $patron->id
455             }
456         }
457     );
458     my $account = $patron_1->account;
459     $account->add_debit(
460         {
461             type      => 'OVERDUE',
462             amount    => '6.99',
463             issue_id  => $checkout_1->issue_id,
464             interface => 'TEST'
465         }
466     );
467     $account->add_debit(
468         {
469             type      => 'OVERDUE',
470             amount    => '.10',
471             issue_id  => $checkout_1->issue_id,
472             interface => 'TEST'
473         }
474     );
475     $account->add_debit(
476         {
477             type      => 'OVERDUE',
478             amount    => '.10',
479             issue_id  => $checkout_1->issue_id,
480             interface => 'TEST'
481         }
482     );
483     $account->add_debit(
484         {
485             type      => 'OVERDUE',
486             amount    => '.01',
487             issue_id  => $checkout_1->issue_id,
488             interface => 'TEST'
489         }
490     );
491     UpdateFine(
492         {
493             issue_id       => $checkout_2->issue_id,
494             itemnumber     => $item_2->itemnumber,
495             borrowernumber => $patron_1->borrowernumber,
496             amount         => '.1',
497             due            => $checkout_2->date_due
498         }
499     );
500     $fines = Koha::Account::Lines->search(
501         { borrowernumber => $patron_1->borrowernumber },
502         { order_by       => { '-asc' => 'accountlines_id' } }
503     );
504     is( $fines->count,        4,    "New amount should be 0 so no fine added" );
505     ok( C4::Circulation::AddReturn( $item_1->barcode, $item_1->homebranch, 1), "Returning the item and forgiving fines succeeds");
506
507     t::lib::Mocks::mock_preference( 'MaxFine', 0 );
508
509     # Ensure CalcFine calculations work correctly for floats (bug #27079)
510     # 1.800000 (amount from database) != 1.8~ (CalcFine of 0.15cents * 12units) (😢)
511     my $amount = 0.15 * 12;
512     UpdateFine(
513         {
514             issue_id       => $checkout_2->issue_id,
515             itemnumber     => $item_2->itemnumber,
516             borrowernumber => $patron_1->borrowernumber,
517             amount         => $amount,
518             due            => $checkout_2->date_due
519         }
520     );
521     $fine = Koha::Account::Lines->search({ issue_id => $checkout_2->issue_id })->single;
522     ok( $fine, 'Fine added for checkout 2');
523     is( $fine->amount, "1.800000", "Fine amount is 1.800000 as expected");
524
525     $fine->amountoutstanding(0)->store;
526     $fine->discard_changes;
527     is( $fine->amountoutstanding + 0, 0, "Fine was paid off");
528     UpdateFine(
529         {
530             issue_id       => $checkout_2->issue_id,
531             itemnumber     => $item_2->itemnumber,
532             borrowernumber => $patron_1->borrowernumber,
533             amount         => $amount,
534             due            => $checkout_2->date_due
535         }
536     );
537     my $refunds = Koha::Account::Lines->search({ itemnumber => $item_2->itemnumber, credit_type_code => 'OVERPAYMENT' });
538     is( $refunds->count, 0, "Overpayment refund not added when the amounts are equal" );
539
540     # Adding an OVERDUE fine not linked with a checkout (possible with historical OVERDUE fines)
541     $builder->build_object(
542         {
543             class => "Koha::Account::Lines",
544             value => {
545                 borrowernumber => $patron_1->borrowernumber,
546                 issue_id       => undef,
547                 debit_type_code => 'OVERDUE',
548             }
549         }
550     );
551     $fine->issue_id(undef)->store;
552     warnings_are {
553         UpdateFine(
554             {
555                 issue_id       => $checkout_2->issue_id,
556                 itemnumber     => $item_2->itemnumber,
557                 borrowernumber => $patron_1->borrowernumber,
558                 amount         => $amount,
559                 due            => $checkout_2->date_due
560             }
561         );
562     } [], 'No warning generated if fine is not linked with a checkout';
563
564     $schema->storage->txn_rollback;
565 };