Bug 9088: if there is only one active order, pre-select it when creating new orderings
[koha.git] / t / db_dependent / Circulation.t
blob6b2520809ba1d28d630497a0b23ed7e0b00842b7
1 #!/usr/bin/perl
3 use Modern::Perl;
5 use DateTime;
6 use C4::Biblio;
7 use C4::Branch;
8 use C4::Items;
9 use C4::Members;
10 use C4::Reserves;
12 use Test::More tests => 48;
14 BEGIN {
15 use_ok('C4::Circulation');
18 my $dbh = C4::Context->dbh;
20 # Start transaction
21 $dbh->{AutoCommit} = 0;
22 $dbh->{RaiseError} = 1;
24 # Start with a clean slate
25 $dbh->do('DELETE FROM issues');
27 my $CircControl = C4::Context->preference('CircControl');
28 my $HomeOrHoldingBranch = C4::Context->preference('HomeOrHoldingBranch');
30 my $item = {
31 homebranch => 'MPL',
32 holdingbranch => 'MPL'
35 my $borrower = {
36 branchcode => 'MPL'
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'
46 is(
47 C4::Circulation::_GetCircControlBranch($item, $borrower),
48 $item->{$HomeOrHoldingBranch},
49 '_GetCircControlBranch returned item branch (no userenv defined)'
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'
59 is(
60 C4::Circulation::_GetCircControlBranch($item, $borrower),
61 $borrower->{branchcode},
62 '_GetCircControlBranch returned borrower branch'
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'
72 is(
73 $item->{$HomeOrHoldingBranch},
74 C4::Circulation::_GetCircControlBranch($item, $borrower),
75 '_GetCircControlBranch returned item branch'
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');
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'
90 is(
91 C4::Circulation::_GetCircControlBranch($item, $borrower),
92 'MPL',
93 '_GetCircControlBranch returned current branch'
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'
104 C4::Circulation::_GetCircControlBranch($item, $borrower),
105 $borrower->{branchcode},
106 '_GetCircControlBranch returned borrower branch'
109 # Userenv set, ItemHomeLibrary
110 C4::Context->set_preference('CircControl', 'ItemHomeLibrary');
112 C4::Context->preference('CircControl'),
113 'ItemHomeLibrary',
114 'CircControl changed to ItemHomeLibrary'
117 C4::Circulation::_GetCircControlBranch($item, $borrower),
118 $item->{$HomeOrHoldingBranch},
119 '_GetCircControlBranch returned item branch'
122 # Reset initial configuration
123 C4::Context->set_preference('CircControl', $CircControl);
125 C4::Context->preference('CircControl'),
126 $CircControl,
127 'CircControl reset to its initial value'
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 ?, ?
144 '*', '*', '*', 25,
145 20, 14, 'days',
146 1, 7,
147 .10, 1
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();
155 C4::Context->dbh->do("INSERT INTO borrowers ( cardnumber, surname, firstname, categorycode, branchcode ) VALUES ( '99999999999', 'Hall', 'Kyle', 'S', 'MPL' )");
157 C4::Circulation::ProcessOfflinePayment({ cardnumber => '99999999999', amount => '123.45' });
159 $sth->execute();
160 my ( $new_count ) = $sth->fetchrow_array();
162 ok( $new_count == $original_count + 1, 'ProcessOfflinePayment makes payment correctly' );
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");
168 # CanBookBeRenewed tests
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),
178 my ($biblionumber, $biblioitemnumber) = AddBiblio($biblio, '');
180 my $barcode = 'R00000342';
181 my $branch = 'MPL';
183 my ( $item_bibnum, $item_bibitemnum, $itemnumber ) = AddItem(
185 homebranch => $branch,
186 holdingbranch => $branch,
187 barcode => $barcode,
188 replacementprice => 12.00
190 $biblionumber
193 my $barcode2 = 'R00000343';
194 my ( $item_bibnum2, $item_bibitemnum2, $itemnumber2 ) = AddItem(
196 homebranch => $branch,
197 holdingbranch => $branch,
198 barcode => $barcode2,
199 replacementprice => 23.00
201 $biblionumber
204 my $barcode3 = 'R00000346';
205 my ( $item_bibnum3, $item_bibitemnum3, $itemnumber3 ) = AddItem(
207 homebranch => $branch,
208 holdingbranch => $branch,
209 barcode => $barcode3,
210 replacementprice => 23.00
212 $biblionumber
215 # Create 2 borrowers
216 my %renewing_borrower_data = (
217 firstname => 'John',
218 surname => 'Renewal',
219 categorycode => 'S',
220 branchcode => $branch,
223 my %reserving_borrower_data = (
224 firstname => 'Katrin',
225 surname => 'Reservation',
226 categorycode => 'S',
227 branchcode => $branch,
230 my $renewing_borrowernumber = AddMember(%renewing_borrower_data);
231 my $reserving_borrowernumber = AddMember(%reserving_borrower_data);
233 my $renewing_borrower = GetMember( borrowernumber => $renewing_borrowernumber );
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;
244 my $datedue = AddIssue( $renewing_borrower, $barcode);
245 is (defined $datedue, 1, "Item 1 checked out, due date: $datedue");
247 my $datedue2 = AddIssue( $renewing_borrower, $barcode2);
248 is (defined $datedue2, 1, "Item 2 checked out, due date: $datedue2");
250 my $borrowing_borrowernumber = GetItemIssue($itemnumber)->{borrowernumber};
251 is ($borrowing_borrowernumber, $renewing_borrowernumber, "Item checked out to $renewing_borrower->{firstname} $renewing_borrower->{surname}");
253 my ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber, 1);
254 is( $renewokay, 1, 'Can renew, no holds for this title or item');
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
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)');
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)');
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
280 is($reserve->{found}, 'F', 'hold marked completed when checking out item that fills it');
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
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)');
293 ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber2, 1);
294 is( $renewokay, 1, 'Can renew item 2, item-level hold is on item 1');
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);
303 # FIXME: Add more for itemtype not for loan etc.
305 $reserveid = C4::Reserves::GetReserveId({ biblionumber => $biblionumber, itemnumber => $itemnumber, borrowernumber => $reserving_borrowernumber});
306 CancelReserve({ reserve_id => $reserveid });
308 # set policy to require that loans cannot be
309 # renewed until seven days prior to the due date
310 $dbh->do('UPDATE issuingrules SET norenewalbefore = 7');
311 ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber);
312 is( $renewokay, 0, 'Cannot renew, renewal is premature');
313 is( $error, 'too_soon', 'Cannot renew, renewal is premature (returned code is too_soon)');
315 GetSoonestRenewDate($renewing_borrowernumber, $itemnumber),
316 $datedue->clone->add(days => -7),
317 'renewals permitted 7 days before due date, as expected',
320 diag("Too many renewals");
322 # set policy to forbid renewals
323 $dbh->do('UPDATE issuingrules SET norenewalbefore = NULL, renewalsallowed = 0');
325 ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber);
326 is( $renewokay, 0, 'Cannot renew, 0 renewals allowed');
327 is( $error, 'too_many', 'Cannot renew, 0 renewals allowed (returned code is too_many)');
329 # Test WhenLostForgiveFine and WhenLostChargeReplacementFee
330 diag("WhenLostForgiveFine and WhenLostChargeReplacementFee");
331 C4::Context->set_preference('WhenLostForgiveFine','1');
332 C4::Context->set_preference('WhenLostChargeReplacementFee','1');
334 C4::Overdues::UpdateFine( $itemnumber, $renewing_borrower->{borrowernumber},
335 15.00, q{}, Koha::DateUtils::output_pref($datedue) );
337 LostItem( $itemnumber, 1 );
339 my $total_due = $dbh->selectrow_array(
340 'SELECT SUM( amountoutstanding ) FROM accountlines WHERE borrowernumber = ?',
341 undef, $renewing_borrower->{borrowernumber}
344 ok( $total_due == 12, 'Borrower only charged replacement fee with both WhenLostForgiveFine and WhenLostChargeReplacementFee enabled' );
346 C4::Context->dbh->do("DELETE FROM accountlines");
348 C4::Context->set_preference('WhenLostForgiveFine','0');
349 C4::Context->set_preference('WhenLostChargeReplacementFee','0');
351 C4::Overdues::UpdateFine( $itemnumber2, $renewing_borrower->{borrowernumber},
352 15.00, q{}, Koha::DateUtils::output_pref($datedue) );
354 LostItem( $itemnumber2, 1 );
356 $total_due = $dbh->selectrow_array(
357 'SELECT SUM( amountoutstanding ) FROM accountlines WHERE borrowernumber = ?',
358 undef, $renewing_borrower->{borrowernumber}
361 ok( $total_due == 15, 'Borrower only charged fine with both WhenLostForgiveFine and WhenLostChargeReplacementFee disabled' );
365 # GetUpcomingDueIssues tests
366 my $barcode = 'R00000342';
367 my $barcode2 = 'R00000343';
368 my $barcode3 = 'R00000344';
369 my $branch = 'MPL';
371 #Create another record
372 my $biblio2 = MARC::Record->new();
373 my $title2 = 'Something is worng here';
374 $biblio2->append_fields(
375 MARC::Field->new('100', ' ', ' ', a => 'Anonymous'),
376 MARC::Field->new('245', ' ', ' ', a => $title2),
378 my ($biblionumber2, $biblioitemnumber2) = AddBiblio($biblio2, '');
380 #Create third item
381 AddItem(
383 homebranch => $branch,
384 holdingbranch => $branch,
385 barcode => $barcode3
387 $biblionumber2
390 # Create a borrower
391 my %a_borrower_data = (
392 firstname => 'Fridolyn',
393 surname => 'SOMERS',
394 categorycode => 'S',
395 branchcode => $branch,
398 my $a_borrower_borrowernumber = AddMember(%a_borrower_data);
399 my $a_borrower = GetMember( borrowernumber => $a_borrower_borrowernumber );
401 my $yesterday = DateTime->today(time_zone => C4::Context->tz())->add( days => -1 );
402 my $two_days_ahead = DateTime->today(time_zone => C4::Context->tz())->add( days => 2 );
403 my $today = DateTime->today(time_zone => C4::Context->tz());
405 my $datedue = AddIssue( $a_borrower, $barcode, $yesterday );
406 my $datedue2 = AddIssue( $a_borrower, $barcode2, $two_days_ahead );
408 my $upcoming_dues;
410 diag( "GetUpcomingDueIssues tests" );
412 for my $i(0..1) {
413 $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => $i } );
414 is ( scalar( @$upcoming_dues ), 0, "No items due in less than one day ($i days in advance)" );
417 #days_in_advance needs to be inclusive, so 1 matches items due tomorrow, 0 items due today etc.
418 $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 2 } );
419 is ( scalar ( @$upcoming_dues), 1, "Only one item due in 2 days or less" );
421 for my $i(3..5) {
422 $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => $i } );
423 is ( scalar( @$upcoming_dues ), 1,
424 "Bug 9362: Only one item due in more than 2 days ($i days in advance)" );
427 # Bug 11218 - Due notices not generated - GetUpcomingDueIssues needs to select due today items as well
429 my $datedue3 = AddIssue( $a_borrower, $barcode3, $today );
431 $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => -1 } );
432 is ( scalar ( @$upcoming_dues), 0, "Overdues can not be selected" );
434 $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 0 } );
435 is ( scalar ( @$upcoming_dues), 1, "1 item is due today" );
437 $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 1 } );
438 is ( scalar ( @$upcoming_dues), 1, "1 item is due today, none tomorrow" );
440 $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 2 } );
441 is ( scalar ( @$upcoming_dues), 2, "2 items are due withing 2 days" );
443 $upcoming_dues = C4::Circulation::GetUpcomingDueIssues( { days_in_advance => 3 } );
444 is ( scalar ( @$upcoming_dues), 2, "2 items are due withing 2 days" );
446 $upcoming_dues = C4::Circulation::GetUpcomingDueIssues();
447 is ( scalar ( @$upcoming_dues), 2, "days_in_advance is 7 in GetUpcomingDueIssues if not provided" );
451 $dbh->rollback;