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" );
175 issue_id => $checkout1->issue_id,
176 itemnumber => $item1->itemnumber,
177 borrowernumber => $patron->borrowernumber,
179 due => $checkout1->date_due
183 $fines = Koha::Account::Lines->search(
184 { borrowernumber => $patron->borrowernumber } );
185 is( $fines->count, 1, "Fine added when amount is greater than 0" );
186 my $fine = $fines->next;
187 is( $fine->amount, '50.000000', "Fine amount correctly set to 50" );
188 is( $fine->issue_id, $checkout1->issue_id, "Fine is associated with the correct issue" );
189 is( $fine->itemnumber, $checkout1->itemnumber, "Fine is associated with the correct item" );
194 issue_id => $checkout1->issue_id,
195 itemnumber => $item1->itemnumber,
196 borrowernumber => $patron->borrowernumber,
198 due => $checkout1->date_due
202 $fines = Koha::Account::Lines->search(
203 { borrowernumber => $patron->borrowernumber } );
204 is( $fines->count, 1, "Existing fine updated" );
205 $fine = $fines->next;
206 is( $fine->amount, '80.000000', "Fine amount correctly updated to 80" );
211 issue_id => $checkout2->issue_id,
212 itemnumber => $item2->itemnumber,
213 borrowernumber => $patron->borrowernumber,
215 due => $checkout2->date_due
219 $fines = Koha::Account::Lines->search(
220 { borrowernumber => $patron->borrowernumber },
221 { order_by => { '-asc' => 'accountlines_id' } }
223 is( $fines->count, 2, "New fine added for second checkout" );
224 $fine = $fines->next;
225 is( $fine->amount, '80.000000', "First fine amount unchanged" );
226 my $fine2 = $fines->next;
227 is( $fine2->amount, '20.000000', "Second fine capped at '20' by MaxFine" );
228 is( $fine2->issue_id, $checkout2->issue_id, "Second fine is associated with the correct issue" );
229 is( $fine2->itemnumber, $checkout2->itemnumber, "Second fine is associated with the correct item" );
232 $fine->amountoutstanding('50')->store;
235 issue_id => $checkout2->issue_id,
236 itemnumber => $item2->itemnumber,
237 borrowernumber => $patron->borrowernumber,
239 due => $checkout2->date_due
243 $fines = Koha::Account::Lines->search(
244 { borrowernumber => $patron->borrowernumber },
245 { order_by => { '-asc' => 'accountlines_id' } }
247 is( $fines->count, 2, "Still two fines after second checkout update" );
248 $fine = $fines->next;
249 is( $fine->amount, '80.000000', "First fine amount unchanged" );
250 $fine2 = $fines->next;
251 is( $fine2->amount, '30.000000', "Second fine increased after partial payment of first" );
253 # Fix fine 1, create third fine
254 $fine->status('RETURNED')->store;
257 issue_id => $checkout1->issue_id,
258 itemnumber => $item1->itemnumber,
259 borrowernumber => $patron->borrowernumber,
261 due => $checkout1->date_due
265 $fines = Koha::Account::Lines->search(
266 { borrowernumber => $patron->borrowernumber },
267 { order_by => { '-asc' => 'accountlines_id' } }
269 is( $fines->count, 3, "Third fine added for overdue renewal" );
270 $fine = $fines->next;
271 is( $fine->amount, '80.000000', "First fine amount unchanged" );
272 $fine2 = $fines->next;
273 is( $fine2->amount, '30.000000', "Second fine amount unchanged" );
274 my $fine3 = $fines->next;
275 is( $fine3->amount, '20.000000', "Third fine amount capped due to MaxFine" );
276 is( $fine3->issue_id, $checkout1->issue_id, "Third fine is associated with the correct issue" );
277 is( $fine3->itemnumber, $checkout1->itemnumber, "Third fine is associated with the correct item" );
279 # FIXME: Add test to check whether sundry/manual charges are included within MaxFine.
280 # FIXME: Add test to ensure other charges are not included within MaxFine.
283 t::lib::Mocks::mock_preference( 'MaxFine', '0' );
286 issue_id => $checkout1->issue_id,
287 itemnumber => $item1->itemnumber,
288 borrowernumber => $patron->borrowernumber,
290 due => $checkout1->date_due
294 $fines = Koha::Account::Lines->search(
295 { borrowernumber => $patron->borrowernumber },
296 { order_by => { '-asc' => 'accountlines_id' } }
298 is( $fines->count, 3, "Still only three fines after MaxFine cap removed" );
299 $fine = $fines->next;
300 is( $fine->amount, '80.000000', "First fine amount unchanged" );
301 $fine2 = $fines->next;
302 is( $fine2->amount, '30.000000', "Second fine amount unchanged" );
303 $fine3 = $fines->next;
304 is( $fine3->amount, '30.000000', "Third fine increased now MaxFine cap is disabled" );
306 $schema->storage->txn_rollback;