Bug 8918: (follow-up) support creating brief UNIAMRC bibs in Reserves.t
[koha.git] / t / db_dependent / Circulation.t
1 #!/usr/bin/perl
2
3 use Modern::Perl;
4
5 use DateTime;
6 use C4::Biblio;
7 use C4::Branch;
8 use C4::Items;
9 use C4::Members;
10 use C4::Reserves;
11
12 use Test::More tests => 45;
13
14 BEGIN {
15     use_ok('C4::Circulation');
16 }
17
18 my $dbh = C4::Context->dbh;
19
20 # Start transaction
21 $dbh->{AutoCommit} = 0;
22 $dbh->{RaiseError} = 1;
23
24 # Start with a clean slate
25 $dbh->do('DELETE FROM issues');
26
27 my $CircControl = C4::Context->preference('CircControl');
28 my $HomeOrHoldingBranch = C4::Context->preference('HomeOrHoldingBranch');
29
30 my $item = {
31     homebranch => 'MPL',
32     holdingbranch => 'MPL'
33 };
34
35 my $borrower = {
36     branchcode => 'MPL'
37 };
38
39 # No userenv, PickupLibrary
40 C4::Context->set_preference('CircControl', 'PickupLibrary');
41 is(
42     C4::Context->preference('CircControl'),
43     'PickupLibrary',
44     'CircControl changed to PickupLibrary'
45 );
46 is(
47     C4::Circulation::_GetCircControlBranch($item, $borrower),
48     $item->{$HomeOrHoldingBranch},
49     '_GetCircControlBranch returned item branch (no userenv defined)'
50 );
51
52 # No userenv, PatronLibrary
53 C4::Context->set_preference('CircControl', 'PatronLibrary');
54 is(
55     C4::Context->preference('CircControl'),
56     'PatronLibrary',
57     'CircControl changed to PatronLibrary'
58 );
59 is(
60     C4::Circulation::_GetCircControlBranch($item, $borrower),
61     $borrower->{branchcode},
62     '_GetCircControlBranch returned borrower branch'
63 );
64
65 # No userenv, ItemHomeLibrary
66 C4::Context->set_preference('CircControl', 'ItemHomeLibrary');
67 is(
68     C4::Context->preference('CircControl'),
69     'ItemHomeLibrary',
70     'CircControl changed to ItemHomeLibrary'
71 );
72 is(
73     $item->{$HomeOrHoldingBranch},
74     C4::Circulation::_GetCircControlBranch($item, $borrower),
75     '_GetCircControlBranch returned item branch'
76 );
77
78 diag('Now, set a userenv');
79 C4::Context->_new_userenv('xxx');
80 C4::Context::set_userenv(0,0,0,'firstname','surname', 'MPL', 'Midway Public Library', '', '', '');
81 is(C4::Context->userenv->{branch}, 'MPL', 'userenv set');
82
83 # Userenv set, PickupLibrary
84 C4::Context->set_preference('CircControl', 'PickupLibrary');
85 is(
86     C4::Context->preference('CircControl'),
87     'PickupLibrary',
88     'CircControl changed to PickupLibrary'
89 );
90 is(
91     C4::Circulation::_GetCircControlBranch($item, $borrower),
92     'MPL',
93     '_GetCircControlBranch returned current branch'
94 );
95
96 # Userenv set, PatronLibrary
97 C4::Context->set_preference('CircControl', 'PatronLibrary');
98 is(
99     C4::Context->preference('CircControl'),
100     'PatronLibrary',
101     'CircControl changed to PatronLibrary'
102 );
103 is(
104     C4::Circulation::_GetCircControlBranch($item, $borrower),
105     $borrower->{branchcode},
106     '_GetCircControlBranch returned borrower branch'
107 );
108
109 # Userenv set, ItemHomeLibrary
110 C4::Context->set_preference('CircControl', 'ItemHomeLibrary');
111 is(
112     C4::Context->preference('CircControl'),
113     'ItemHomeLibrary',
114     'CircControl changed to ItemHomeLibrary'
115 );
116 is(
117     C4::Circulation::_GetCircControlBranch($item, $borrower),
118     $item->{$HomeOrHoldingBranch},
119     '_GetCircControlBranch returned item branch'
120 );
121
122 # Reset initial configuration
123 C4::Context->set_preference('CircControl', $CircControl);
124 is(
125     C4::Context->preference('CircControl'),
126     $CircControl,
127     'CircControl reset to its initial value'
128 );
129
130 # Set a simple circ policy
131 $dbh->do('DELETE FROM issuingrules');
132 $dbh->do(
133     q{INSERT INTO issuingrules (categorycode, branchcode, itemtype, reservesallowed,
134                                 maxissueqty, issuelength, lengthunit,
135                                 renewalsallowed, renewalperiod,
136                                 fine, chargeperiod)
137       VALUES (?, ?, ?, ?,
138               ?, ?, ?,
139               ?, ?,
140               ?, ?
141              )
142     },
143     {},
144     '*', '*', '*', 25,
145     20, 14, 'days',
146     1, 7,
147     .10, 1
148 );
149
150 # Test C4::Circulation::ProcessOfflinePayment
151 my $sth = C4::Context->dbh->prepare("SELECT COUNT(*) FROM accountlines WHERE amount = '-123.45' AND accounttype = 'Pay'");
152 $sth->execute();
153 my ( $original_count ) = $sth->fetchrow_array();
154
155 C4::Context->dbh->do("INSERT INTO borrowers ( cardnumber, surname, firstname, categorycode, branchcode ) VALUES ( '99999999999', 'Hall', 'Kyle', 'S', 'MPL' )");
156
157 C4::Circulation::ProcessOfflinePayment({ cardnumber => '99999999999', amount => '123.45' });
158
159 $sth->execute();
160 my ( $new_count ) = $sth->fetchrow_array();
161
162 ok( $new_count == $original_count  + 1, 'ProcessOfflinePayment makes payment correctly' );
163
164 C4::Context->dbh->do("DELETE FROM accountlines WHERE borrowernumber IN ( SELECT borrowernumber FROM borrowers WHERE cardnumber = '99999999999' )");
165 C4::Context->dbh->do("DELETE FROM borrowers WHERE cardnumber = '99999999999'");
166 C4::Context->dbh->do("DELETE FROM accountlines");
167 {
168 # CanBookBeRenewed tests
169
170     # Generate test biblio
171     my $biblio = MARC::Record->new();
172     my $title = 'Silence in the library';
173     $biblio->append_fields(
174         MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'),
175         MARC::Field->new('245', ' ', ' ', a => $title),
176     );
177
178     my ($biblionumber, $biblioitemnumber) = AddBiblio($biblio, '');
179
180     my $barcode = 'R00000342';
181     my $branch = 'MPL';
182
183     my ( $item_bibnum, $item_bibitemnum, $itemnumber ) = AddItem(
184         {
185             homebranch       => $branch,
186             holdingbranch    => $branch,
187             barcode          => $barcode,
188             replacementprice => 12.00
189         },
190         $biblionumber
191     );
192
193     my $barcode2 = 'R00000343';
194     my ( $item_bibnum2, $item_bibitemnum2, $itemnumber2 ) = AddItem(
195         {
196             homebranch       => $branch,
197             holdingbranch    => $branch,
198             barcode          => $barcode2,
199             replacementprice => 23.00
200         },
201         $biblionumber
202     );
203
204     my $barcode3 = 'R00000346';
205     my ( $item_bibnum3, $item_bibitemnum3, $itemnumber3 ) = AddItem(
206         {
207             homebranch       => $branch,
208             holdingbranch    => $branch,
209             barcode          => $barcode3,
210             replacementprice => 23.00
211         },
212         $biblionumber
213     );
214
215     # Create 2 borrowers
216     my %renewing_borrower_data = (
217         firstname =>  'John',
218         surname => 'Renewal',
219         categorycode => 'S',
220         branchcode => $branch,
221     );
222
223     my %reserving_borrower_data = (
224         firstname =>  'Katrin',
225         surname => 'Reservation',
226         categorycode => 'S',
227         branchcode => $branch,
228     );
229
230     my $renewing_borrowernumber = AddMember(%renewing_borrower_data);
231     my $reserving_borrowernumber = AddMember(%reserving_borrower_data);
232
233     my $renewing_borrower = GetMember( borrowernumber => $renewing_borrowernumber );
234
235     my $constraint     = 'a';
236     my $bibitems       = '';
237     my $priority       = '1';
238     my $resdate        = undef;
239     my $expdate        = undef;
240     my $notes          = '';
241     my $checkitem      = undef;
242     my $found          = undef;
243
244     my $datedue = AddIssue( $renewing_borrower, $barcode);
245     is (defined $datedue, 1, "Item 1 checked out, due date: $datedue");
246
247     my $datedue2 = AddIssue( $renewing_borrower, $barcode2);
248     is (defined $datedue2, 1, "Item 2 checked out, due date: $datedue2");
249
250     my $borrowing_borrowernumber = GetItemIssue($itemnumber)->{borrowernumber};
251     is ($borrowing_borrowernumber, $renewing_borrowernumber, "Item checked out to $renewing_borrower->{firstname} $renewing_borrower->{surname}");
252
253     my ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber, 1);
254     is( $renewokay, 1, 'Can renew, no holds for this title or item');
255
256
257     diag("Biblio-level hold, renewal test");
258     AddReserve(
259         $branch, $reserving_borrowernumber, $biblionumber,
260         $constraint, $bibitems,  $priority, $resdate, $expdate, $notes,
261         $title, $checkitem, $found
262     );
263
264     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber);
265     is( $renewokay, 0, '(Bug 10663) Cannot renew, reserved');
266     is( $error, 'on_reserve', '(Bug 10663) Cannot renew, reserved (returned error is on_reserve)');
267
268     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber2);
269     is( $renewokay, 0, '(Bug 10663) Cannot renew, reserved');
270     is( $error, 'on_reserve', '(Bug 10663) Cannot renew, reserved (returned error is on_reserve)');
271
272     my $reserveid = C4::Reserves::GetReserveId({ biblionumber => $biblionumber, borrowernumber => $reserving_borrowernumber});
273     my $reserving_borrower = GetMember( borrowernumber => $reserving_borrowernumber );
274     AddIssue($reserving_borrower, $barcode3);
275     my $reserve = $dbh->selectrow_hashref(
276         'SELECT * FROM old_reserves WHERE reserve_id = ?',
277         { Slice => {} },
278         $reserveid
279     );
280     is($reserve->{found}, 'F', 'hold marked completed when checking out item that fills it');
281
282     diag("Item-level hold, renewal test");
283     AddReserve(
284         $branch, $reserving_borrowernumber, $biblionumber,
285         $constraint, $bibitems,  $priority, $resdate, $expdate, $notes,
286         $title, $itemnumber, $found
287     );
288
289     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber, 1);
290     is( $renewokay, 0, '(Bug 10663) Cannot renew, item reserved');
291     is( $error, 'on_reserve', '(Bug 10663) Cannot renew, item reserved (returned error is on_reserve)');
292
293     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber2, 1);
294     is( $renewokay, 1, 'Can renew item 2, item-level hold is on item 1');
295
296
297     diag("Items can't fill hold for reasons");
298     ModItem({ notforloan => 1 }, $biblionumber, $itemnumber);
299     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber, 1);
300     is( $renewokay, 1, 'Can renew, item is marked not for loan, hold does not block');
301     ModItem({ notforloan => 0, itype => '' }, $biblionumber, $itemnumber,1);
302
303     # FIXME: Add more for itemtype not for loan etc.
304
305     $reserveid = C4::Reserves::GetReserveId({ biblionumber => $biblionumber, itemnumber => $itemnumber, borrowernumber => $reserving_borrowernumber});
306     CancelReserve({ reserve_id => $reserveid });
307
308     diag("Too many renewals");
309
310     # set policy to forbid renewals
311     $dbh->do('UPDATE issuingrules SET renewalsallowed = 0');
312
313     ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber);
314     is( $renewokay, 0, 'Cannot renew, 0 renewals allowed');
315     is( $error, 'too_many', 'Cannot renew, 0 renewals allowed (returned code is too_many)');
316
317     # Test WhenLostForgiveFine and WhenLostChargeReplacementFee
318     diag("WhenLostForgiveFine and WhenLostChargeReplacementFee");
319     C4::Context->set_preference('WhenLostForgiveFine','1');
320     C4::Context->set_preference('WhenLostChargeReplacementFee','1');
321
322     C4::Overdues::UpdateFine( $itemnumber, $renewing_borrower->{borrowernumber},
323         15.00, q{}, Koha::DateUtils::output_pref($datedue) );
324
325     LostItem( $itemnumber, 1 );
326
327     my $total_due = $dbh->selectrow_array(
328         'SELECT SUM( amountoutstanding ) FROM accountlines WHERE borrowernumber = ?',
329         undef, $renewing_borrower->{borrowernumber}
330     );
331
332     ok( $total_due == 12, 'Borrower only charged replacement fee with both WhenLostForgiveFine and WhenLostChargeReplacementFee enabled' );
333
334     C4::Context->dbh->do("DELETE FROM accountlines");
335
336     C4::Context->set_preference('WhenLostForgiveFine','0');
337     C4::Context->set_preference('WhenLostChargeReplacementFee','0');
338
339     C4::Overdues::UpdateFine( $itemnumber2, $renewing_borrower->{borrowernumber},
340         15.00, q{}, Koha::DateUtils::output_pref($datedue) );
341
342     LostItem( $itemnumber2, 1 );
343
344     $total_due = $dbh->selectrow_array(
345         'SELECT SUM( amountoutstanding ) FROM accountlines WHERE borrowernumber = ?',
346         undef, $renewing_borrower->{borrowernumber}
347     );
348
349     ok( $total_due == 15, 'Borrower only charged fine with both WhenLostForgiveFine and WhenLostChargeReplacementFee disabled' );
350 }
351
352 {
353     # GetUpcomingDueIssues tests
354     my $barcode  = 'R00000342';
355     my $barcode2 = 'R00000343';
356     my $barcode3 = 'R00000344';
357     my $branch   = 'MPL';
358
359     #Create another record
360     my $biblio2 = MARC::Record->new();
361     my $title2 = 'Something is worng here';
362     $biblio2->append_fields(
363         MARC::Field->new('100', ' ', ' ', a => 'Anonymous'),
364         MARC::Field->new('245', ' ', ' ', a => $title2),
365     );
366     my ($biblionumber2, $biblioitemnumber2) = AddBiblio($biblio2, '');
367
368     #Create third item
369     AddItem(
370         {
371             homebranch       => $branch,
372             holdingbranch    => $branch,
373             barcode          => $barcode3
374         },
375         $biblionumber2
376     );
377
378     # Create a borrower
379     my %a_borrower_data = (
380         firstname =>  'Fridolyn',
381         surname => 'SOMERS',
382         categorycode => 'S',
383         branchcode => $branch,
384     );
385
386     my $a_borrower_borrowernumber = AddMember(%a_borrower_data);
387     my $a_borrower = GetMember( borrowernumber => $a_borrower_borrowernumber );
388
389     my $yesterday = DateTime->today(time_zone => C4::Context->tz())->add( days => -1 );
390     my $two_days_ahead = DateTime->today(time_zone => C4::Context->tz())->add( days => 2 );
391     my $today = DateTime->today(time_zone => C4::Context->tz());
392
393     my $datedue  = AddIssue( $a_borrower, $barcode, $yesterday );
394     my $datedue2 = AddIssue( $a_borrower, $barcode2, $two_days_ahead );
395
396     my $upcoming_dues;
397
398     diag( "GetUpcomingDueIssues tests" );
399
400     for my $i(0..1) {
401         $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => $i } );
402         is ( scalar( @$upcoming_dues ), 0, "No items due in less than one day ($i days in advance)" );
403     }
404
405     #days_in_advance needs to be inclusive, so 1 matches items due tomorrow, 0 items due today etc.
406     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 2 } );
407     is ( scalar ( @$upcoming_dues), 1, "Only one item due in 2 days or less" );
408
409     for my $i(3..5) {
410         $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => $i } );
411         is ( scalar( @$upcoming_dues ), 1,
412             "Bug 9362: Only one item due in more than 2 days ($i days in advance)" );
413     }
414
415     # Bug 11218 - Due notices not generated - GetUpcomingDueIssues needs to select due today items as well
416
417     my $datedue3 = AddIssue( $a_borrower, $barcode3, $today );
418
419     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => -1 } );
420     is ( scalar ( @$upcoming_dues), 0, "Overdues can not be selected" );
421
422     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 0 } );
423     is ( scalar ( @$upcoming_dues), 1, "1 item is due today" );
424
425     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 1 } );
426     is ( scalar ( @$upcoming_dues), 1, "1 item is due today, none tomorrow" );
427
428     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 2 }  );
429     is ( scalar ( @$upcoming_dues), 2, "2 items are due withing 2 days" );
430
431     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 3 } );
432     is ( scalar ( @$upcoming_dues), 2, "2 items are due withing 2 days" );
433
434     $upcoming_dues = C4::Circulation::GetUpcomingDueIssues();
435     is ( scalar ( @$upcoming_dues), 2, "days_in_advance is 7 in GetUpcomingDueIssues if not provided" );
436
437 }
438
439 $dbh->rollback;