Bug 28238: Add tests
[koha.git] / t / db_dependent / ILSDI_Services.t
1 #!/usr/bin/perl
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18 use Modern::Perl;
19
20 use CGI qw ( -utf8 );
21
22 use Test::More tests => 11;
23 use Test::MockModule;
24 use t::lib::Mocks;
25 use t::lib::TestBuilder;
26 use t::lib::Dates;
27 use XML::LibXML;
28
29 use C4::Items qw( ModItemTransfer );
30 use C4::Circulation qw( AddIssue );
31
32 use Koha::AuthUtils;
33 use Koha::DateUtils qw( dt_from_string );
34
35 BEGIN {
36     use_ok('C4::ILSDI::Services', qw( AuthenticatePatron GetPatronInfo LookupPatron HoldTitle HoldItem GetRecords RenewLoan GetAvailability ));
37 }
38
39 my $schema  = Koha::Database->schema;
40 my $dbh     = C4::Context->dbh;
41 my $builder = t::lib::TestBuilder->new;
42
43 subtest 'AuthenticatePatron test' => sub {
44
45     plan tests => 16;
46
47     $schema->storage->txn_begin;
48
49     my $plain_password = 'tomasito';
50
51     $builder->build({
52         source => 'Borrower',
53         value => {
54             cardnumber => undef,
55         }
56     });
57
58     my $borrower = $builder->build({
59         source => 'Borrower',
60         value  => {
61             cardnumber => undef,
62             password => Koha::AuthUtils::hash_password( $plain_password ),
63             lastseen => "2001-01-01 12:34:56"
64         }
65     });
66
67     my $query = CGI->new;
68     $query->param( 'username', $borrower->{userid});
69     $query->param( 'password', $plain_password);
70
71     t::lib::Mocks::mock_preference( 'TrackLastPatronActivity', '' );
72     my $reply = C4::ILSDI::Services::AuthenticatePatron( $query );
73     is( $reply->{id}, $borrower->{borrowernumber}, "userid and password - Patron authenticated" );
74     is( $reply->{code}, undef, "Error code undef");
75     my $seen_patron = Koha::Patrons->find({ borrowernumber => $reply->{id} });
76     is( $seen_patron->lastseen(), '2001-01-01 12:34:56','Last seen not updated if not tracking patrons');
77
78     $query->param('password','ilsdi-passworD');
79     $reply = C4::ILSDI::Services::AuthenticatePatron( $query );
80     is( $reply->{code}, 'PatronNotFound', "userid and wrong password - PatronNotFound" );
81     is( $reply->{id}, undef, "id undef");
82
83     $query->param( 'password', $plain_password );
84     $query->param( 'username', 'wrong-ilsdi-useriD' );
85     $reply = C4::ILSDI::Services::AuthenticatePatron( $query );
86     is( $reply->{code}, 'PatronNotFound', "non-existing userid - PatronNotFound" );
87     is( $reply->{id}, undef, "id undef");
88
89     t::lib::Mocks::mock_preference( 'TrackLastPatronActivity', '1' );
90     $query->param( 'username', uc( $borrower->{userid} ));
91     $reply = C4::ILSDI::Services::AuthenticatePatron( $query );
92     my $now = dt_from_string;
93     is( $reply->{id}, $borrower->{borrowernumber}, "userid is not case sensitive - Patron authenticated" );
94     is( $reply->{code}, undef, "Error code undef");
95     $seen_patron = Koha::Patrons->find({ borrowernumber => $reply->{id} });
96     is( t::lib::Dates::compare( $seen_patron->lastseen, $now), 0, 'Last seen updated to today if tracking patrons' );
97
98     $query->param( 'username', $borrower->{cardnumber} );
99     $reply = C4::ILSDI::Services::AuthenticatePatron( $query );
100     is( $reply->{id}, $borrower->{borrowernumber}, "cardnumber and password - Patron authenticated" );
101     is( $reply->{code}, undef, "Error code undef" );
102
103     $query->param( 'password', 'ilsdi-passworD' );
104     $reply = C4::ILSDI::Services::AuthenticatePatron( $query );
105     is( $reply->{code}, 'PatronNotFound', "cardnumber and wrong password - PatronNotFount" );
106     is( $reply->{id}, undef, "id undef" );
107
108     $query->param( 'username', 'randomcardnumber1234' );
109     $query->param( 'password', $plain_password );
110     $reply = C4::ILSDI::Services::AuthenticatePatron($query);
111     is( $reply->{code}, 'PatronNotFound', "non-existing cardnumer/userid - PatronNotFound" );
112     is( $reply->{id}, undef, "id undef");
113
114     $schema->storage->txn_rollback;
115 };
116
117
118 subtest 'GetPatronInfo/GetBorrowerAttributes test for extended patron attributes' => sub {
119
120     plan tests => 5;
121
122     $schema->storage->txn_begin;
123
124     $schema->resultset( 'Issue' )->delete_all;
125     $schema->resultset( 'Borrower' )->delete_all;
126     $schema->resultset( 'BorrowerAttribute' )->delete_all;
127     $schema->resultset( 'BorrowerAttributeType' )->delete_all;
128     $schema->resultset( 'Category' )->delete_all;
129     $schema->resultset( 'Item' )->delete_all; # 'Branch' deps. on this
130     $schema->resultset( 'Club' )->delete_all;
131     $schema->resultset( 'Branch' )->delete_all;
132
133     # Configure Koha to enable ILS-DI server and extended attributes:
134     t::lib::Mocks::mock_preference( 'ILS-DI', 1 );
135     t::lib::Mocks::mock_preference( 'ExtendedPatronAttributes', 1 );
136
137     # Set up a library/branch for our user to belong to:
138     my $lib = $builder->build( {
139         source => 'Branch',
140         value => {
141             branchcode => 'T_ILSDI',
142         }
143     } );
144
145     # Create a new category for user to belong to:
146     my $cat = $builder->build( {
147         source => 'Category',
148         value  => {
149             category_type                 => 'A',
150             BlockExpiredPatronOpacActions => -1,
151         }
152     } );
153
154     # Create a new attribute type:
155     my $attr_type = $builder->build( {
156         source => 'BorrowerAttributeType',
157         value  => {
158             code                      => 'HIDEME',
159             opac_display              => 0,
160             authorised_value_category => '',
161             class                     => '',
162         }
163     } );
164     my $attr_type_visible = $builder->build( {
165         source => 'BorrowerAttributeType',
166         value  => {
167             code                      => 'SHOWME',
168             opac_display              => 1,
169             authorised_value_category => '',
170             class                     => '',
171         }
172     } );
173
174     # Create a new user:
175     my $brwr = $builder->build( {
176         source => 'Borrower',
177         value  => {
178             categorycode => $cat->{'categorycode'},
179             branchcode   => $lib->{'branchcode'},
180         }
181     } );
182
183     # Authorised value:
184     my $auth = $builder->build( {
185         source => 'AuthorisedValue',
186         value  => {
187             category => $cat->{'categorycode'}
188         }
189     } );
190
191     # Set the new attribute for our user:
192     my $attr_hidden = $builder->build( {
193         source => 'BorrowerAttribute',
194         value  => {
195             borrowernumber => $brwr->{'borrowernumber'},
196             code           => $attr_type->{'code'},
197             attribute      => '1337 hidden',
198         }
199     } );
200     my $attr_shown = $builder->build( {
201         source => 'BorrowerAttribute',
202         value  => {
203             borrowernumber => $brwr->{'borrowernumber'},
204             code           => $attr_type_visible->{'code'},
205             attribute      => '1337 shown',
206         }
207     } );
208
209     my $fine = $builder->build(
210         {
211             source => 'Accountline',
212             value  => {
213                 borrowernumber    => $brwr->{borrowernumber},
214                 debit_type_code   => 'OVERDUE',
215                 amountoutstanding => 10
216             }
217         }
218     );
219
220     # Prepare and send web request for IL-SDI server:
221     my $query = CGI->new;
222     $query->param( 'service', 'GetPatronInfo' );
223     $query->param( 'patron_id', $brwr->{'borrowernumber'} );
224     $query->param( 'show_attributes', '1' );
225     $query->param( 'show_fines', '1' );
226
227     my $reply = C4::ILSDI::Services::GetPatronInfo( $query );
228
229     # Build a structure for comparison:
230     my $cmp = {
231         borrowernumber    => $brwr->{borrowernumber},
232         value             => $attr_shown->{'attribute'},
233         value_description => $attr_shown->{'attribute'},
234         %$attr_type_visible,
235         %$attr_shown,
236     };
237
238     is( $reply->{'charges'}, '10.00',
239         'The \'charges\' attribute should be correctly filled (bug 17836)' );
240
241     is( scalar( @{$reply->{fines}->{fine}}), 1, 'There should be only 1 account line');
242     is(
243         $reply->{fines}->{fine}->[0]->{accountlines_id},
244         $fine->{accountlines_id},
245         "The accountline should be the correct one"
246     );
247
248     # Check results:
249     is_deeply( $reply->{'attributes'}, [ $cmp ], 'Test GetPatronInfo - show_attributes parameter' );
250
251     ok( exists $reply->{is_expired}, 'There should be the is_expired information');
252
253     # Cleanup
254     $schema->storage->txn_rollback;
255 };
256
257 subtest 'LookupPatron test' => sub {
258
259     plan tests => 9;
260
261     $schema->storage->txn_begin;
262
263     $schema->resultset( 'Issue' )->delete_all;
264     $schema->resultset( 'Borrower' )->delete_all;
265     $schema->resultset( 'BorrowerAttribute' )->delete_all;
266     $schema->resultset( 'BorrowerAttributeType' )->delete_all;
267     $schema->resultset( 'Category' )->delete_all;
268     $schema->resultset( 'Item' )->delete_all; # 'Branch' deps. on this
269     $schema->resultset( 'Branch' )->delete_all;
270
271     my $borrower = $builder->build({
272         source => 'Borrower',
273     });
274
275     my $query = CGI->new();
276     my $bad_result = C4::ILSDI::Services::LookupPatron($query);
277     is( $bad_result->{message}, 'PatronNotFound', 'No parameters' );
278
279     $query->delete_all();
280     $query->param( 'id', $borrower->{firstname} );
281     my $optional_result = C4::ILSDI::Services::LookupPatron($query);
282     is(
283         $optional_result->{id},
284         $borrower->{borrowernumber},
285         'Valid Firstname only'
286     );
287
288     $query->delete_all();
289     $query->param( 'id', 'ThereIsNoWayThatThisCouldPossiblyBeValid' );
290     my $bad_optional_result = C4::ILSDI::Services::LookupPatron($query);
291     is( $bad_optional_result->{message}, 'PatronNotFound', 'Invalid ID' );
292
293     foreach my $id_type (
294         'cardnumber',
295         'userid',
296         'email',
297         'borrowernumber',
298         'surname',
299         'firstname'
300     ) {
301         $query->delete_all();
302         $query->param( 'id_type', $id_type );
303         $query->param( 'id', $borrower->{$id_type} );
304         my $result = C4::ILSDI::Services::LookupPatron($query);
305         is( $result->{'id'}, $borrower->{borrowernumber}, "Checking $id_type" );
306     }
307
308     # Cleanup
309     $schema->storage->txn_rollback;
310 };
311
312 subtest 'Holds test' => sub {
313
314     plan tests => 9;
315
316     $schema->storage->txn_begin;
317
318     t::lib::Mocks::mock_preference( 'AllowHoldsOnDamagedItems', 0 );
319
320     my $patron = $builder->build({
321         source => 'Borrower',
322     });
323
324     my $item = $builder->build_sample_item(
325         {
326             damaged => 1
327         }
328     );
329
330     my $query = CGI->new;
331     $query->param( 'patron_id', $patron->{borrowernumber});
332     $query->param( 'bib_id', $item->biblionumber);
333
334     my $reply = C4::ILSDI::Services::HoldTitle( $query );
335     is( $reply->{code}, 'damaged', "Item damaged" );
336
337     $item->damaged(0)->store;
338
339     my $hold = $builder->build({
340         source => 'Reserve',
341         value => {
342             borrowernumber => $patron->{borrowernumber},
343             biblionumber => $item->biblionumber,
344             itemnumber => $item->itemnumber
345         }
346     });
347
348     $reply = C4::ILSDI::Services::HoldTitle( $query );
349     is( $reply->{code}, 'itemAlreadyOnHold', "Item already on hold" );
350
351     my $biblio_with_no_item = $builder->build_sample_biblio;
352
353     $query = CGI->new;
354     $query->param( 'patron_id', $patron->{borrowernumber});
355     $query->param( 'bib_id', $biblio_with_no_item->biblionumber);
356
357     $reply = C4::ILSDI::Services::HoldTitle( $query );
358     is( $reply->{code}, 'NoItems', 'Biblio has no item' );
359
360     my $item2 = $builder->build_sample_item(
361         {
362             damaged => 0,
363         }
364     );
365
366     t::lib::Mocks::mock_preference( 'ReservesControlBranch', 'PatronLibrary' );
367     Koha::CirculationRules->set_rule(
368         {
369             categorycode => $patron->{categorycode},
370             itemtype     => $item2->{itype},
371             branchcode   => $patron->{branchcode},
372             rule_name    => 'reservesallowed',
373             rule_value   => 1,
374         }
375     );
376
377     $query = CGI->new;
378     $query->param( 'patron_id', $patron->{borrowernumber});
379     $query->param( 'bib_id', $item2->biblionumber);
380     $query->param( 'item_id', $item2->itemnumber);
381
382     $reply = C4::ILSDI::Services::HoldItem( $query );
383     is( $reply->{code}, 'tooManyReserves', "Too many reserves" );
384
385     Koha::CirculationRules->set_rule(
386         {
387             categorycode => $patron->{categorycode},
388             itemtype     => $item2->{itype},
389             branchcode   => $patron->{branchcode},
390             rule_name    => 'reservesallowed',
391             rule_value   => 0,
392         }
393     );
394
395     $query = CGI->new;
396     $query->param( 'patron_id', $patron->{borrowernumber});
397     $query->param( 'bib_id', $item2->biblionumber);
398     $query->param( 'item_id', $item2->itemnumber);
399
400     $reply = C4::ILSDI::Services::HoldItem( $query );
401     is( $reply->{code}, 'noReservesAllowed', "No reserves allowed" );
402
403     my $origin_branch = $builder->build(
404         {
405             source => 'Branch',
406             value  => {
407                 pickup_location => 1,
408             }
409         }
410     );
411
412     # Adding a holdable item.
413     my $item3 = $builder->build_sample_item(
414        {
415            barcode => '123456789',
416            library => $origin_branch->{branchcode}
417        });
418
419     my $item4 = $builder->build_sample_item(
420         {
421            biblionumber => $item3->biblionumber,
422            damaged => 1,
423            library => $origin_branch->{branchcode}
424        });
425
426     Koha::CirculationRules->set_rule(
427         {
428             categorycode => $patron->{categorycode},
429             itemtype     => $item3->{itype},
430             branchcode   => $patron->{branchcode},
431             rule_name    => 'reservesallowed',
432             rule_value   => 10,
433         }
434     );
435
436     $query = CGI->new;
437     $query->param( 'patron_id', $patron->{borrowernumber});
438     $query->param( 'bib_id', $item4->biblionumber);
439     $query->param( 'item_id', $item4->itemnumber);
440
441     $reply = C4::ILSDI::Services::HoldItem( $query );
442     is( $reply->{code}, 'damaged', "Item is damaged" );
443
444     my $module = Test::MockModule->new('C4::Context');
445     $module->mock('userenv', sub { { patron => $patron } });
446     my $issue = C4::Circulation::AddIssue($patron, $item3->barcode);
447     t::lib::Mocks::mock_preference( 'AllowHoldsOnPatronsPossessions', '0' );
448
449     $query = CGI->new;
450     $query->param( 'patron_id', $patron->{borrowernumber});
451     $query->param( 'bib_id', $item3->biblionumber);
452     $query->param( 'item_id', $item3->itemnumber);
453     $query->param( 'pickup_location', $origin_branch->{branchcode});
454     $reply = C4::ILSDI::Services::HoldItem( $query );
455
456     is( $reply->{code}, 'alreadypossession', "Patron has issued same book" );
457     is( $reply->{pickup_location}, undef, "No reserve placed");
458
459     # Test Patron cannot reserve if expired and BlockExpiredPatronOpacActions
460     my $category = $builder->build({
461         source => 'Category',
462         value => { BlockExpiredPatronOpacActions => -1 }
463         });
464
465     my $branch_1 = $builder->build({ source => 'Branch' })->{ branchcode };
466
467     my $expired_borrowernumber = Koha::Patron->new({
468         firstname =>  'Expired',
469         surname => 'Patron',
470         categorycode => $category->{categorycode},
471         branchcode => $branch_1,
472         dateexpiry => '2000-01-01',
473     })->store->borrowernumber;
474
475     t::lib::Mocks::mock_preference('BlockExpiredPatronOpacActions', 1);
476
477     my $item5 = $builder->build({
478         source => 'Item',
479         value => {
480             biblionumber => $biblio_with_no_item->biblionumber,
481             damaged => 0,
482         }
483     });
484
485     $query = CGI->new;
486     $query->param( 'patron_id', $expired_borrowernumber);
487     $query->param( 'bib_id', $biblio_with_no_item->biblionumber);
488     $query->param( 'item_id', $item5->{itemnumber});
489
490     $reply = C4::ILSDI::Services::HoldItem( $query );
491     is( $reply->{code}, 'PatronExpired', "Patron is expired" );
492
493     $schema->storage->txn_rollback;
494 };
495
496 subtest 'Holds test for branch transfer limits' => sub {
497
498     plan tests => 6;
499
500     $schema->storage->txn_begin;
501
502     # Test enforement of branch transfer limits
503     t::lib::Mocks::mock_preference( 'UseBranchTransferLimits', '1' );
504     t::lib::Mocks::mock_preference( 'BranchTransferLimitsType', 'itemtype' );
505
506     my $patron = $builder->build({
507         source => 'Borrower',
508     });
509
510     my $origin_branch = $builder->build(
511         {
512             source => 'Branch',
513             value  => {
514                 pickup_location => 1,
515             }
516         }
517     );
518     my $pickup_branch = $builder->build(
519         {
520             source => 'Branch',
521             value  => {
522                 pickup_location => 1,
523             }
524         }
525     );
526
527     my $item = $builder->build_sample_item(
528         {
529             library => $origin_branch->{branchcode},
530         }
531     );
532
533     Koha::CirculationRules->set_rule(
534         {
535             categorycode => undef,
536             itemtype     => undef,
537             branchcode   => undef,
538             rule_name    => 'reservesallowed',
539             rule_value   => 99,
540         }
541     );
542
543     my $limit = Koha::Item::Transfer::Limit->new({
544         toBranch => $pickup_branch->{branchcode},
545         fromBranch => $item->holdingbranch,
546         itemtype => $item->effective_itemtype,
547     })->store();
548
549     my $query = CGI->new;
550     $query->param( 'pickup_location', $pickup_branch->{branchcode} );
551     $query->param( 'patron_id', $patron->{borrowernumber});
552     $query->param( 'bib_id', $item->biblionumber);
553     $query->param( 'item_id', $item->itemnumber);
554
555     my $reply = C4::ILSDI::Services::HoldItem( $query );
556     is( $reply->{code}, 'cannotBeTransferred', "Item hold, Item cannot be transferred" );
557
558     $reply = C4::ILSDI::Services::HoldTitle( $query );
559     is( $reply->{code}, 'cannotBeTransferred', "Record hold, Item cannot be transferred" );
560
561     t::lib::Mocks::mock_preference( 'UseBranchTransferLimits', '0' );
562
563     $reply = C4::ILSDI::Services::HoldItem( $query );
564     is( $reply->{code}, undef, "Item hold, Item can be transferred" );
565     my $hold = Koha::Holds->search({ itemnumber => $item->itemnumber, borrowernumber => $patron->{borrowernumber} })->next;
566     is( $hold->branchcode, $pickup_branch->{branchcode}, 'The library id is correctly set' );
567
568     Koha::Holds->search()->delete();
569
570     $reply = C4::ILSDI::Services::HoldTitle( $query );
571     is( $reply->{code}, undef, "Record hold, Item con be transferred" );
572     $hold = Koha::Holds->search({ biblionumber => $item->biblionumber, borrowernumber => $patron->{borrowernumber} })->next;
573     is( $hold->branchcode, $pickup_branch->{branchcode}, 'The library id is correctly set' );
574
575     $schema->storage->txn_rollback;
576 };
577
578 subtest 'Holds test with start_date and end_date' => sub {
579
580     plan tests => 8;
581
582     $schema->storage->txn_begin;
583
584     my $pickup_library = $builder->build_object(
585         {
586             class  => 'Koha::Libraries',
587             value  => {
588                 pickup_location => 1,
589             }
590         }
591     );
592
593     my $patron = $builder->build_object({
594         class => 'Koha::Patrons',
595     });
596
597     my $item = $builder->build_sample_item({ library => $pickup_library->branchcode });
598
599     Koha::CirculationRules->set_rule(
600         {
601             categorycode => undef,
602             itemtype     => undef,
603             branchcode   => undef,
604             rule_name    => 'reservesallowed',
605             rule_value   => 99,
606         }
607     );
608
609     my $query = CGI->new;
610     $query->param( 'pickup_location', $pickup_library->branchcode );
611     $query->param( 'patron_id', $patron->borrowernumber);
612     $query->param( 'bib_id', $item->biblionumber);
613     $query->param( 'item_id', $item->itemnumber);
614     $query->param( 'start_date', '2020-03-20');
615     $query->param( 'expiry_date', '2020-04-22');
616
617     my $reply = C4::ILSDI::Services::HoldItem( $query );
618     is ($reply->{pickup_location}, $pickup_library->branchname, "Item hold with date parameters was placed");
619     my $hold = Koha::Holds->search({ biblionumber => $item->biblionumber})->next();
620     is( $hold->biblionumber, $item->biblionumber, "correct biblionumber");
621     is( $hold->reservedate, '2020-03-20', "Item hold has correct start date" );
622     is( $hold->expirationdate, '2020-04-22', "Item hold has correct end date" );
623
624     $hold->delete();
625
626     $reply = C4::ILSDI::Services::HoldTitle( $query );
627     is ($reply->{pickup_location}, $pickup_library->branchname, "Record hold with date parameters was placed");
628     $hold = Koha::Holds->search({ biblionumber => $item->biblionumber})->next();
629     is( $hold->biblionumber, $item->biblionumber, "correct biblionumber");
630     is( $hold->reservedate, '2020-03-20', "Record hold has correct start date" );
631     is( $hold->expirationdate, '2020-04-22', "Record hold has correct end date" );
632
633     $schema->storage->txn_rollback;
634 };
635
636 subtest 'GetRecords' => sub {
637
638     plan tests => 8;
639
640     $schema->storage->txn_begin;
641
642     t::lib::Mocks::mock_preference( 'ILS-DI', 1 );
643
644     my $branch1 = $builder->build({
645         source => 'Branch',
646     });
647     my $branch2 = $builder->build({
648         source => 'Branch',
649     });
650
651     my $item = $builder->build_sample_item(
652         {
653             library => $branch1->{branchcode},
654         }
655     );
656
657     my $patron = $builder->build({
658         source => 'Borrower',
659     });
660
661     my $issue = $builder->build({
662         source => 'Issue',
663         value => {
664             itemnumber => $item->itemnumber,
665         }
666     });
667
668     my $hold = $builder->build({
669         source => 'Reserve',
670         value => {
671             biblionumber => $item->biblionumber,
672         }
673     });
674
675     ModItemTransfer($item->itemnumber, $branch1->{branchcode}, $branch2->{branchcode}, 'Manual');
676
677     my $cgi = CGI->new;
678     $cgi->param(service => 'GetRecords');
679     $cgi->param(id => $item->biblionumber);
680
681     my $reply = C4::ILSDI::Services::GetRecords($cgi);
682
683     my $transfer = $item->get_transfer;
684     my $expected = {
685         datesent => $transfer->datesent,
686         frombranch => $transfer->frombranch,
687         tobranch => $transfer->tobranch,
688     };
689     is_deeply($reply->{record}->[0]->{items}->{item}->[0]->{transfer}, $expected,
690         'GetRecords returns transfer informations');
691
692     # Check informations exposed
693     my $reply_issue = $reply->{record}->[0]->{issues}->{issue}->[0];
694     is($reply_issue->{itemnumber}, $item->itemnumber, 'GetRecords has an issue tag');
695     is($reply_issue->{borrowernumber}, undef, 'GetRecords does not expose borrowernumber in issue tag');
696     is($reply_issue->{surname}, undef, 'GetRecords does not expose surname in issue tag');
697     is($reply_issue->{firstname}, undef, 'GetRecords does not expose firstname in issue tag');
698     is($reply_issue->{cardnumber}, undef, 'GetRecords does not expose cardnumber in issue tag');
699     my $reply_reserve = $reply->{record}->[0]->{reserves}->{reserve}->[0];
700     is($reply_reserve->{biblionumber}, $item->biblionumber, 'GetRecords has a reserve tag');
701     is($reply_reserve->{borrowernumber}, undef, 'GetRecords does not expose borrowernumber in reserve tag');
702
703     $schema->storage->txn_rollback;
704 };
705
706 subtest 'RenewHold' => sub {
707     plan tests => 4;
708
709     $schema->storage->txn_begin;
710
711     my $cgi    = CGI->new;
712     my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
713     my $item   = $builder->build_sample_item;
714     $cgi->param( patron_id => $patron->borrowernumber );
715     $cgi->param( item_id   => $item->itemnumber );
716
717     t::lib::Mocks::mock_userenv( { patron => $patron } );    # For AddIssue
718     my $checkout = C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
719
720     # Everything is ok
721     my $reply = C4::ILSDI::Services::RenewLoan($cgi);
722     is( exists $reply->{date_due}, 1, 'If the item is checked out, the date_due key should exist' );
723
724     # The item is not checked out
725     $checkout->delete;
726     $reply = C4::ILSDI::Services::RenewLoan($cgi);
727     is( $reply, undef, 'If the item is not checked out, we should not explode.');    # FIXME We should return an error code instead
728
729     # The item does not exist
730     $item->delete;
731     $reply = C4::ILSDI::Services::RenewLoan($cgi);
732     is( $reply->{code}, 'RecordNotFound', 'If the item does not exist, RecordNotFound should be returned');
733
734     $patron->delete;
735     $reply = C4::ILSDI::Services::RenewLoan($cgi);
736     is( $reply->{code}, 'PatronNotFound', 'If the patron does not exist, PatronNotFound should be returned');
737
738     $schema->storage->txn_rollback;
739 };
740
741 subtest 'GetPatronInfo paginated loans' => sub {
742     plan tests => 7;
743
744     $schema->storage->txn_begin;
745
746     my $library = $builder->build_object({
747         class => 'Koha::Libraries',
748     });
749
750     my $item1 = $builder->build_sample_item({ library => $library->branchcode });
751     my $item2 = $builder->build_sample_item({ library => $library->branchcode });
752     my $item3 = $builder->build_sample_item({ library => $library->branchcode });
753     my $patron = $builder->build_object({
754         class => 'Koha::Patrons',
755         value => {
756             branchcode => $library->branchcode,
757         },
758     });
759     my $module = Test::MockModule->new('C4::Context');
760     $module->mock('userenv', sub { { branch => $library->branchcode } });
761     my $date_due = Koha::DateUtils::dt_from_string()->add(weeks => 2);
762     my $issue1 = C4::Circulation::AddIssue($patron->unblessed, $item1->barcode, $date_due);
763     my $date_due1 = Koha::DateUtils::dt_from_string( $issue1->date_due );
764     my $issue2 = C4::Circulation::AddIssue($patron->unblessed, $item2->barcode, $date_due);
765     my $date_due2 = Koha::DateUtils::dt_from_string( $issue2->date_due );
766     my $issue3 = C4::Circulation::AddIssue($patron->unblessed, $item3->barcode, $date_due);
767     my $date_due3 = Koha::DateUtils::dt_from_string( $issue3->date_due );
768
769     my $cgi = CGI->new;
770
771     $cgi->param( 'service', 'GetPatronInfo' );
772     $cgi->param( 'patron_id', $patron->borrowernumber );
773     $cgi->param( 'show_loans', '1' );
774     $cgi->param( 'loans_per_page', '2' );
775     $cgi->param( 'loans_page', '1' );
776     my $reply = C4::ILSDI::Services::GetPatronInfo($cgi);
777
778     is($reply->{total_loans}, 3, 'total_loans == 3');
779     is(scalar @{ $reply->{loans}->{loan} }, 2, 'GetPatronInfo returned only 2 loans');
780     is($reply->{loans}->{loan}->[0]->{itemnumber}, $item3->itemnumber);
781     is($reply->{loans}->{loan}->[1]->{itemnumber}, $item2->itemnumber);
782
783     $cgi->param( 'loans_page', '2' );
784     $reply = C4::ILSDI::Services::GetPatronInfo($cgi);
785
786     is($reply->{total_loans}, 3, 'total_loans == 3');
787     is(scalar @{ $reply->{loans}->{loan} }, 1, 'GetPatronInfo returned only 1 loan');
788     is($reply->{loans}->{loan}->[0]->{itemnumber}, $item1->itemnumber);
789
790     $schema->storage->txn_rollback;
791 };
792
793 subtest 'GetAvailability itemcallnumber' => sub {
794
795     plan tests => 4;
796
797     $schema->storage->txn_begin;
798
799     t::lib::Mocks::mock_preference( 'ILS-DI', 1 );
800
801     my $item1 = $builder->build_sample_item(
802         {
803             itemcallnumber => "callnumber",
804         }
805     );
806
807     my $item2 = $builder->build_sample_item( {} );
808
809     # Build the query
810     my $cgi = CGI->new;
811     $cgi->param( service => 'GetAvailability' );
812     $cgi->param( id      => $item1->itemnumber );
813     $cgi->param( id_type => 'item' );
814
815     # Output of GetAvailability is a string containing XML
816     my $reply = C4::ILSDI::Services::GetAvailability($cgi);
817
818     # Parse the output and get info
819     my $result_XML = XML::LibXML->load_xml( string => $reply );
820     my $reply_callnumber =
821       $result_XML->findnodes('//dlf:itemcallnumber')->to_literal();
822
823     # Test the output
824     is( $reply_callnumber, $item1->itemcallnumber,
825         "GetAvailability item has an itemcallnumber tag" );
826
827     $cgi = CGI->new;
828     $cgi->param( service => 'GetAvailability' );
829     $cgi->param( id      => $item2->itemnumber );
830     $cgi->param( id_type => 'item' );
831     $reply      = C4::ILSDI::Services::GetAvailability($cgi);
832     $result_XML = XML::LibXML->load_xml( string => $reply );
833     $reply_callnumber =
834       $result_XML->findnodes('//dlf:itemcallnumber')->to_literal();
835     is( $reply_callnumber, '',
836         "As expected, GetAvailability item has no itemcallnumber tag" );
837
838     $cgi = CGI->new;
839     $cgi->param( service => 'GetAvailability' );
840     $cgi->param( id      => $item1->biblionumber );
841     $cgi->param( id_type => 'biblio' );
842     $reply      = C4::ILSDI::Services::GetAvailability($cgi);
843     $result_XML = XML::LibXML->load_xml( string => $reply );
844     $reply_callnumber =
845       $result_XML->findnodes('//dlf:itemcallnumber')->to_literal();
846     is( $reply_callnumber, $item1->itemcallnumber,
847         "GetAvailability biblio has an itemcallnumber tag" );
848
849     $cgi = CGI->new;
850     $cgi->param( service => 'GetAvailability' );
851     $cgi->param( id      => $item2->biblionumber );
852     $cgi->param( id_type => 'biblio' );
853     $reply      = C4::ILSDI::Services::GetAvailability($cgi);
854     $result_XML = XML::LibXML->load_xml( string => $reply );
855     $reply_callnumber =
856       $result_XML->findnodes('//dlf:itemcallnumber')->to_literal();
857     is( $reply_callnumber, '',
858         "As expected, GetAvailability biblio has no itemcallnumber tag" );
859
860     $schema->storage->txn_rollback;
861 };