e01592707437795c9887a8d29655523d37625648
[koha.git] / t / db_dependent / Koha / Charges / Fees.t
1 #!/usr/bin/perl
2 #
3 # Copyright 2018 ByWater Solutions
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, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 use Modern::Perl;
21
22 use Test::More tests => 8;
23 use Test::Exception;
24 use Test::Warn;
25
26 use t::lib::Mocks;
27 use t::lib::TestBuilder;
28
29 use Time::Fake;
30 use C4::Calendar;
31 use Koha::DateUtils qw(dt_from_string);
32
33 BEGIN {
34     use_ok('Koha::Charges::Fees');
35 }
36
37 my $schema  = Koha::Database->new->schema;
38 my $builder = t::lib::TestBuilder->new();
39 $schema->storage->txn_begin;
40
41 my $patron_category = $builder->build_object(
42     {
43         class => 'Koha::Patron::Categories',
44         value => {
45             category_type => 'P',
46             enrolmentfee  => 0,
47         }
48     }
49 );
50 my $library = $builder->build_object(
51     {
52         class => 'Koha::Libraries',
53     }
54 );
55 my $biblio = $builder->build_object(
56     {
57         class => 'Koha::Biblios',
58     }
59 );
60 my $itemtype = $builder->build_object(
61     {
62         class => 'Koha::ItemTypes',
63         value => {
64             rentalcharge_daily  => '0.00',
65             rentalcharge_hourly => '0.00',
66             rentalcharge        => '0.00',
67             processfee          => '0.00',
68             defaultreplacecost  => '0.00',
69         },
70     }
71 );
72 my $item = $builder->build_object(
73     {
74         class => 'Koha::Items',
75         value => {
76             biblionumber  => $biblio->id,
77             homebranch    => $library->id,
78             holdingbranch => $library->id,
79             itype         => $itemtype->id,
80         }
81     }
82 );
83 my $patron = $builder->build_object(
84     {
85         class => 'Koha::Patrons',
86         value => {
87             dateexpiry   => '9999-12-31',
88             categorycode => $patron_category->id,
89         }
90     }
91 );
92
93 my $now = dt_from_string()->set_time_zone('floating');
94 Time::Fake->offset( $now->epoch );
95
96 my $dt_from = $now->clone->subtract( days => 2 );
97 my $dt_to = $now->clone->add( days => 4 );
98
99 subtest 'new' => sub {
100     plan tests => 9;
101
102     # Mandatory parameters missing
103     throws_ok {
104         Koha::Charges::Fees->new(
105             {
106                 library => $library,
107                 item    => $item,
108                 to_date => $dt_to,
109             }
110           )
111     }
112     'Koha::Exceptions::MissingParameter', 'MissingParameter thrown for patron';
113     throws_ok {
114         Koha::Charges::Fees->new(
115             {
116                 patron  => $patron,
117                 item    => $item,
118                 to_date => $dt_to,
119             }
120           )
121     }
122     'Koha::Exceptions::MissingParameter', 'MissingParameter thrown for library';
123     throws_ok {
124         Koha::Charges::Fees->new(
125             {
126                 patron  => $patron,
127                 library => $library,
128                 to_date => $dt_to,
129             }
130           )
131     }
132     'Koha::Exceptions::MissingParameter', 'MissingParameter thrown for item';
133     throws_ok {
134         Koha::Charges::Fees->new(
135             {
136                 patron  => $patron,
137                 library => $library,
138                 item    => $item,
139             }
140           )
141     }
142     'Koha::Exceptions::MissingParameter', 'MissingParameter thrown for to_date';
143
144     # Mandatory parameter bad
145     dies_ok {
146         Koha::Charges::Fees->new(
147             {
148                 patron  => '12345',
149                 library => $library,
150                 item    => $item,
151                 to_date => $dt_to,
152             }
153           )
154     }
155     'dies for bad patron';
156     dies_ok {
157         Koha::Charges::Fees->new(
158             {
159                 patron  => $patron,
160                 library => '12345',
161                 item    => $item,
162                 to_date => $dt_to,
163             }
164           )
165     }
166     'dies for bad library';
167     dies_ok {
168         Koha::Charges::Fees->new(
169             {
170                 patron  => $patron,
171                 library => $library,
172                 item    => '12345',
173                 to_date => $dt_to,
174             }
175           )
176     }
177     'dies for bad item';
178     dies_ok {
179         Koha::Charges::Fees->new(
180             {
181                 patron  => $patron,
182                 library => $library,
183                 item    => $item,
184                 to_date => 12345
185             }
186           )
187     }
188     'dies for bad to_date';
189
190     # Defaults
191     my $fees = Koha::Charges::Fees->new(
192         {
193             patron  => $patron,
194             library => $library,
195             item    => $item,
196             to_date => $dt_to,
197         }
198     );
199     is( $fees->from_date, dt_from_string(),
200         'from_date default set correctly to today' );
201 };
202
203 subtest 'patron accessor' => sub {
204     plan tests => 2;
205
206     my $fees = Koha::Charges::Fees->new(
207         {
208             patron  => $patron,
209             library => $library,
210             item    => $item,
211             to_date => $dt_to,
212         }
213     );
214
215     ok(
216         $fees->patron->isa('Koha::Patron'),
217         'patron accessor returns a Koha::Patron'
218     );
219     warning_is { $fees->patron('12345') }
220     { carped =>
221 "Setting 'patron' to something other than a Koha::Patron is not supported!"
222     }, "Warning thrown when attempting to set patron to string";
223
224 };
225
226 subtest 'library accessor' => sub {
227     plan tests => 2;
228
229     my $fees = Koha::Charges::Fees->new(
230         {
231             patron  => $patron,
232             library => $library,
233             item    => $item,
234             to_date => $dt_to,
235         }
236     );
237
238     ok(
239         $fees->library->isa('Koha::Library'),
240         'library accessor returns a Koha::Library'
241     );
242     warning_is { $fees->library('12345') }
243     { carped =>
244 "Setting 'library' to something other than a Koha::Library is not supported!"
245     }, "Warning thrown when attempting to set library to string";
246 };
247
248 subtest 'item accessor' => sub {
249     plan tests => 2;
250
251     my $fees = Koha::Charges::Fees->new(
252         {
253             patron  => $patron,
254             library => $library,
255             item    => $item,
256             to_date => $dt_to,
257         }
258     );
259
260     ok( $fees->item->isa('Koha::Item'), 'item accessor returns a Koha::Item' );
261     warning_is { $fees->item('12345') }
262     { carped =>
263 "Setting 'item' to something other than a Koha::Item is not supported!"
264     }, "Warning thrown when attempting to set item to string";
265 };
266
267 subtest 'to_date accessor' => sub {
268     plan tests => 2;
269
270     my $fees = Koha::Charges::Fees->new(
271         {
272             patron  => $patron,
273             library => $library,
274             item    => $item,
275             to_date => $dt_to,
276         }
277     );
278
279     ok( $fees->to_date->isa('DateTime'),
280         'to_date accessor returns a DateTime' );
281     warning_is { $fees->to_date(12345) }
282     { carped =>
283 "Setting 'to_date' to something other than a DateTime is not supported!"
284     }, "Warning thrown when attempting to set to_date to integer";
285 };
286
287 subtest 'from_date accessor' => sub {
288     plan tests => 2;
289
290     my $fees = Koha::Charges::Fees->new(
291         {
292             patron  => $patron,
293             library => $library,
294             item    => $item,
295             to_date => $dt_to,
296         }
297     );
298
299     ok(
300         $fees->from_date->isa('DateTime'),
301         'from_date accessor returns a DateTime'
302     );
303     warning_is { $fees->from_date(12345) }
304     { carped =>
305 "Setting 'from_date' to something other than a DateTime is not supported!"
306     }, "Warning thrown when attempting to set from_date to integer";
307 };
308
309 subtest 'accumulate_rentalcharge tests' => sub {
310     plan tests => 7;
311
312     my $fees = Koha::Charges::Fees->new(
313         {
314             patron    => $patron,
315             library   => $library,
316             item      => $item,
317             to_date   => $dt_to,
318             from_date => $dt_from,
319         }
320     );
321
322     # Daily tests
323     Koha::CirculationRules->set_rules({
324             categorycode => $patron->categorycode,
325             itemtype     => $itemtype->id,
326             branchcode   => $library->id,
327             rules => {
328                 lengthunit => 'days',
329             }
330         }
331     );
332
333     $itemtype->rentalcharge_daily(1.00);
334     $itemtype->store();
335     is( $itemtype->rentalcharge_daily,
336         1.00, 'Daily return charge stored correctly' );
337
338     t::lib::Mocks::mock_preference( 'finesCalendar', 'ignoreCalendar' );
339     my $charge = $fees->accumulate_rentalcharge();
340     is( $charge, 6.00,
341 'Daily rental charge calculated correctly with finesCalendar = ignoreCalendar'
342     );
343
344     t::lib::Mocks::mock_preference( 'finesCalendar', 'noFinesWhenClosed' );
345     $charge = $fees->accumulate_rentalcharge();
346     is( $charge, 6.00,
347 'Daily rental charge calculated correctly with finesCalendar = noFinesWhenClosed'
348     );
349
350     my $calendar = C4::Calendar->new( branchcode => $library->id );
351     # DateTime 1..7 (Mon..Sun), C4::Calender 0..6 (Sun..Sat)
352     my $closed_day =
353         ( $dt_from->day_of_week == 6 ) ? 0
354       : ( $dt_from->day_of_week == 7 ) ? 1
355       :                                  $dt_from->day_of_week + 1;
356     $calendar->insert_week_day_holiday(
357         weekday     => $closed_day,
358         title       => 'Test holiday',
359         description => 'Test holiday'
360     );
361     $charge = $fees->accumulate_rentalcharge();
362     my $day_names = {
363         0 => 'Sunday',
364         1 => 'Monday',
365         2 => 'Tuesday',
366         3 => 'Wednesday',
367         4 => 'Thursday',
368         5 => 'Friday',
369         6 => 'Saturday'
370     };
371     my $dayname = $day_names->{$closed_day};
372     is( $charge, 5.00,
373 "Daily rental charge calculated correctly with finesCalendar = noFinesWhenClosed and closed $dayname"
374     );
375
376     # Hourly tests
377     Koha::CirculationRules->set_rules({
378             categorycode => $patron->categorycode,
379             itemtype     => $itemtype->id,
380             branchcode   => $library->id,
381             rules => {
382                 lengthunit => 'hours',
383             }
384         }
385
386     );
387
388     $itemtype->rentalcharge_hourly("0.25");
389     $itemtype->store();
390
391     $dt_to   = $dt_from->clone->add( hours => 96 );
392     $fees    = Koha::Charges::Fees->new(
393         {
394             patron    => $patron,
395             library   => $library,
396             item      => $item,
397             to_date   => $dt_to,
398             from_date => $dt_from,
399         }
400     );
401
402     t::lib::Mocks::mock_preference( 'finesCalendar', 'ignoreCalendar' );
403     $charge = $fees->accumulate_rentalcharge();
404     is( $charge, 24.00, 'Hourly rental charge calculated correctly (96h * 0.25u)' );
405
406     t::lib::Mocks::mock_preference( 'finesCalendar', 'noFinesWhenClosed' );
407     $charge = $fees->accumulate_rentalcharge();
408     is( $charge, 18.00,
409 "Hourly rental charge calculated correctly with finesCalendar = noFinesWhenClosed and closed $dayname (96h - 24h * 0.25u)"
410     );
411
412     $calendar->delete_holiday( weekday => $closed_day );
413     $charge = $fees->accumulate_rentalcharge();
414     is( $charge, 24.00, 'Hourly rental charge calculated correctly with finesCalendar = noFinesWhenClosed (96h - 0h * 0.25u)' );
415 };
416
417 $schema->storage->txn_rollback;
418 Time::Fake->reset;