4 use Test::More tests => 17;
11 use t::lib::TestBuilder;
13 use_ok('C4::Overdues');
14 can_ok('C4::Overdues', 'GetOverdueMessageTransportTypes');
15 can_ok('C4::Overdues', 'GetBranchcodesWithOverdueRules');
17 my $schema = Koha::Database->new->schema;
18 my $builder = t::lib::TestBuilder->new;
20 $schema->storage->txn_begin;
21 my $dbh = C4::Context->dbh;
23 $dbh->do(q|DELETE FROM letter|);
24 $dbh->do(q|DELETE FROM message_queue|);
25 $dbh->do(q|DELETE FROM message_transport_types|);
26 $dbh->do(q|DELETE FROM overduerules|);
27 $dbh->do(q|DELETE FROM overduerules_transport_types|);
30 INSERT INTO message_transport_types( message_transport_type ) VALUES ('email'), ('phone'), ('print'), ('sms')
34 INSERT INTO overduerules ( overduerules_id, branchcode, categorycode ) VALUES
41 $dbh->do(q|INSERT INTO overduerules_transport_types (overduerules_id, letternumber, message_transport_type) VALUES
57 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('CPL', 'PT');
58 is( $mtts, undef, 'GetOverdueMessageTransportTypes: returns undef if no letternumber given' );
60 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('CPL', undef, 1);
61 is( $mtts, undef, 'GetOverdueMessageTransportTypes: returns undef if no categorycode given' );
63 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('CPL');
64 is( $mtts, undef, 'GetOverdueMessageTransportTypes: returns undef if no letternumber and categorycode given' );
66 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('CPL', 'PT', 1);
67 is_deeply( $mtts, ['email'], 'GetOverdueMessageTransportTypes: first overdue is by email for PT (CPL)' );
69 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('CPL', 'PT', 2);
70 is_deeply( $mtts, ['sms'], 'GetOverdueMessageTransportTypes: second overdue is by sms for PT (CPL)' );
72 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('CPL', 'PT', 3);
73 is_deeply( $mtts, ['email'], 'GetOverdueMessageTransportTypes: third overdue is by email for PT (CPL)' );
75 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('', 'PT', 1);
76 is_deeply( $mtts, ['email'], 'GetOverdueMessageTransportTypes: first overdue is by email for PT (default)' );
78 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('', 'PT', 2);
79 is_deeply( $mtts, ['email', 'sms'], 'GetOverdueMessageTransportTypes: second overdue is by email and sms for PT (default)' );
81 $mtts = C4::Overdues::GetOverdueMessageTransportTypes('', 'PT', 3);
82 is_deeply( $mtts, ['print', 'sms', 'email'], 'GetOverdueMessageTransportTypes: third overdue is by print, sms and email for PT (default). With print in first.' );
84 # Test GetBranchcodesWithOverdueRules
85 $dbh->do(q|DELETE FROM overduerules|);
87 INSERT INTO overduerules
88 ( branchcode,categorycode, delay1,letter1,debarred1, delay2,letter2,debarred2, delay3,letter3,debarred3 )
90 ( '', '', 1, 'LETTER_CODE1', 1, 5, 'LETTER_CODE2', 1, 10, 'LETTER_CODE3', 1 )
93 my @branchcodes = map { $_->branchcode } Koha::Libraries->search;
95 my @overdue_branches = C4::Overdues::GetBranchcodesWithOverdueRules();
96 is_deeply( [ sort @overdue_branches ], [ sort @branchcodes ], 'If a default rule exists, all branches should be returned' );
99 INSERT INTO overduerules
100 ( branchcode,categorycode, delay1,letter1,debarred1, delay2,letter2,debarred2, delay3,letter3,debarred3 )
102 ( 'CPL', '', 1, 'LETTER_CODE1', 1, 5, 'LETTER_CODE2', 1, 10, 'LETTER_CODE3', 1 )
105 @overdue_branches = C4::Overdues::GetBranchcodesWithOverdueRules();
106 is_deeply( [ sort @overdue_branches ], [ sort @branchcodes ], 'If a default rule exists and a specific rule exists, all branches should be returned' );
108 $dbh->do(q|DELETE FROM overduerules|);
110 INSERT INTO overduerules
111 ( branchcode,categorycode, delay1,letter1,debarred1, delay2,letter2,debarred2, delay3,letter3,debarred3 )
113 ( 'CPL', '', 1, 'LETTER_CODE1', 1, 5, 'LETTER_CODE2', 1, 10, 'LETTER_CODE3', 1 )
116 @overdue_branches = C4::Overdues::GetBranchcodesWithOverdueRules();
117 is_deeply( \@overdue_branches, ['CPL'] , 'If only a specific rule exist, only 1 branch should be returned' );
119 $dbh->do(q|DELETE FROM overduerules|);
121 INSERT INTO overduerules
122 ( branchcode,categorycode, delay1,letter1,debarred1, delay2,letter2,debarred2, delay3,letter3,debarred3 )
124 ( 'CPL', '', 1, 'LETTER_CODE1_CPL', 1, 5, 'LETTER_CODE2_CPL', 1, 10, 'LETTER_CODE3_CPL', 1 ),
125 ( 'MPL', '', 1, 'LETTER_CODE1_MPL', 1, 5, 'LETTER_CODE2_MPL', 1, 10, 'LETTER_CODE3_MPL', 1 )
128 @overdue_branches = C4::Overdues::GetBranchcodesWithOverdueRules();
129 is_deeply( \@overdue_branches, ['CPL', 'MPL'] , 'If only 2 specific rules exist, 2 branches should be returned' );
131 $schema->storage->txn_rollback;
133 subtest 'UpdateFine tests' => sub {
137 $schema->storage->txn_begin;
139 t::lib::Mocks::mock_preference( 'MaxFine', '100' );
141 my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
142 my $item1 = $builder->build_sample_item();
143 my $item2 = $builder->build_sample_item();
144 my $checkout1 = $builder->build_object(
146 class => 'Koha::Checkouts',
147 value => { itemnumber => $item1->itemnumber }
150 my $checkout2 = $builder->build_object(
152 class => 'Koha::Checkouts',
153 value => { itemnumber => $item2->itemnumber }
157 # Try to add 0 amount fine
160 issue_id => $checkout1->issue_id,
161 itemnumber => $item1->itemnumber,
162 borrowernumber => $patron->borrowernumber,
164 due => $checkout1->date_due
168 my $fines = Koha::Account::Lines->search(
169 { borrowernumber => $patron->borrowernumber } );
170 is( $fines->count, 0, "No fine added when amount is 0" );
171 # Total : Outstanding : MaxFine
174 # Add fine 1 - First Item Overdue
177 issue_id => $checkout1->issue_id,
178 itemnumber => $item1->itemnumber,
179 borrowernumber => $patron->borrowernumber,
181 due => $checkout1->date_due
185 $fines = Koha::Account::Lines->search(
186 { borrowernumber => $patron->borrowernumber } );
187 is( $fines->count, 1, "Fine added when amount is greater than 0" );
188 my $fine = $fines->next;
189 is( $fine->amount+0, 50, "Fine amount correctly set to 50" );
190 is( $fine->amountoutstanding+0, 50, "Fine amountoutstanding correctly set to 50" );
191 is( $fine->issue_id, $checkout1->issue_id, "Fine is associated with the correct issue" );
192 is( $fine->itemnumber, $checkout1->itemnumber, "Fine is associated with the correct item" );
193 # Total : Outstanding : MaxFine
196 # Increase fine 1 - First Item Overdue
199 issue_id => $checkout1->issue_id,
200 itemnumber => $item1->itemnumber,
201 borrowernumber => $patron->borrowernumber,
203 due => $checkout1->date_due
207 $fines = Koha::Account::Lines->search(
208 { borrowernumber => $patron->borrowernumber } );
209 is( $fines->count, 1, "Existing fine updated" );
210 $fine = $fines->next;
211 is( $fine->amount+0, 80, "Fine amount correctly updated to 80" );
212 is( $fine->amountoutstanding+0, 80, "Fine amountoutstanding correctly updated to 80" );
213 # Total : Outstanding : MaxFine
216 # Add fine 2 - Second Item Overdue
219 issue_id => $checkout2->issue_id,
220 itemnumber => $item2->itemnumber,
221 borrowernumber => $patron->borrowernumber,
223 due => $checkout2->date_due
227 $fines = Koha::Account::Lines->search(
228 { borrowernumber => $patron->borrowernumber },
229 { order_by => { '-asc' => 'accountlines_id' } }
231 is( $fines->count, 2, "New fine added for second checkout" );
232 $fine = $fines->next;
233 is( $fine->amount+0, 80, "First fine amount unchanged" );
234 is( $fine->amountoutstanding+0, 80, "First fine amountoutstanding unchanged" );
235 my $fine2 = $fines->next;
236 is( $fine2->amount+0, 20, "Second fine capped at '20' by MaxFine" );
237 is( $fine2->amountoutstanding+0, 20, "Second fine amountoutstanding capped at '20' by MaxFine" );
238 is( $fine2->issue_id, $checkout2->issue_id, "Second fine is associated with the correct issue" );
239 is( $fine2->itemnumber, $checkout2->itemnumber, "Second fine is associated with the correct item" );
240 is( $fine->amount + $fine2->amount, '100', "Total fines = 100" );
241 is( $fine->amountoutstanding + $fine2->amountoutstanding, '100', "Total outstanding = 100" );
242 # Total : Outstanding : MaxFine
246 $fine->amountoutstanding(50)->store;
247 # Total : Outstanding : MaxFine
250 # Increase fine 2 - Second item overdue
253 issue_id => $checkout2->issue_id,
254 itemnumber => $item2->itemnumber,
255 borrowernumber => $patron->borrowernumber,
257 due => $checkout2->date_due
261 $fines = Koha::Account::Lines->search(
262 { borrowernumber => $patron->borrowernumber },
263 { order_by => { '-asc' => 'accountlines_id' } }
265 is( $fines->count, 2, "Still two fines after second checkout update" );
266 $fine = $fines->next;
267 is( $fine->amount+0, 80, "First fine amount unchanged" );
268 is( $fine->amountoutstanding+0, 50, "First fine amountoutstanding unchanged" );
269 $fine2 = $fines->next;
270 is( $fine2->amount+0, 30, "Second fine increased after partial payment of first" );
271 is( $fine2->amountoutstanding+0, 30, "Second fine amountoutstanding increased after partial payment of first" );
272 is( $fine->amount + $fine2->amount, '110', "Total fines = 100" );
273 is( $fine->amountoutstanding + $fine2->amountoutstanding, '80', "Total outstanding = 80" );
274 # Total : Outstanding : MaxFine
277 # Fix fine 1 - First item renewed
278 $fine->status('RETURNED')->store;
280 # Add fine 3 - First item second overdue
283 issue_id => $checkout1->issue_id,
284 itemnumber => $item1->itemnumber,
285 borrowernumber => $patron->borrowernumber,
287 due => $checkout1->date_due
291 $fines = Koha::Account::Lines->search(
292 { borrowernumber => $patron->borrowernumber },
293 { order_by => { '-asc' => 'accountlines_id' } }
295 is( $fines->count, 3, "Third fine added for overdue renewal" );
296 $fine = $fines->next;
297 is( $fine->amount+0, 80, "First fine amount unchanged" );
298 is( $fine->amountoutstanding+0, 50, "First fine amountoutstanding unchanged" );
299 $fine2 = $fines->next;
300 is( $fine2->amount+0, 30, "Second fine amount unchanged" );
301 is( $fine2->amountoutstanding+0, 30, "Second fine amountoutstanding unchanged" );
302 my $fine3 = $fines->next;
303 is( $fine3->amount+0, 20, "Third fine amount capped due to MaxFine" );
304 is( $fine3->amountoutstanding+0, 20, "Third fine amountoutstanding capped at '20' by MaxFine" );
305 is( $fine3->issue_id, $checkout1->issue_id, "Third fine is associated with the correct issue" );
306 is( $fine3->itemnumber, $checkout1->itemnumber, "Third fine is associated with the correct item" );
307 is( $fine->amount + $fine2->amount + $fine3->amount, '130', "Total fines = 130" );
308 is( $fine->amountoutstanding + $fine2->amountoutstanding + $fine3->amountoutstanding, '100', "Total outstanding = 100" );
309 # Total : Outstanding : MaxFine
312 # Payoff accruing fine and ensure next increment doesn't create a new one (bug #24146)
313 $fine3->amountoutstanding('0')->store;
314 is( $fine->amount + $fine2->amount + $fine3->amount, '130', "Total fines = 130" );
315 is( $fine->amountoutstanding + $fine2->amountoutstanding + $fine3->amountoutstanding, '80', "Total outstanding = 80" );
316 # Total : Outstanding : MaxFine
319 # Increase fine 3 - First item, second overdue increase
322 issue_id => $checkout1->issue_id,
323 itemnumber => $item1->itemnumber,
324 borrowernumber => $patron->borrowernumber,
326 due => $checkout1->date_due
330 $fines = Koha::Account::Lines->search(
331 { borrowernumber => $patron->borrowernumber },
332 { order_by => { '-asc' => 'accountlines_id' } }
334 is( $fines->count, 3, "Still three fines after third checkout update" );
335 $fine = $fines->next;
336 is( $fine->amount+0, 80, "First fine amount unchanged" );
337 is( $fine->amountoutstanding+0, 50, "First fine amountoutstanding unchanged" );
338 $fine2 = $fines->next;
339 is( $fine2->amount+0, 30, "Second fine amount unchanged" );
340 is( $fine2->amountoutstanding+0, 30, "Second fine amountoutstanding unchanged" );
341 $fine3 = $fines->next;
342 is( $fine3->amount+0, 40, "Third fine amount capped due to MaxFine" );
343 is( $fine3->amountoutstanding+0, 20, "Third fine amountoutstanding increased ..." );
344 is( $fine3->issue_id, $checkout1->issue_id, "Third fine is associated with the correct issue" );
345 is( $fine3->itemnumber, $checkout1->itemnumber, "Third fine is associated with the correct item" );
346 is( $fine->amount + $fine2->amount + $fine3->amount, '150', "Total fines = 150" );
347 is( $fine->amountoutstanding + $fine2->amountoutstanding + $fine3->amountoutstanding, '100', "Total outstanding = 100" );
348 # Total : Outstanding : MaxFine
351 # FIXME: Add test to check whether sundry/manual charges are included within MaxFine.
352 # FIXME: Add test to ensure other charges are not included within MaxFine.
355 t::lib::Mocks::mock_preference( 'MaxFine', '0' );
358 issue_id => $checkout1->issue_id,
359 itemnumber => $item1->itemnumber,
360 borrowernumber => $patron->borrowernumber,
362 due => $checkout1->date_due
366 $fines = Koha::Account::Lines->search(
367 { borrowernumber => $patron->borrowernumber },
368 { order_by => { '-asc' => 'accountlines_id' } }
370 is( $fines->count, 3, "Still only three fines after MaxFine cap removed" );
371 $fine = $fines->next;
372 is( $fine->amount+0, 80, "First fine amount unchanged" );
373 $fine2 = $fines->next;
374 is( $fine2->amount+0, 30, "Second fine amount unchanged" );
375 $fine3 = $fines->next;
376 is( $fine3->amount+0, 50, "Third fine increased now MaxFine cap is disabled" );
377 is( $fine3->amountoutstanding+0, 30, "Third fine increased now MaxFine cap is disabled" );
379 # If somehow the fine should be reduced, we changed rules or checkout date or something
382 issue_id => $checkout1->issue_id,
383 itemnumber => $item1->itemnumber,
384 borrowernumber => $patron->borrowernumber,
386 due => $checkout1->date_due
390 $fines = Koha::Account::Lines->search(
391 { borrowernumber => $patron->borrowernumber },
392 { order_by => { '-asc' => 'accountlines_id' } }
394 is( $fines->count, 3, "Still only three fines after MaxFine cap removed and third fine altered" );
395 $fine = $fines->next;
396 is( $fine->amount+0, 80, "First fine amount unchanged" );
397 $fine2 = $fines->next;
398 is( $fine2->amount+0, 30, "Second fine amount unchanged" );
399 $fine3 = $fines->next;
400 is( $fine3->amount+0, 30, "Third fine reduced" );
401 is( $fine3->amountoutstanding+0, 10, "Third fine amount outstanding is reduced" );
403 $schema->storage->txn_rollback;